Showing posts with label enterprise firefox. Show all posts
Showing posts with label enterprise firefox. Show all posts

Wednesday, February 6, 2008

Customizing Firefox's Reporter for the Enterprise

Objective
This post will provide a step-through for customizing Mozilla Firefox's Reporter Extension. Goal is to rewire publishing functionality so that submissions via Help->Report Broken Web Site... can be directed to the Enterprise's internal repository rather than Mozilla's. Caveat: modifications have been tested and are in production for deployments of Firefox 2.

Motivation
  • Early in the adoption process, not every site/webapp on the Intranet will be Firefox-friendly. A balanced view of "low hanging fruit" is required to prioritize remediation.
  • Reporting will help you identify applications causing the most "pain" to end-users. Every submission of a URL is essentially a Vote. Parlaying popularity with business criticality of the application itself will aid the remediation teams in scheduling work that takes into account, both, the needs of community members and your institution.
  • In short, creating transparency for what could be a tedious process along with active engagement of your community of users is
    • a crowd pleaser
    • will allow you to avoid lonesome hours of investigating and cataloging broken apps
    • will enable you to leverage your community for all phases of the remediation process, from discovery to testing
"Wall of Shame" Dashboard
Where should these results be published? The ideal data structure is a basic list. The important columns are already provided by "vanilla" Reporter:
  1. Web site URL
  2. Problem Type
  3. Describe Problem
  4. Email -> For the Enterprise, this Optional field is changed to Required, is locked and is auto-populated with the user's domain UID, basically whatever is used to log on to the work environment and is set to the Environment's %USERNAME% variable.
Whether you decide to build your own database with a frontend + REST/SOAP interface or simply leverage something like Sharepoint, where list-based reporting of this exact nature is the bread and butter of the platform, a few things are recommended:
  1. Make your data Public - allow anyone in the Firm to view the list of "broken" websites. Depending on your company's culture, you may even wish to induce a meme to the effect of "not supporting Firefox is shameful...". My preference is to title the Dashboard "Wall of Shame".
  2. Group entries by "Product" not by "URL". Since all you are getting from the report is a potentially long URL, without getting too fancy, you may wish to simply truncate it at the top directory level. At this point, grouping by "URL" is good enough.
  3. If you are piping the data into something that also facilitates Status Tracking data columns, you should expose the status of remediation efforts for that Product to the public.
High level overview of How Reporter Works
  1. User fills out a form, some fields are automatically picked up, i.e. URL, firefox build etc
  2. User submits form. Under the hood, a POST request is sent over HTTP, via XMLHttpRequest object, to some designated Web Service.
  3. The Web Service enters form data into it's database and throws back a response.
  4. Reporter renders success/failure page.
Anatomy of the Reporter Extension
In source code, Reporter lives in root/extensions/reporter. Layout of source code is shown to the right. For clarity, only the tweaked source files are shown.

After compilation and packaging, the resources folder becomes bin/chrome/reporter.jar. The locales section is merged into bin/chrome/en-US.jar/locale/en-US/reporter.

Privacy Notice -> Splash Page
Let's start by customizing the Privacy Notice dialog -- the very first thing our end-users will see when reporting a broken web site. Caveat: to disable the dialog that asks the user to acknowledge Mozilla's Privacy Policy, set extensions.reporter.hidePrivacyStatement in your global/default settings. I default this setting to true via Mission Control but using GPOs or CCK just to get that pref into the profile will work as well.
  • resources/skin/classic/reporter/firefoxlogo.gif -
    Any changes to the file name have to be reflected in reportWziard.xul (below). I replace the logo with the logo of my team, to visually indicate to the user that they are, in fact, interacting with my internal system. My image is a gif, sized 350x133 pixels.


  • resources/content/reporter/reportWizard.xul - any changes to logo or layout of splash page happen here
    This is what I have:
    <!-- Privacy Notice -->
    <wizardpage id="privacyNotice"
    onpageshow="initPrivacyNotice()"
    label = "&privacyNotice.label;">
    <!--description>&reportWizardPrivacy.description;</description-->
    <vbox id="privacyFrame" flex="1" style="padding:10px">
    <hbox>
    <html:img width="350px" height="133px"
    src="chrome://reporter/skin/firefoxlogo.gif" />
    </hbox>
    <hbox height="100px"></hbox>
    <hbox style="padding:4px;">
    <description align="end" flex="1"
    style="text-align:right;">&reportWizardPrivacy.description;</description>
    </hbox>
    </vbox>
    </wizardpage>

  • locales/en_US/chrome/reportWizard.dtd - just changing text here...
    <!ENTITY privacyNotice.label ""My" Firefox Reporter Agent">
    <!ENTITY reportWizardPrivacy.description "This tool allows you to tell the MY.Team about web sites that do not work properly in &brandShortName;, or shut &brandShortName; out. This is your way to help us ensure the best possible experience for &brandShortName; users.">
Report Form
  • resources/content/reporter/reportWizard.xul - only change is in disabling the textbox (since it will auto-populate)
     <row align="center">
    <label control="email" value="&reportForm.email.title;" accesskey="&reportForm.email.accesskey;"/>
    <textbox id="email" size="60" class="noborder" disabled="true"/>
    </row>
  • locales/en_US/chrome/reportWizard.dtd - only cosmetic changes here as well...
    <!ENTITY reportForm.email.title  "Username (Required):">
    <!ENTITY reportForm.email.accesskey "U">

  • resources/content/reporter/reportWizard.js - the username is pulled from the environment in this snippet. getUsername() is the function of interest.
    function initForm() {
    var strbundle=document.getElementById("strings");
    var reportWizard = document.getElementById('reportWizard');

    reportWizard.canRewind = true;
    document.getElementById('url').value = gURL;
    document.getElementById('email').value = getUsername();

    // Change next button to "submit report"
    reportWizard.getButton('next').label = strbundle.getString("submitReport") + " >";

    // We don't let the user go forward until they fufill certain requirements - see validateform()
    reportWizard.canAdvance = false;
    }

    function getUsername() {
    var env = Components.classes["@mozilla.org/process/environment;1"]
    .getService(Components.interfaces.nsIEnvironment);

    var username = env.get('USERNAME');
    return username;
    }
Send Data
All interesting pieces happen under the hood. In my environment, a SOAP envelope is constructed from the input form and is sent via XMLHttpRequest to a Sharepoint 2003 list. Obviously the implementation will have to be tweaked to work in your specific environment. However, the process of gathering and sending data over REST via XMLHttpRequest object should not change. Please explore the full listing of reportWizard.js available below.

Note-worthy areas are:
  • const declarations - POST parameters: url, operationName, listName, soapAction, myXMLNS, actionURI
  • prepareRequest() - converts form data into a qualified SOAP envelope for Sharepoint. See Example Envelop comment in the function.
  • callReporter() - executes the AJAX POST against the server and depending on response prepares the Results pane of the wizard
  • sendReport() - this is the master function, it pulls visible and hidden data from the form, stuffs it into an Array, calls prepareRequest() to convert the array into the SOAP envelope, and finally, calls callReporter() to fire off the data to my Sharepoint server.
