/*jslint forin: true, rhino: true, onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false, strict: true */
// Released under the MIT/X11 license
// http://www.opensource.org/licenses/mit-license.php
"use strict";
var util = require("util");
var passUtils = require("passwords");
var apiUtils = require("api-utils");
var selfMod = require("self");
var clip = require("clipboard");
var fileMod = require("file");
var simpleStorage = require("simple-storage");
var preferences = require("preferences-service");
var selection = require("selection");
var prompts = require("prompts");
var tabs = require("tabs");
var Color = require("color").Color;
var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id=";
// Shared contstants
var TriagedDistro = 13;
exports.TriagedDistro = TriagedDistro;
var NumberOfFrames = 7;
exports.NumberOfFrames = NumberOfFrames;
var BTSPrefNS = "bugzilla-triage.setting.";
exports.BTSPrefNS = BTSPrefNS;
var BTSPassRealm = "BTSXMLRPCPass";
// ============================================
var NotLoggedinException = function NotLoggedinException (message) {
this.message = message;
this.name = "NotLoggedinException";
};
NotLoggedinException.prototype.toString = function () {
return this.name + ': "' + this.message + '"';
};
exports.NotLoggedinException = NotLoggedinException;
// ====================================================================================
// BZPage's methods
var BZPage = function BZPage(win, config) {
// constants
this.SalmonPink = new Color(255, 224, 176); // RGB 255, 224, 176; HSL 36, 2,
// 85
this.ReporterColor = new Color(255, 255, 166); // RGB 255, 255, 166; HSL 60, 2,
// 83
// initialize dynamic properties
var that = this;
this.win = win;
this.doc = win.document;
this.hostname = this.win.location.hostname;
// First, preflight check ... if we are not logged in, there
// is nothing we can do.
var logoutLink = Array.some(this.doc.links, function (x) {
return x.search === "?logout=1" ;
});
if (!logoutLink) {
throw new NotLoggedinException("Not logged in");
}
// So, now we know we are logged in, so we can get to
// the real work.
this.packages = this.getInstalledPackages(config);
if ("commentStrings" in config.gJSONData) {
this.commentStrings = config.gJSONData.commentStrings;
}
this.constantData = {};
if ("constantData" in config.gJSONData) {
this.constantData = config.gJSONData.constantData;
this.constantData.queryUpstreamBug = JSON.parse(
selfMod.data.load("queryUpstreamBug.json"));
}
if ("CCmaintainer" in this.constantData) {
this.defBugzillaMaintainerArr = this.constantData.CCmaintainer;
}
if ("suspiciousComponents" in config.gJSONData.configData) {
this.suspiciousComponents = config.gJSONData.configData.suspiciousComponents;
}
if ("XorgLogAnalysis" in config.gJSONData.configData) {
this.xorglogAnalysis = config.gJSONData.configData.XorgLogAnalysis;
}
if ("submitsLogging" in config.gJSONData.configData &&
config.gJSONData.configData.submitsLogging) {
this.log = config.logger;
this.setUpLogging();
}
if ("killNodes" in config.gJSONData.configData &&
this.hostname in config.gJSONData.configData.killNodes) {
var killConf = config.gJSONData.configData.killNodes[this.hostname];
util.killNodes(this.doc, killConf[0], killConf[1])
}
this.setConfigurationButton();
this.submitHandlerInstalled = false;
this.bugNo = util.getBugNo(this.doc.location.toString());
this.title = this.doc.getElementById("short_desc_nonedit_display").textContent;
this.CCList = this.getCCList();
// Prepare for query buttons
// element ID brElementPlace_location is later used in JSON files
// Stay with this add_comment element even if RH BZ upgrades, this seems
// to be generally much more stable (even with other bugzillas, e.g. b.gnome.org)
// then some getElementById.
var commentArea = this.doc.getElementsByName("add_comment")[0].parentNode;
if (commentArea) {
var brElementPlacer = commentArea.getElementsByTagName("br");
brElementPlacer = brElementPlacer[0];
if (brElementPlacer) {
brElementPlacer.setAttribute("id","brElementPlacer_location");
brElementPlacer.parentNode.insertBefore(this.doc.createElement("br"),
brElementPlacer);
}
}
this.checkComments();
this.generateButtons();
};
/**
* Get the ID of the bug.
*
* @return string
*/
BZPage.prototype.getBugId = function getBugId () {
return util.getBugNo(this.doc.location.href);
};
/**
*
*/
BZPage.prototype.getInstalledPackages = function getInstalledPackages(cfg) {
var installedPackages = {};
var enabledPackages = [];
// Collect enabled packages per hostname (plus default ones)
if (cfg.gJSONData && ("commentPackages" in cfg.gJSONData)) {
if ("enabledPackages" in cfg.gJSONData.configData) {
var epObject = cfg.gJSONData.configData.enabledPackages;
if (this.hostname in epObject) {
enabledPackages = enabledPackages.concat(epObject[this.hostname].split(/[, ]/));
}
if ("any" in epObject) {
enabledPackages = enabledPackages.concat(epObject.any.split(/[, ]/));
}
}
if ((enabledPackages.length === 1) && (enabledPackages[0] === "all")) {
enabledPackages = [];
for (var key in cfg.gJSONData.commentPackages) {
enabledPackages.push(key);
}
}
enabledPackages.forEach(function (pkg, idx, arr) {
if (pkg in cfg.gJSONData.commentPackages) {
installedPackages[pkg] = cfg.gJSONData.commentPackages[pkg];
}
});
}
return installedPackages;
};
/**
* Actual execution function
*
* @param cmdLabel String with the name of the command to be executed
* @param cmdParams Object with the appropriate parameters for the command
*/
BZPage.prototype.centralCommandDispatch = function centralCommandDispatch (cmdLabel, cmdParams) {
switch (cmdLabel) {
case "resolution":
case "product":
case "component":
case "version":
case "priority":
this.selectOption(cmdLabel, cmdParams);
break;
case "status":
this.selectOption("bug_status", cmdParams);
break;
case "platform":
this.selectOption("rep_platform", cmdParams);
break;
case "os":
this.selectOption("op_sys", cmdParams);
break;
case "severity":
this.selectOption("bug_severity", cmdParams);
break;
case "target":
this.selectOption("target_milestone", cmdParams);
break;
case "addKeyword":
this.addStuffToTextBox("keywords",cmdParams);
break;
case "removeKeyword":
this.removeStuffFromTextBox("keywords", cmdParams);
break;
case "addWhiteboard":
this.addStuffToTextBox("status_whiteboard",cmdParams);
break;
case "removeWhiteboard":
this.removeStuffFromTextBox("status_whiteboard",cmdParams);
break;
case "assignee":
this.changeAssignee(cmdParams);
break;
case "qacontact":
this.clickMouse("bz_qa_contact_edit_action");
this.doc.getElementById("qa_contact").value = cmdParams;
break;
case "url":
this.clickMouse("bz_url_edit_action");
this.doc.getElementById("bug_file_loc").value = cmdParams;
break;
// TODO dependson/blocked doesn't work. Find out why.
case "addDependsOn":
this.clickMouse("dependson_edit_action");
this.addStuffToTextBox("dependson", cmdParams);
break;
case "removeDependsOn":
this.clickMouse("dependson_edit_action");
this.removeStuffFromTextBox("dependson", cmdParams);
break;
case "addBlocks":
this.clickMouse("blocked_edit_action");
this.addStuffToTextBox("blocked", cmdParams);
break;
case "removeBlocks":
this.clickMouse("blocked_edit_action");
this.removeStuffFromTextBox("blocked", cmdParams);
break;
case "comment":
this.addStuffToTextBox("comment", cmdParams);
break;
case "commentIdx":
var commentText = this.commentStrings[cmdParams];
this.addStuffToTextBox("comment", commentText);
break;
case "setNeedinfo":
// cmdParams are actually ignored for now; we may in future
// distinguish different actors to be target of needinfo
this.setNeedinfoReporter();
break;
case "addCC":
this.addToCCList(cmdParams);
break;
case "queryStringOurBugzilla":
this.queryForSelection();
break;
// TODO flags, see also
case "commit":
if (cmdParams) {
// Directly commit the form
this.doc.forms.namedItem("changeform").submit();
}
break;
}
};
/**
* Take the ID of the package/id combination, and execute it
*
* @param String combined package + "//" + id combination
* Fetches the command object from this.installedPackages and then
* goes through all commands contained in it, and calls
* this.centralCommandDispatch to execute them.
*
* PROBLEM: according to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\
* /Statements/for...in there is no guaranteed order of execution of
* commands (i.e., key, commentObj[key] pairs) in for..in cycle.
* According to https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\
* /Operators/Special_Operators/delete_Operator#Cross-browser_issues it seems that
* everywhere except of Internet Explorer this should work well, but waiting
* impatiently when this bite us.
*/
BZPage.prototype.executeCommand = function executeCommand (cmd) {
var cmdArr = cmd.split("//");
var commentObj = this.packages[cmdArr[0]][cmdArr[1]];
for (var key in commentObj) {
this.centralCommandDispatch(key,commentObj[key]);
}
};
/**
* Change assignee of the bug
*
* @param newAssignee String with the email address of new assigneeAElement
* or 'default' if the component's default assignee should be used.
* Value null clears "Reset Assignee to default for component" checkbox
* @return none
*/
BZPage.prototype.changeAssignee = function changeAssignee (newAssignee) {
var defAssigneeButton = null;
// Previous assignee should know what's going on in his bug
this.addToCCList(this.owner);
// Optional value null
if (newAssignee === null) {
this.doc.getElementById("set_default_assignee").removeAttribute(
"checked");
return ;
}
if (this.getDefaultAssignee) {
if (newAssignee === "default") {
var defAss = this.getDefaultAssignee();
if (defAss) {
newAssignee = defAss;
} else {
return ;
}
}
}
if (newAssignee) {
this.clickMouse("bz_assignee_edit_action");
this.doc.getElementById("assigned_to").value = newAssignee;
this.doc.getElementById("set_default_assignee").checked = false;
defAssigneeButton = this.doc.getElementById("setDefaultAssignee_btn");
if (defAssigneeButton) {
defAssigneeButton.style.display = "none";
}
}
};
/**
* Adds new option to the 'comment_action' scroll down box
*
* @param pkg String package name
* @param cmd String with the name of the command
* If the 'comment_action' scroll down box doesn't exist, this
* function will set up new one.
*/
BZPage.prototype.addToCommentsDropdown = function addToCommentsDropdown (pkg, cmd) {
var select = this.doc.getElementById("comment_action");
if (!select) {
var that = this;
this.doc.getElementById("comments").innerHTML +=
"