The Results Page
At this point, our submission either succeeded or not. A report.xhtml or an error.xhtml page are rendered into an iframe on the final screen. These pages are dynamically populated by JavaScript.
  • resources/content/reporter/reportWizard.xul

    <!-- Finish -->
    <wizardpage id="finish"
    label="&finish.label;">
    <textbox id="finishSummary" size="60" readonly="true"/>
    <hbox>
    <checkbox id="showDetail" label="&reportResults.showDetail.title;" accesskey="&reportResults.showDetail.accesskey;" oncommand="showDetail()"/>
    </hbox>
    <vbox id="finishExtendedFrame" flex="1">
    <iframe id="finishExtendedSuccess" type="content" src="report.xhtml" flex="1"/>
    <iframe id="finishExtendedFailed" type="content" src="error.xhtml" flex="1"/>
    </vbox>
    </wizardpage>

    </wizard>

  • resources/content/reporter/report.xhtml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
    <!ENTITY % reporterDTD SYSTEM "chrome://reporter/locale/reportResults.dtd" >
    %reporterDTD;
    ]>
    <!-- ***** BEGIN LICENSE BLOCK *****
    - Version: MPL 1.1/GPL 2.0/LGPL 2.1
    -
    - The contents of this file are subject to the Mozilla Public License Version
    - 1.1 (the "License"); you may not use this file except in compliance with
    - the License. You may obtain a copy of the License at
    - http://www.mozilla.org/MPL/
    -
    - Software distributed under the License is distributed on an "AS IS" basis,
    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    - for the specific language governing rights and limitations under the
    - License.
    -
    - The Original Code is Mozilla Reporter (r.m.o).
    -
    - The Initial Developer of the Original Code is
    - Robert Accettura <robert@accettura.com>.
    -
    - Portions created by the Initial Developer are Copyright (C) 2004
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
    -
    - Alternatively, the contents of this file may be used under the terms of
    - either the GNU General Public License Version 2 or later (the "GPL"), or
    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    - in which case the provisions of the GPL or the LGPL are applicable instead
    - of those above. If you wish to allow use of your version of this file only
    - under the terms of either the GPL or the LGPL, and not to allow others to
    - use your version of this file under the terms of the MPL, indicate your
    - decision by deleting the provisions above and replace them with the notice
    - and other provisions required by the LGPL or the GPL. If you do not delete
    - the provisions above, a recipient may use your version of this file under
    - the terms of any one of the MPL, the GPL or the LGPL.
    -
    - ***** END LICENSE BLOCK ***** -->
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>reporter</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="chrome://reporter/skin/reportResults.css"/>
    </head>
    <body>
    <table>
    <tr>
    <th>&reportSite;:</th>
    <td><span id="urlStri"/></td>
    </tr>
    <tr>
    <th>&reportProblemType;:</th>
    <td><span id="problemTypeStri"/></td>
    </tr>
    <tr>
    <th>&reportDecsription;:</th>
    <td><span id="descriptionStri"/></td>
    </tr>
    <tr>
    <th>&reportPlatform;:</th>
    <td><span id="platformStri"/></td>
    </tr>
    <tr>
    <th>&reportProduct;:</th>
    <td><span id="productStri"/></td>
    </tr>
    <tr>
    <th>&reportoscpu;:</th>
    <td><span id="oscpuStri"/></td>
    </tr>
    <tr>
    <th>&reportGecko;:</th>
    <td><span id="geckoStri"/></td>
    </tr>
    <tr>
    <th>&reportBuildConfig;:</th>
    <td><span id="buildConfigStri"/></td>
    </tr>
    <tr>
    <th>&reportUseragent;:</th>
    <td><span id="userAgentStri"/></td>
    </tr>
    <tr>
    <th>&reportLanguage;:</th>
    <td><span id="langStri"/></td>
    </tr>
    <tr>
    <th>&reportEmail;:</th>
    <td><span id="emailStri"/></td>
    </tr>
    </table>
    </body>
    </html>
  • resources/content/reporter/error.xhtml
    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
    <!ENTITY % reporterDTD SYSTEM "chrome://reporter/locale/reportResults.dtd" >
    %reporterDTD;
    ]>
    <!-- ***** BEGIN LICENSE BLOCK *****
    - Version: MPL 1.1/GPL 2.0/LGPL 2.1
    -
    - The contents of this file are subject to the Mozilla Public License Version
    - 1.1 (the "License"); you may not use this file except in compliance with
    - the License. You may obtain a copy of the License at
    - http://www.mozilla.org/MPL/
    -
    - Software distributed under the License is distributed on an "AS IS" basis,
    - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
    - for the specific language governing rights and limitations under the
    - License.
    -
    - The Original Code is Mozilla Reporter (r.m.o).
    -
    - The Initial Developer of the Original Code is
    - Robert Accettura <robert@accettura.com>.
    -
    - Portions created by the Initial Developer are Copyright (C) 2004
    - the Initial Developer. All Rights Reserved.
    -
    - Contributor(s):
    -
    - Alternatively, the contents of this file may be used under the terms of
    - either the GNU General Public License Version 2 or later (the "GPL"), or
    - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
    - in which case the provisions of the GPL or the LGPL are applicable instead
    - of those above. If you wish to allow use of your version of this file only
    - under the terms of either the GPL or the LGPL, and not to allow others to
    - use your version of this file under the terms of the MPL, indicate your
    - decision by deleting the provisions above and replace them with the notice
    - and other provisions required by the LGPL or the GPL. If you do not delete
    - the provisions above, a recipient may use your version of this file under
    - the terms of any one of the MPL, the GPL or the LGPL.
    -
    - ***** END LICENSE BLOCK ***** -->

    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <title>reporter</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <link rel="stylesheet" type="text/css" href="chrome://reporter/skin/reportResults.css"/>
    </head>
    <body>
    <h3>&error;</h3>
    <div id="messagediv"><b>Error Code: </b><span id="faultCode"/><br/><b>Error Message: </b><span id="faultMessage"/></div>
    </body>
    </html>

Full Listing of reportWizard.js

/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Reporter (r.m.o).
*
* The Initial Developer of the Original Code is
* Robert Accettura .
*
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Boris Zbarsky
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */

/********************************************************
* *** Warning ****
* DO _NOT_ MODIFY THIS FILE without first contacting
* Robert Accettura
* or a reporter.mozilla.org Admin!
*******************************************************/

const gURL = window.arguments[0];
const gLanguage = window.navigator.language;
const gRMOvers = "0.2"; // Do not touch without contacting reporter admin!

// Globals
var gReportID;
var gSysID;
var gFaultCode;
var gFaultMessage;
var gSOAPerror = false;
var gPrefBranch;
const gAsync = false;
var gButton;
var reportWizard;

var myRequest;
var xmlEnvelope;
var consoleService;
/* SOAP Services for Sharepoint 2003 */
const url = 'http://myshareporint/sites/MyMozillaSite/_vti_bin/Lists.asmx';
const operationName = 'UpdateListItems';
const listName = 'MyReporterList';
const soapAction = 'SOAPAction';
const myXMLNS = 'http://schemas.microsoft.com/sharepoint/soap/';
const actionURI = myXMLNS+operationName;


function getReporterPrefBranch() {
if (!gPrefBranch) {
gPrefBranch = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefService)
.getBranch("extensions.reporter.");
}
return gPrefBranch;
}


function getBoolPref(prefname, aDefault) {
try {
var prefs = getReporterPrefBranch();
return prefs.getBoolPref(prefname);
} catch(ex) {
return aDefault;
}
}


function getCharPref(prefname, aDefault) {
try {
var prefs = getReporterPrefBranch();
return prefs.getCharPref(prefname);
} catch(ex) {
return aDefault;
}
}


function initPrivacyNotice() {
var strbundle=document.getElementById("strings");
var reportWizard = document.getElementById('reportWizard');

// Change next button to "submit report"
reportWizard.getButton('next').label = "Next >";

reportWizard.canRewind = false;
reportWizard.canAdvance = true;
}

function setPrivacyPref(){
if (document.getElementById('dontShowPrivacyStatement').checked){
var prefs = getReporterPrefBranch();
prefs.setBoolPref("hidePrivacyStatement", true);
}
}

function initForm() {
var strbundle=document.getElementById("strings");
var reportWizard = document.getElementById('reportWizard');

reportWizard.canRewind = true;
document.getElementById('url').value = gURL;
document.getElementById('email').value = getUsername();

// Change next button to "submit report"
reportWizard.getButton('next').label = strbundle.getString("submitReport") + " >";

// We don't let the user go forward until they fufill certain requirements - see validateform()
reportWizard.canAdvance = false;
}

function getUsername() {
var env = Components.classes["@mozilla.org/process/environment;1"]
.getService(Components.interfaces.nsIEnvironment);

var username = env.get('USERNAME');
return username;
}

function validateForm() {
var canAdvance = document.getElementById('problem_type').value != "0";
document.getElementById('reportWizard').canAdvance = canAdvance;
}


function registerSysID(){
var param = new Array();;
param[0] = new SOAPParameter(gLanguage, "language");

// get sysID
callReporter("register", param, setValSysID);

// saving
if (gSysID != undefined){
var prefs = getReporterPrefBranch();
prefs.setCharPref("sysid", gSysID);
return gSysID;
}
return "";
}


function getSysID() {
var sysId = getCharPref("sysid", "");
if (sysId == "")
//sysId = registerSysID();

return sysId;
}

function sendReport() {
// we control the user path from here.
var reportWizard = document.getElementById('reportWizard');

reportWizard.canRewind = false;
reportWizard.canAdvance = false;
// why would we need a cancel button?
reportWizard.getButton("cancel").disabled = true;

var strbundle=document.getElementById("strings");
var statusDescription = document.getElementById('sendReportProgressDescription');
var statusIndicator = document.getElementById('sendReportProgressIndicator');

// Data from form we need
var myData = new Array();
myData['rmoVers'] = gRMOvers;
myData['url'] = gURL;
myData['problem_type'] = document.getElementById('problem_type').value;
myData['description'] = document.getElementById('description').value;
myData['behind_login'] = (document.getElementById('behind_login').checked ? 'Yes' : 'No');
myData['platform'] = navigator.platform;
myData['oscpu'] = navigator.oscpu;

myData['gecko'] = getGecko();
myData['product'] = getProduct();
myData['useragent'] = navigator.userAgent;
myData['buildconfig'] = getBuildConfig();
myData['language'] = gLanguage;
myData['email'] = document.getElementById('email').value;
myData['sysid'] = getSysID();

//build XMLHttpRequest
consoleService = Components.classes['@mozilla.org/consoleservice;1'].getService(Components.interfaces.nsIConsoleService);

doLog("init(): creating xmlEnvelope");
xmlEnvelope = prepareRequest(operationName,listName,myData);

var s = new XMLSerializer();
//var strMessage = s.serializeToString(xmlEnvelope);
doLog("doc: "+s.serializeToString(xmlEnvelope));

statusIndicator.setAttribute("value", "5%");
statusDescription.setAttribute("value", strbundle.getString("sendingReport"));

//CALL REPORTER
callReporter(operationName, xmlEnvelope); //setValReportID);

var finishSummary = document.getElementById('finishSummary');
var finishExtendedFailed = document.getElementById('finishExtendedFailed');
var finishExtendedSuccess = document.getElementById('finishExtendedSuccess');
if (!gSOAPerror) {
// If successful
finishExtendedFailed.setAttribute("class", "hide");

statusIndicator.setAttribute("value", "95%");
statusDescription.setAttribute("value", strbundle.getString("reportSent"));

reportWizard.canAdvance = true;
statusIndicator.setAttribute("value", "100%");

// Send to the finish page
reportWizard.advance();

// report ID returned from the web service
finishSummary.setAttribute("value", strbundle.getString("successfullyCreatedReport") + " " + gReportID);

finishExtendedDoc = finishExtendedSuccess.contentDocument;
finishExtendedDoc.getElementById('urlStri').textContent = myData['url'];
finishExtendedDoc.getElementById('problemTypeStri').textContent = myData['problem_type'];
finishExtendedDoc.getElementById('descriptionStri').textContent = myData['description'];
finishExtendedDoc.getElementById('platformStri').textContent = myData['platform'];
finishExtendedDoc.getElementById('oscpuStri').textContent = myData['oscpu'];
finishExtendedDoc.getElementById('productStri').textContent = myData['product'];
finishExtendedDoc.getElementById('geckoStri').textContent = myData['gecko'];
finishExtendedDoc.getElementById('buildConfigStri').textContent = myData['buildconfig'];
finishExtendedDoc.getElementById('userAgentStri').textContent = myData['useragent'];
finishExtendedDoc.getElementById('langStri').textContent = myData['language'];
finishExtendedDoc.getElementById('emailStri').textContent = myData['email'];

reportWizard.canRewind = false;
} else {
doLog('Failed to update list!');
// If there was an error from the server
finishExtendedSuccess.setAttribute("class", "hide");

// Change the label on the page so users know we have an error
var finishPage = document.getElementById('finish');
finishPage.setAttribute("label",strbundle.getString("finishError"));

reportWizard.canAdvance = true;
reportWizard.advance();

finishSummary.setAttribute("value",strbundle.getString("failedCreatingReport"));

finishExtendedDoc = finishExtendedFailed.contentDocument;
finishExtendedDoc.getElementById('faultCode').textContent = gFaultCode;
finishExtendedDoc.getElementById('faultMessage').textContent = gFaultMessage;
}
document.getElementById('finishExtendedFrame').collapsed = true;
reportWizard.canRewind = false;
reportWizard.getButton("cancel").disabled = true;
}


function showDetail() {
var hideDetail = document.getElementById('showDetail').checked ? false : true;
document.getElementById('finishExtendedFrame').collapsed = hideDetail;
}


function getBuildConfig() {
// bz and Biesi are my heroes for writing/debugging this chunk.
try {
netscape.security.PrivilegeManager
.enablePrivilege("UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite");
var ioservice =
Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);
var channel = ioservice.newChannel("chrome://global/content/buildconfig.html", null, null);
var stream = channel.open();
var scriptableInputStream =
Components.classes["@mozilla.org/scriptableinputstream;1"]
.createInstance(Components.interfaces.nsIScriptableInputStream);
scriptableInputStream.init(stream);
var data = "";
var curBit = scriptableInputStream.read(4096);
while (curBit.length) {
data += curBit;
curBit = scriptableInputStream.read(4096);
}
// Strip out the part, since it's not valid XML
data = data.replace(/^]*>/, "");
// Probably not strictly needed, but what the heck
data = data.replace(/^/, "");
var parser = new DOMParser();
var buildconfig = parser.parseFromString(data, "application/xhtml+xml");
var text = buildconfig.getElementsByTagName("body")[0].textContent;
var start= text.indexOf('Configure arguments')+19;
return text.substring(start);
} catch(ex) {
dump(ex);
return "Unknown";
}
}

// Execute an AJAX call
function callReporter(method, message) {
//var serviceURL = getCharPref("serviceURL", myServiceURL);

doLog("init(): sending request to "+url);
myRequest = new XMLHttpRequest();

myRequest.onreadystatechange=function() {
if (myRequest.readyState==4) {
if (myRequest.status==200) {
doLog("URL Exists!");
doLog(myRequest.getAllResponseHeaders());
doLog(myRequest.responseText);
alert("URL '"+url+"' exists");
alert(myRequest.responseText);
} else if(myRequest.status==404) {
doLog("URL doesn't exist!");

} else if(myRequest.status==500) {
doLog("Server Failed "+myRequest.status);
doLog("theResponse: "+myRequest.responseText);
gSOAPerror = true;
gFaultCode = myRequest.status;
gFaultMessage = myRequest.responseText;
} else {
doLog("unknown error!");
gSOAPerror = true;
doLog("Server Failed "+myRequest.status);
doLog("theResponse: "+myRequest.responseText);
}
}
};

doLog("xmlEvenlope: Serialized -- "+message);

try{
myRequest.open("POST", url,gAsync);
myRequest.setRequestHeader(soapAction,actionURI);
myRequest.setRequestHeader('Content-Type','text/xml');
doLog("myRequest: "+myRequest);
myRequest.send(message);
doLog("myRequest: sent -- \n"+message);

if(!gAsync) {
if(myRequest.status != 200) {
gSOAPerror = true;
gFaultCode = myRequest.status;
gFaultMessage = myRequest.responseText;
}
}
} catch(e) {
doLog("exception: "+e);
}
}


function setValSysID(results) {
if (results) {
var params = results.getParameters(false,{});
for (var i = 0; i < gsysid =" params[i].value;" params =" results.getParameters(false,{});" i =" 0;" greportid =" params[i].value;" appinfo =" Components.classes[" appinfo =" Components.classes[" doc =" document.implementation.createDocument(myXMLNS," env =" document.createElement(" body =" document.createElement(" operation =" document.createElement(" listname =" document.createElement(" op ="=" op ="=" env="http://schemas.xmlsoap.org/soap/envelope/" xsi="http://www.w3.org/2001/XMLSchema-instance" xsd="http://www.w3.org/2001/XMLSchema">ReporterNewkrylovy

*/

// XML document
var Updates = document.createElement("a0:updates");
Operation.appendChild(Updates);

var Batch = document.createElement("Batch");
Batch.setAttribute("OnError","Return");
Batch.setAttribute("ListVersion","1");
Updates.appendChild(Batch);

var Method = document.createElement("Method");
Method.setAttribute("ID","1");
Method.setAttribute("Cmd","New");
Batch.appendChild(Method);

var Field;
var key;

//one-time operations
Field = createField('ID','New');
Method.appendChild(Field);

Field = createField('Title',data['url']);
Method.appendChild(Field);

for(key in data) {
doLog('key: '+key+' value: '+data[key]);
Field = createField(key,data[key]);
Method.appendChild(Field);
}
}
return Doc;
}

function createField(key,value) {
var field = document.createElement('Field');
field.setAttribute('Name',key);

var text = document.createTextNode(value);
field.appendChild(text);

return field;
}

function doLog(aMessage) {
consoleService.logStringMessage("MSReporter: "+aMessage);
}


Full Listing of reportWizard.xul
<?xml version="1.0"?>
<!-- ***** BEGIN LICENSE BLOCK *****
- Version: MPL 1.1/GPL 2.0/LGPL 2.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 (the "License"); you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is Mozilla Reporter (r.m.o).
-
- The Initial Developer of the Original Code is
- Robert Accettura <robert@accettura.com>.
-
- Portions created by the Initial Developer are Copyright (C) 2004
- the Initial Developer. All Rights Reserved.
-
- Contributor(s):
-
- Alternatively, the contents of this file may be used under the terms of
- either the GNU General Public License Version 2 or later (the "GPL"), or
- the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- in which case the provisions of the GPL or the LGPL are applicable instead
- of those above. If you wish to allow use of your version of this file only
- under the terms of either the GPL or the LGPL, and not to allow others to
- use your version of this file under the terms of the MPL, indicate your
- decision by deleting the provisions above and replace them with the notice
- and other provisions required by the LGPL or the GPL. If you do not delete
- the provisions above, a recipient may use your version of this file under
- the terms of any one of the MPL, the GPL or the LGPL.
-
- ***** END LICENSE BLOCK ***** -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://reporter/skin/reportWizard.css" type="text/css"?>

<!DOCTYPE wizard [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!ENTITY % reportWizardDTD SYSTEM "chrome://reporter/locale/reportWizard.dtd">
%reportWizardDTD;
]>

<wizard id="reportWizard" title="&reportWizard.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml">
<script type="application/x-javascript" src="chrome://reporter/content/reportWizard.js"/>
<stringbundle id="strings" src="chrome://reporter/locale/reportWizard.properties"/>

<!-- Privacy Notice -->
<wizardpage id="privacyNotice"
onpageshow="initPrivacyNotice()"
label = "&privacyNotice.label;">
<!--description>&reportWizardPrivacy.description;</description-->
<vbox id="privacyFrame" flex="1" style="padding:10px">
<hbox>
<html:img width="350px" height="133px" src="chrome://reporter/skin/firefoxlogo.gif" />
</hbox>
<hbox height="100px"></hbox>
<hbox style="padding:4px;">
<description align="end" flex="1" style="text-align:right;">&reportWizardPrivacy.description;</description>
</hbox>
</vbox>
</wizardpage>

<!-- Report Form -->
<wizardpage id="reportForm"
onpageshow="initForm()"
label="&reportForm.label;">
<description>&reportForm.description;</description>
<separator />
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row align="center">
<label control="url" value="&reportForm.url.title;"/>
<textbox id="url" size="60" readonly="true" class="noborder"/>
</row>
<row align="center">
<spacer/>
<checkbox id="behind_login" label="&reportForm.behind_login.title;" accesskey="&reportForm.behind_login.accesskey;"/>
</row>
<row align="center">
<label control="problem_type" value="&reportForm.problem_type.title;" accesskey="&reportForm.problem_type.accesskey;"/>
<!-- XXX: Perhaps this should eventually/maybe come from somewhere else? Eh, not sure so lets just hardcode this for now. -->
<menulist label="problem_type" id="problem_type" oncommand="validateForm()">
<menupopup>
<!-- ************* WARNING *************** -->
<!-- DO *NOT* Add/change/modify without consulting with r.m.o server admin first! -->
<!-- ************ /WARNING *************** -->
<menuitem label="&reportForm.problem_type.chooseOne.title;" value="0"/>
<menuitem label="&reportForm.problem_type.item1.title;" value="1"/>
<menuitem label="&reportForm.problem_type.item2.title;" value="2"/>
<menuitem label="&reportForm.problem_type.item3.title;" value="3"/>
<menuitem label="&reportForm.problem_type.item4.title;" value="4"/>
<menuitem label="&reportForm.problem_type.item5.title;" value="5"/>
<menuitem label="&reportForm.problem_type.item6.title;" value="6"/>
<menuitem label="&reportForm.problem_type.item7.title;" value="7"/>
</menupopup>
</menulist>
</row>
<row>
<label control="description" value="&reportForm.describe.title;" accesskey="&reportForm.describe.accesskey;"/>
<textbox id="description" value="" cols="40" rows="5" multiline="true" size="40" class="noborder"/>
</row>
<row align="center">
<label control="email" value="&reportForm.email.title;" accesskey="&reportForm.email.accesskey;"/>
<textbox id="email" size="60" class="noborder" disabled="true"/>
</row>
<!--row align="center">
<spacer/>
<hbox>
<label id="privacyPolicy" class="text-link"
value="&reportForm.privacyPolicy.title;"
tooltiptext="&reportForm.privacyPolicy.tooltip;"/>
</hbox>
</row-->
</rows>
</grid>
</wizardpage>

<!-- Send Data -->
<wizardpage id="sendReport"
onpageshow="sendReport()"
label="&sendReport.label;">
<description>&sendReport.description;</description>
<separator />
<description id="sendReportProgressDescription"/>
<progressmeter id="sendReportProgressIndicator" mode="undetermined" value="0%"/>
</wizardpage>

<!-- Finish -->
<wizardpage id="finish"
label="&finish.label;">
<textbox id="finishSummary" size="60" readonly="true"/>
<hbox>
<checkbox id="showDetail" label="&reportResults.showDetail.title;" accesskey="&reportResults.showDetail.accesskey;" oncommand="showDetail()"/>
</hbox>
<vbox id="finishExtendedFrame" flex="1">
<iframe id="finishExtendedSuccess" type="content" src="report.xhtml" flex="1"/>
<iframe id="finishExtendedFailed" type="content" src="error.xhtml" flex="1"/>
</vbox>
</wizardpage>

</wizard>

Testing and Packaging
Backup the .jar's with Reporter's components. For quick/dirty tests, simply open up each of the aforementioned jars with something like WinRAR and simply overwrite/add the pristine files with your version. Changes should take effect after restart of the browser.

Optionally, and I have not done this, you can create the manifests and treat reporter as just another extension under development. This is probably the best way to go if changes to the extension are significant.

My team compiles our own version of Firefox for internal distribution. Changes to the reporter extension are part of our "pre-build" process. Luckily, this extension has not changed over the lifetime of Firefox 2.

Other options are simply to use CCK or a wrapper to drop in your replacement jars prior to roll-out.

Spread the word
Users need to know that the "Report a Broken Website..." has been customized and is available for use. Within a few weeks we accumulated over 300 entries with around 10 unique sites making our "To be Remediated List". Yeah, I wussed out on the "Wall of Shame" title =)

Wrap up

Hopefully, the walk-through illustrated some strategies in customizing the Reporter extension. Although my example publishes to Sharepoint in SOAP format, the mechanisms for submitting to any Web Service are standard and should be fairly straight-forward to customize for your own needs. The resulting Dashboard/Reporting aspect is community-friendly and provides useful statistics to guide the prioritization of remediation work.

Errata will certainly follow in the form of comments and I of course welcome corrections, opinions, feedback and your own stories from the frontier. Cheers.

Wednesday, September 19, 2007

Firefox EWG - Meeting #3

Summary
Call on Sept 19th was about useful Extensions for the Enterprise. A larger portion of the meeting, however focused on the apparent dwindling interest in Enterprise Firefox within the larger community.

Is the Enterprise simply not ready to bring Firefox in-house? Or, are the majority of institutional adopters simply happy with a consumer product floating about?

Perhaps one of the problems is that we still talk about Firefox as a Web Browser as opposed to a "Productivity Platform" for the Desktop.

Firefox is a Productivity Platform
All the features that make Firefox a consumer favorite deal with productivity. Tabs, keywords, search bars, extensions, dictionaries, etc help us be effective and productive in our work and home lives. When consumer says "better usability", enterprise says "better productivity".

Mike Kaply spoke about adoption at IBM. Developers are finding ways to enhance the end-user experience by writing custom extensions for Firefox. Whether the extensions glue several applications together or simply automate the tedious process of filling out web-forms, end result is a boost to productivity.

If Firefox is the gateway or glue between what's on the Desktop and what's on the network, potential for productivity-boosting application is something to ponder.

Raising awareness
Mike and I are going to take lead on a few initiatives to help raise awareness on the Enterprise Firefox front:
  • 10 Steps to adopting Firefox in the Enterprise -
    Now that we've begun to pool experiences, we should be able to bake out a definitive guide of sorts for things to consider when bringing Firefox into an enterprise environment
  • Good Ol' Conference -
    We will begin looking at opportunities to present at some Conferences
  • Blogging - check...
  • Code Day and Training -
    The learning curve for any kind of Mozilla-related development is rather high. Perhaps training targeting institutions and enterprises (like an Enterprise track) will help developers scale this wall.
  • Enterprise Firefox Incubator -
    Talk has alway been cheap. As we address settings management, security, tools etc we will kick off projects within the Enterprise Working Group.
Enterprise Working Group Incubator
On my end, I'm going to kick off some projects for the Incubator. Specifically there has been interest in:
  • Mission Control - how to get up and running, what the back-end implementation can look like and benefits over GPO
  • Customized Reporter Extension - so that you can redirect Broken Website reports to a repository on your intranet
  • Managed Security Zones for Firefox - how to configure and lock capabilitiy.policy settings in Firefox, how to create a "trusted" zone and fully leverage Web2.0 technologies within your trusted intranet.
As always, if any of these topics or projects are of interest to you, please visit http://enterprisefirefox.org, participate in the calls or simply leave a comment on this here Blog.

Monday, August 20, 2007

Third Firefox Enterprise Working Group Meeting August 22nd

Third call is scheduled for Wednesday, August 22th at 10:00am Pacific, 1:00pm Eastern, 17:00 UTC. Details:

  • 650-903-0800 or 650-215-1282 x91 Conf# 280 (US/INTL)
  • 1-800-707-2533 (pin 369) Conf# 280 (US)
  • IRC - irc.mozilla.org - #ewg

The theme is “Extensions.” Please check out the EWG Wiki page for more information.

Wednesday, August 8, 2007

Firefox EWG - Meeting #2

Overview
This call focused on the Enterprise Wishlist. What are some common problems that need resolving and what features can we vote "up" as a Community?

Topics covered:
  • Addon Management
    • Do enterprises wish to control updates to addons or the application itself?
      The general sentiment seems to be "yes". At one extreme an enterprise may wish to allow the user's browser to pull updates directly from Mozilla. At the other extreme, an enterprise may wish to provide an intermediary point so that all internal browsers pull updates from this point. The intermediary infrastructure then has to have a way of pulling latest updates from Mozilla, either manually or automatically.

      So, first, we need to understand how to configure for each scenario. Second we need to understand how to set up our internal infrastructure to facilitate the second scenario.
  • Distribution
    • MSI packaging -This seems to be something everyone wants.
    • Ability to easily repackage the browser so we can add in our own "default" addons.
    • Distributors that create packages for others to deploy cannot use the Firefox brand as part of their package. However, enterprises that deploy for internal audience only can use the Firefox brand. Can we work through these issues to make distribution channels more accessible?
  • Settings Management
    • Group Policy - a non-trivial problem to solve. Need to work on proper GPO integration!
    • GPO vs Mission Control - pros and cons - will schedule a separate meeting to discuss these issues.
  • Security Zones
    • By design, Firefox does not allow cross-domain scripting. For an intranet, this can effectively break Web2.0 where developers want web technologies to federate. With IE, subdomains on a common domain are placed in a Trusted Security Zone. The relaxed security settings allow full-blow AJAX federation and interoperability. Similar capabilities exist in Firefox but they are no where near on par with IE.

      Firefox has a notion of a "security setting". There are even policy.* settings that make it possible to simulate Security Zones. This is great! However, Firefox treats policy.* and other security settings differently from rest of the settings. In fact - according to Mike Kaply - it is a completely different API. Mission Control cannot manage these settings which means we can't enable them across the Firm. This is a real challenge. How can we allow Mission Control or GPOs to enable these settings in a managed manner?
Personal Musings
My team compiles our own version of Firefox from the latest "stable" source. We make no changes to the code base. Why do we do this?
  1. Set our own version and timestamp info for the executable and the dlls. This helps us differentiate between "supported" Firefox and a "renegade" Firefox within the Firm. We actively discourage users from installing vanilla or "renegade" versions of anything. This is simply a security precaution for a Fortune 500. All software deployment goes through this pipeline. An unpatched Firefox within our environment that admits an exploit vector could be disastrous for business.
  2. We enable and compile in the "Autoconfig" mode that allows us to run Mission Control for settings management.
  3. We introduce some custom extensions and themes that we want to include in our package.
  4. We drop in config files for Mission Control
Points 3 and 4 don't really have much to do with compilation. Using Mike Kaply's technique for repackaging Firefox (see here) we can drop in our custom stuff without having to re-compile. Points 1 and 2, however really prevent us from taking advantage of a third-party MSIs. In fact all packages of "vanilla" Firefox are pretty much useless to us.

My point, we really do need an Enterprise Firefox package that's separate from the Consumer package. But what is it? What's the common denominator? One enterprise will want to manage settings with GPOs. Another one will want Mission Control. A third will want both. Is it possible to instrument a flexible package that can be configured? Maybe asking enterprises to compile their own version is not such a big deal when instructions on doing so are bullet proof. Much to think about...

Wednesday, July 25, 2007

Firefox EWG's first call is a success!

Thanks to everyone who attended the first Enterprise Working Group for Firefox call. We're still waiting for a statistic on how many folks were on the call and on the irc channel -- I'd ballpark the figure to be somewhere in the mid 20s.

Special thanks to Mike Kaply for MCing the call and to JT Batson and Basil Hashem from Mozilla for providing insight and effectively validating this initiative.

Very Brief Summary
The theme of this meeting was "Experience". Folks on the call shared their personal experience with Firefox in an enterprise environment. There were clear areas of interest and overlap.

My main take-away has been this: It cannot be understated nor unappreciated that "Enterprises" do, in fact, share pain-points and challenges and therefore must come together to form a real, effective Community.

Some identified synergies
By no means a completed list, but here is what jumped out at me:
  • Packaging.
    • Story around MSIs and MSTs vs some open source formats. Most Enterprises are heavy Windows users. At what point does the lack of these hinder adoption?
    • What is feasible? What can Mozilla own and what can and should the Enterprise Community own?
  • Patch Management.
    • If an organization is not very Agile and agility assumes that not everything is perfect out of the box, is Mozilla's patch cycle too agile for "enterprises"?
    • How are others patching Firefox on desktops without having to uninstall and install the entire product?
  • Settings Management.
    • What are some trade-offs between GPO and Mission Control?
    • Not all settings are manageable.
    • What settings should be exposed by all extensions to enable pref-level lockdown?
    • Can we drive best practices for deploying plugins and extensions? There is very little documentation on where Plugins should go and how to configure in the registry.
    • How do we prevent some critical settings from being "tattooed" into prefs.js? I.e. mcontrol and user agent settings should never be written to prefs.js when in Enterprise mode.
  • User Profiles
    • How can profiles/certificates be migrated with minimum impact to the end-users?
    • Does having Firefox and IE share bookmarks help adoption? Can Firefox "piggy-back" on Favorites? How can it be done easily?
Follow-ups
Attendees were asked to contribute content to the http://wiki.mozilla.org/Enterprise wiki. Content will be refactored as we overflow. Mike and I will put up a page for each Meeting where we can post and track agendas, follow-ups etc and will work to keep content organized.

Up Next (August 8th)
Next call is scheduled for August 8th, same time, same station. Topic will be "Enterprise Wish List" and should be a fun, rowdy call!

Come one, come all. Help us spread the word.

To be politely blunt, we're committed to helping this community thrive. There will be wins. To my "enterprise" peers, you cannot afford to not have your interests represented - the potential for the Enterprise Web Browser as the Knowledge Worker's Killer App is too real.

Friday, July 13, 2007

Enterprise Firefox Adoption Part 2: Where are my Bookmarks?

All these Bookmark syncing engines out there are great. Unfortunately, many large enterprises are simply at a point where policy restricts or greatly limits an employee's ability to expose information outside of their Firm. Bookmarks are no exception to the rule. Talk to a friend who works in Finance, you'll hear interesting stories directly from the battle field.

I don't have a solution for the above - this will take more scaffolding work. I do, however, want to raise the issue of synchronizing bookmarks between different browsers on a single Desktop -- for starters.

Some more realities of Firefox Adoption in the Enterprise:
  • Not every business critical application will work with Firefox on Day 1 if all you have deployed Firm-wide is the Not Firefox Browser.
  • Users will continue to use the Not Firefox Browser. Assume this is forever.
  • Mose users will have years of bookmarks stored in the Not Firefox Browser and this list will grow.
As you can see, simply "Importing" Not Firefox Browser's bookmarks into Firefox just doesn't solve the problem. When multiple browsers live side-by-side in an Enterprise, users will want bookmarks to always be in sync between All browsers on All Desktops that they may ever use.

This is a two-step project:
  • Step 1: how do I make Not Firefox and Firefox share bookmarks on a single Desktop?
  • Step 2: how do I roam these as I move around from Desktop to Desktop?
Challenges
Theoretically, it is not very difficult to write a Desktop application that will listen for changes to your Not Firefox and Firefox bookmarks, resolve the differences, update each repository, etc.

But did you know that with Firefox 2, there is no easy way to reload bookmarks from disk while Firefox is active? The Bookmark Service just doesn't expose that functionality. Furthermore, when you shut Firefox down, it will out-dump bookmarks stored in memory back into the bookmarks.html file, effectively trumping any changes you've made to the file with your Syncing Engine.

Of course, there are workarounds and it is possible to hack your way around everything BUT who has the time or the will to do that??

What would an Architect say?
Managing two or more discrete, proprietary repositories for Bookmarks on the Desktop is a huge pain for the End-User, the Support servicing these Users, and for IT trying to enable this synchronization.

Any architect would already be thinking: "Centralize & Federate - store in one spot, leverage the one spot, make things easy".

Plain Old Favorites indeed!
At what point does it make sense to replace Firefox's Bookmarking engine with something else? I don't want to replace the whole interface per se, just don't want bookmarks stored in bookmarks.html anymore.

In fact, I want to leverage the Favorites folder for storage on my Windows Desktop.

Perhaps the File System implementation for bookmarks is not as flexible as what Firefox 3 promises to deliver via SQL Lite for Places. Still, the Favorites solution exists today and it is the greatest common denominator for a bookmark datasource. That is, by design, every browser can understand the File System with ease. Not to mention, it is a lot easier to get Firefox to read files for bookmarks than it is for IE to learn to read bookmarks.html - for example.

This is why I think that Alex Sirota's PlainOldFavorites extension is brilliant as a vehicle for Firefox Adoption.

Immediately, I spot some some tiny performance issues and a feature gap between what Firefox's bookmarks engine offers and how much of it is implemented under the auspices of "Favorites". Still, this is much simpler than having to engineer synchronization for two distinct data sources.

This also makes Step 2 much more light-weight and simpler to implement.

Part 3 of this post will focus on my research on PlainOldFavorites as an Adoption vehicle. I hope Alex Sirota will help me out...

Enterprise Firefox Adoption Part1: IE Skin not Blasphemy

If IE is the dominant browser within your environment, consider Usability obstacles to adoption.

Firefox may be super user-friendly on its own but if your non-IT people predominantly use IE, are trained to use IE, have nothing but IE at home, and are too busy to care otherwise, Adoption will have to come through a gradual Transition.

Adoption through Transition
Give your users what they are already familiar with. Let them learn the benefits with minimal rides on the learning curve.

If IE is the incumbent browser and you want to deploy beyond IT pockets, consider packaging up Firefox with an IE-look-alike skin. "Looks Familiar" theme extension is a good place to start. I've customized it a bit in-house to retain Firefox-unique branding and saved a bundle on my car insurance.

Jokes aside, little things like this really ease the pains of adoption while keeping training costs low.

Thursday, July 12, 2007

First "Firefox Enterprise Working Group" Call Scheduled!

Please see Michael Kaply's Blog entry: "Firefox Enterprise Working Group Update". Rock'n'Roll!

Wednesday, July 11, 2007

"Firefox Is More Secure Than IE" Should be Treated as a Myth by the "Enterprise"

There are evolving factors that in practice make Firefox less of a target for malicious attacks - and these factors have very little to do with strength of code.

Some educated guesses on why Firefox is perceived to be more secure than IE:
  • Brand perception - Firefox is the perceived underdog, the champion of community and open standards, in league with the equally polished and spotless Google. Common perception of Microsoft in the techie world is not quite that. If an ambitious hacker only has time for a well executed attack on one browser, which do you think she'll choose to pursue? Those savvy enough to do damage play nice with Firefox.
  • Different code - Holes in software are a bi-product of the act of coding in of itself. There are very few and largely insignificant similarities (I'm assuming) between Firefox's and IE's code base. Those things that are attack vectors on one browser will most likely pose no threat to the other browser simply by virtue of dissimilarities in implementation. Spend enough time hacking at Firefox, it's possible to bring the vulnerability reports up to par with IE's.
  • Market share - IE dominates the market place and therefore the same Wisdom of Crowds that builds Firefox on one side of the spectrum plays a hand in breaking IE on the other side of the spectrum. Different communities, same principle.
In summary, when Firefox and IE are both on equal footing when it comes to market share, it will be very foolish of Enterprise Adopters to assume anything other than equal footing when it comes to vulnerabilities and exploits.

In fact, this mindset is a healthy one to have now as well.

Tuesday, July 10, 2007

Enterprise Firefox Can Live Happily With IE

Emergent Value in Firefox
Firefox is emerging as a:
  • Developer Platform
    • Extensions for the Web Developer like Firebug, LiveHTTP Headers, WebDev Toolbar etc are ideal for the Web2.0 developer of any organization.
    • The Browser itself can be enhanced through Addons. Integration with other desktop components and the potential there has barely been tapped.
  • Productivity Platform
    • Knowledge Workers spend significant amount of time interacting with web-based information. The niceties of Firefox tabs, bookmarks, integrated search, and all the other things we already recognize as award-winning implementations within the browser keep us productive. The next opportunity is in Extensions that focus on creating efficiencies and improving productivity for busy people in a busy work environment. These will create significant value for Enterprises. Large firms are willing to pay for such things too...
IE in the Enterprise
In my humble opinion, Internet Explorer is a more mature Enterprise-scale Browser because:
  • Microsoft invests in Enterprise features such as
    • GPO settings management
    • Centrally managed Active-X whitelisting capabilities to control what Addons are allowed to live in IE
  • Microsoft is familiar and accessible
    • The patch process is well understood by engineers and is consistent with other Microsoft products that are as incumbent in large firms
    • Support - you can open cases with Microsoft around enterprise issues and someone is paid to address your inquery
  • Microsoft reaches out to large firms and asks how they can improve their product
    • Efforts around IE have been revitalized and they've begun to really listen
    • They have the resources to not just listen
    • They dealt with enterprise customers regularly and hence they understand the requirements
    • They are also a very friendly and brilliant group of people
"So what?" you say. "Firefox is still a better browser!" you say. "Look at IE7!!". "Who cares about Enterprise anyway?".

It pays to be Enterprise-friendly
  • Large firms train people on how to use the tools made available to them in their workplace because the work force needs to be proficient and hence efficient, and productive. For non-techies, ease of use is directly related to familiarity. Product A may be 10x better than Product B but if I'm proficient with Product B I will install and use Product B at home.

    Enterprise Users are influenced by Enterprise Software when choosing how to be Home Users. The choice does not always exist when moving in the reverse direction. This is not because Corporate IT is evil but because most User-preferred software simply does not meet the criteria for firm-wide deployment and is hence too much of a risk for pocket deployments as well. See my post entitled "Why Vendors don't get Enterprise 2.0" for a more in-depth discussion on what it means to be Enterprise-friendly.
  • Developing with the Enterprise in mind forces you to make better Architecture and Design decisions. Simply put, you wind up with a more robust, more secure, and a more flexible, manageable product. Home Users can appreciate this too.
  • Enterprise is a dependable source of revenue. Open Source is no exception. There is vast opportunity in offering support, training, and consulting services to large firms who, more than ever, are in a buy-vs-build mode.
  • The "large Firm" world is a different world and will innovate around a product in very unpredictable and oftentimes very valuable ways.
Firefox in the Enterprise
Most Fortune 500 companies don't have much experience dealing with Open Source software vendors. Mozilla Corp is especially interesting because like Google, money to pay for a staff of 80-something people (correct me if I'm wrong) comes from online ad revenue referrals via the integrated search feature. Home Users, due to sheer volume, generate significant revenue from this source. Mozilla Corp is not in the business of selling software. On one hand, it can be argued that the evolution of the project itself is revolutionary and that it is what it is today because of it. On the other hand, it itches to ask if the story around adoption for Enterprise customers would have been a rosier one if Mozilla did make money from Licensing or Support fees. In short, there's a disconnect between revenue and product and hence a lack of accountability for the components that big firms demand and are used to paying for.

Free comes with a price. I would imagine that this makes Fortune 500s uneasy.

It is completely understandable that in the last few years Mozilla has been preparing for the Firefox 2 release in competition with IE7. This competition happens on a Home User's playground. For Mozilla Corp, user base is directly related to revenue. For Microsoft, Enterprise licensing fees make up a significant portion of revenue, plus they are the incumbent player - they can afford to add value in areas that Mozilla has not gotten around to yet.

These areas are:
  • Enterprise Settings Management -
    • How do we flip a setting on every Desktop in the event of a vulnerability?
    • How do we prevent users from altering settings that are deemed risky?
    • How can we enable and lock Security Zone-like implementation in Firefox?
  • Granular Addon Management -
    • How can we prevent a user from installing or uninstalling an extension without having to compile extensions into the browser itself?
    • How do we internalize extension repositories so we can "Bless" extensions before our users get to them?
    • How can we disable an extension or plugin on all desktops in the event of an emergency?
  • Patch management -
    • How do we not have to recompile and repackage Firefox every single time a critical vulnerability is released? (For large firms with a 3-6 month turnaround time for patches, aggressive patch releases by the vendor is not always a good thing!)
    • Can Firefox installations take advantage of self-repair (an MSI feature)?
    • Can we get MSI installers?
  • Maintenance, Support
    • We're on our own --

The truth is that those of use dealing with Enterprise deployment challenges are verbalizing the following: "How can we get Firefox's enterprise capabilities to be on par with those of IE?"

Sympathetic Souls
Firefox is the embodiment of Wikinomics-driven success. All this moaning and complaining is out of love, really.

The Enterprise Work Group around Firefox being shaped is a real thing. Community Incubation of Firefox is already a success story - now we just need to collectively learn how to involve the true Enterprise players.

Bring Enterprise to Firefox
One of the goals of the Enterprise Work Group is to create a resource, a support network, an Enterprise-level Community Incubator, really, for Firefox and the Enterprise-scale firms that recognize the aforementioned emergent benefits. It is also being established to apply unified pressure on Mozilla to rally around Enterprise needs. To be fair, there is no reason for Internet Explorer, Safari, and Opera to not be on the discussion table. "Enterprise Browsers Work Group" is really where I think we should end up but as with most things, progress comes from taking baby steps.

As was mentioned in my previous post (and Mike Kaply's post), we want this effort to be organic and inclusive and hence anonymous. We want to break down the walls that have historically prevented the firms that have the potential to be most influential in shaping core products from participating in community-driven projects. If you choose to participate, please help us respect the anonymity of participants -- at least until we all harmoniously come to a point where even this wall is no longer necessary.

Bring Firefox to Enterprise
The aforementioned benefits are very powerful motivators for decision makers in large firms to consider bringing Firefox into their environment. In fact, today most proposals will be driven by the Development Platform point. I personally don't like this selling point because I don't believe that it is solid enough to penetrate beyond IT pockets within an organization nor strong enough to solicit the required attention to the Enterprise features.

Bring Firefox in as a Contingency Browser
In software and infrastructure development and planning, the word "Enterprise" usually implies that availability, fail-over, and redundancy have been considered. Business-critical systems and processes demand it.

If the Web/Enterprise 2.0 revolution is taking place (and it is) and the most tangible evidence is in the proliferation - or migration from Desktop apps to - Rich Internet Applications, why are large Enterprises satisfied with a single browser?

For every IE installation on a Desktop there should be an installation of Firefox for those rare occasions when a critical vulnerability to an ActiveX control turns RSS-syndicated-to-millions-of-hackers-exploit with attack vectors sold on Ebay or traded on MySpace and you're forced to push out a policy that at best disables that ActiveX control bringng down a business process that your most prolific business unit depends on or at worst forces you to prevent IE from launching, period. Ask around, it happens.

I prefer to simply fail over to Firefox.

Thursday, July 5, 2007

Enterprise Working Group

To cut to the chase, I own engineering and deployment efforts around Firefox at a Fortune 500.

Disclaimer: I don't know everything there is to know about Firefox. However, I do know a little bit about what it means to deploy and maintain Firefox within a large enterprise where security and manageability take priority over usability every single time.

If you've read Mike Kaply's posts on Firefox in the Enterprise Part 1 and Part 2 then you already know that an Enterprise Working Group is taking shape. It's time to raise the bar for products that claim to be enterprise friendly! Why not start with Firefox?

In my musings on Enterprise 2.0 I often hammer on the fact that E2.0 is more than just Web2.0 for enterprise customers. Making Firefox Enterprise2.0-friendly is a whole other side of a shaping, Wikinomics-driven, revolution in how innovation and even incubation happens inclusive of large banks, software firms, and head-to-head competitors.

The Enterprise Work Group is being shaped by Mike, myself and other reps from our peer institutions to primarily tackle Firefox but (at least for me) also to tackle so much more...

We are our own Case Study and our own proof of ROI on how large institutions where technical innovation tends to be a well-guarded secret are adopting to and in turn helping shape what I refer to as Community Incubation.

We will be scheduling a call and will be posting rules of engagement so that anyone interested in Enterprise Firefox can participate without fear of violating Codes of Conduct or disclosing sensitive information. Please bear with us as at times it really feels like we're in Star Trek mode -- going where no-one has gone before...