diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/bzpage.js | 1087 | ||||
-rw-r--r-- | lib/color.js | 236 | ||||
-rw-r--r-- | lib/libbugzilla.js | 383 | ||||
-rw-r--r-- | lib/logger.js | 134 | ||||
-rw-r--r-- | lib/main.js | 190 | ||||
-rw-r--r-- | lib/mozillabzpage.js | 19 | ||||
-rw-r--r-- | lib/passwords.js | 1 | ||||
-rw-r--r-- | lib/prompts.js | 3 | ||||
-rw-r--r-- | lib/rhbzpage.js | 1023 | ||||
-rw-r--r-- | lib/util.js | 189 | ||||
-rw-r--r-- | lib/xmlrpc.js | 1 |
11 files changed, 547 insertions, 2719 deletions
diff --git a/lib/bzpage.js b/lib/bzpage.js deleted file mode 100644 index cb539dc..0000000 --- a/lib/bzpage.js +++ /dev/null @@ -1,1087 +0,0 @@ -/*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 preferences = require("preferences-service"); -var selection = require("selection"); -var prompts = require("prompts"); -var Request = require("request").Request; -var tabs = require("tabs"); -var Color = require("color").Color; - -var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; - -// Shared contstants -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")); - this.constantData.XMLRPCData = JSON.parse( - selfMod.data.load("XMLRPCdata.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.login = this.getLogin(); - // XML-RPC password - if (this.hostname in this.constantData.XMLRPCData) { - this.password = this.getPassword(this.login); - } - - this.bugNo = util.getBugNo(this.doc.location.toString()); - - // deal with aliases - if (isNaN(parseInt(this.bugNo, 10)) && this.password) { - this.bugNo = this.getRealBugNo(); - } - - 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(); -}; - -/** - * In case URL contains alias, not the real bug number, get the real bug no - * from the XML representation. Sets correct value to this.bugNo. - */ -BZPage.prototype.getRealBugNo = function () { - console.log("We have to deal with bug aliased as " + this.bugNo); - var that = this; - // https://bugzilla.redhat.com/show_bug.cgi?ctype=xml&id=serialWacom - Request({ - url: this.win.location.href+"&ctype=xml", - onComplete: function(response) { - if (response.status === 200) { - var xmlRepr = response.xml; - var bugID = parseInt(xmlRepr.getElementsByTagName("bug_id")[0].textContent, 10); - if (isNaN(bugID)) { - throw new Error("Cannot get bug no. even from XML representation!"); - } - that.bugNo = bugID; - console.log("The real bug no. is " + bugID); - } - } - }).get(); -}; - -/** - * - */ -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(/[,\s]+/)); - } - if ("any" in epObject) { - enabledPackages = enabledPackages.concat(epObject.any.split(/[,\s]+/)); - } - } - - 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 += - "<div id='make_bugzilla_comment_action'>" + - " <label for='comment_action'>Add Comment: </label>" + - " <select id='comment_action'>" + - " <option value=''>-- Select Comment from List --</option>" + - "</div>"; - select = this.doc.getElementById("comment_action"); - select.addEventListener("change", function (evt) { - var value = ""; - var valueElement = that.doc.getElementById("comment_action"); - if (valueElement) { - var selIdx = valueElement.selectedIndex; - value = valueElement.options[selIdx].value; - console.log("value = " + value); - } else { - return; - } - that.executeCommand(value); - }, false); - } - - var opt = this.doc.createElement("option"); - opt.value = pkg + "//" + cmd; - opt.textContent = this.packages[pkg][cmd].name; - select.appendChild(opt); -}; - -/** - * Create a A element leadink nowhere, but with listener running a callback on the click - * - * @param id String with a id to be added to the element - * @param text String with a string to be added as a textContent of the element - * @param parent Node which is a parent of the object - * @param callback Function to be called after clicking on the link - * @param params Array with parameters of the callback - * @param Boolean breakBefore if there should be a <br> element before. - * @return none - */ -BZPage.prototype.createDeadLink = function createDeadLink (id, text, parent, callback, params, before, covered, accesskey) { - var that = this; - params = util.valToArray(params); - var locParent = {}; - - // Yes, I want != here, not !== - if (covered != null) { - locParent = this.doc.createElement(covered); - parent.appendChild(locParent); - } else { - locParent = parent; - } - - var newAElem = this.doc.createElement("a"); - newAElem.setAttribute("id", id); - if (accesskey) { - newAElem.setAttribute("accesskey", accesskey); - } - newAElem.setAttribute("href", ""); - newAElem.textContent = text; - - newAElem.addEventListener("click", function(evt) { - callback.apply(that, params); - evt.stopPropagation(); - evt.preventDefault(); - }, false); - - if ((before === "br") || (before === true)) { - locParent.appendChild(this.doc.createElement("br")); - } else if (before === "dash") { - locParent.appendChild(this.doc.createTextNode("\u00A0-\u00A0")); - } - - locParent.appendChild(newAElem); -}; - - -/** - * Generic function to add new button to the page. Actually copies new button - * from the old one (in order to have the same look-and-feel, etc. - * - * @param location Object around which the new button will be added - * @param after Boolean before or after location ? - * @param pkg String which package to take the command from - * @param id String which command to take - * @return none - */ -BZPage.prototype.createNewButton = function createNewButton (location, after, pkg, id) { - var that = this; - var cmdObj = this.packages[pkg][id]; - var newId = id + "_btn"; - var label = cmdObj.name; - - // protection against double-firings - if (this.doc.getElementById(newId)) { - console.log("Element with id " + newId + "already exists!"); - return ; - } - - // creation of button might be conditional on existence of data in constantData - if ("ifExist" in cmdObj) { - if (!(cmdObj.ifExist in this.constantData)) { - return ; - } - } - - var newButton = this.doc.createElement("input"); - newButton.setAttribute("id", newId); - newButton.setAttribute("type", "button"); - newButton.value = label; - newButton.addEventListener("click", function(evt) { - that.executeCommand(pkg + "//" + id); - }, false); - - var originalLocation = this.doc.getElementById(location); - - try { - if (after) { - originalLocation.parentNode.insertBefore(newButton, - originalLocation.nextSibling); - originalLocation.parentNode.insertBefore(this.doc - .createTextNode("\u00A0"), newButton); - } else { - originalLocation.parentNode.insertBefore(newButton, originalLocation); - originalLocation.parentNode.insertBefore(this.doc - .createTextNode("\u00A0"), originalLocation); - } - } catch (e) { - if (e instanceof TypeError) { - console.error("cannot find originalLocation element with id " + location); - } else { - throw e; - } - } -}; - -/** - * - */ -BZPage.prototype.generateButtons = function generateButtons () { - var topRowPosition = "topRowPositionID"; - var bottomRowPosition = "commit"; - - // create anchor for the top toolbar - var commentBox = this.doc.getElementById("comment"); - var brElement = this.doc.createElement("br"); - brElement.setAttribute("id",topRowPosition); - commentBox.parentNode.normalize(); - commentBox.parentNode.insertBefore(brElement, commentBox); - - for (var pkg in this.packages) { - for (var cmdIdx in this.packages[pkg]) { - var cmdObj = this.packages[pkg][cmdIdx]; - switch (cmdObj.position) { - case "topRow": - this.createNewButton(topRowPosition, false, pkg, cmdIdx); - break; - case "bottomRow": - this.createNewButton(bottomRowPosition, false, pkg, cmdIdx); - break; - case "dropDown": - this.addToCommentsDropdown(pkg,cmdIdx); - break; - default: // [+-]ID - var firstChr = cmdObj.position.charAt(0); - var newId = cmdObj.position.substr(1); - this.createNewButton(newId, firstChr === "+", pkg, cmdIdx); - break; - } - } - } -}; - -BZPage.prototype.setConfigurationButton = function setConfigurationButton () { - var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); - var configurationButtonUI = this.doc.createElement("li"); - configurationButtonUI.innerHTML = "\u00A0-\u00A0<a href='' id='configurationButton'>" - + "Triage configuration</a>"; - additionalButtons.appendChild(configurationButtonUI); - this.doc.getElementById("configurationButton").addEventListener( - "click", - function(evt) { - var prfNm = BTSPrefNS+"JSONURL"; - var url = preferences.get(prfNm,""); - - var reply = prompts.prompt("New location of JSON configuration file", url); - if (reply) { - preferences.set(prfNm, reply.trim()); - prompts.alert("For now, you should really restart Firefox!"); - } - - evt.stopPropagation(); - evt.preventDefault(); - }, false); -}; - -/* - * From <a> element diggs out just plain email address - * Note that bugzilla.gnome.org doesn't have mailto: url but - * https://bugzilla.gnome.org/page.cgi?id=describeuser.html&login=mcepl%40redhat.com - * - * @param aElement Element with href attribute or something else - * @return String with the address or null - * - */ -BZPage.prototype.parseMailto = function parseMailto(aElement) { - var emailStr = "", hrefStr = ""; - // use url utils - if (aElement) { - hrefStr = decodeURIComponent(aElement.getAttribute("href")); - emailStr = hrefStr.split(":")[1]; - // - if (emailStr === undefined) { - var params = util.getParamsFromURL("https://" + this.hostname + "/" + hrefStr); - emailStr = decodeURI(params.login); - } - return emailStr; - } - return null; -}; - -/** - * Get the current email of the reporter of the bug. - * - * @return string - */ -BZPage.prototype.getReporter = function getReporter () { - var reporterElement = this.getOptionTableCell("bz_show_bug_column_2", "Reported"); - // RH Bugzilla after upgrade to 3.6.2 moved the information to other column - if (!reporterElement) { - reporterElement = this.getOptionTableCell("bz_show_bug_column_1", "Reported", true); - } - // Maemo calls the label "Reporter" and it doesn't have ids on table columns ... TODO(maemo) - - return this.parseMailto(reporterElement); -}; - -BZPage.prototype.getComponent = function getComponent() { - var elem = this.doc.getElementById("component"); - if (elem) { - return elem.value; - } - return null; -}; - - -BZPage.prototype.commentsWalker = function commentsWalker (fce) { - var comments = this.doc.getElementById("comments").getElementsByClassName( - "bz_comment"); - Array.forEach(comments, function(item) { - fce(item); - }, this); -}; - -/** - * Set background color of all comments made by reporter in ReporterColor color - * - */ -BZPage.prototype.checkComments = function checkComments () { - var that = this; - var reporterRE = new RegExp(this.getReporter()); - this.commentsWalker(function(x) { - var email = that.parseMailto(x.getElementsByClassName("vcard")[0] - .getElementsByTagName("a")[0]); - if (reporterRE.test(email)) { - x.style.backgroundColor = that.ReporterColor.toString(); - } - }); -}; - -BZPage.prototype.collectComments = function collectComments () { - var outStr = ""; - this.commentsWalker(function(x) { - outStr += x.getElementsByTagName("pre")[0].textContent + "\n"; - }); - return outStr.trim(); -}; - - -/** - * Select option with given value on the <SELECT> element with given id. - * - * Also execute change HTMLEvent, so that the form behaves accordingly. - * - * @param id - * @param label - * @return none - * - */ -BZPage.prototype.selectOption = function selectOption (id, label, fireEvent) { - if (!fireEvent) { - fireEvent = true; - } - var sel = this.doc.getElementById(id); - sel.value = label; - if (fireEvent) { - var intEvent = this.doc.createEvent("HTMLEvents"); - intEvent.initEvent("change", true, true); - sel.dispatchEvent(intEvent); - } -}; - -BZPage.prototype.selectOptionByLabel = function selectOptionByLabel(id, label, fireEvent) { - if (!fireEvent) { - fireEvent = true; - } - var sel = this.doc.getElementById(id); - var labelRE = new RegExp(label.trim()); - var ourOption = Array.filter(sel.options, function (op) { - return op.textContent.trim() === label; - }, this); - - if (ourOption[0]) { - sel.value = ourOption[0].value; - } - - if (fireEvent) { - var intEvent = this.doc.createEvent("HTMLEvents"); - intEvent.initEvent("change", true, true); - sel.dispatchEvent(intEvent); - } -}; - -/** - * Send mouse click to the specified element - * - * @param String ID of the element to send mouseclick to - * @return None - */ -BZPage.prototype.clickMouse = function clickMouse (targetID) { - var localEvent = this.doc.createEvent("MouseEvents"); - localEvent.initMouseEvent("click", true, true, this.doc.defaultView, 0, 0, - 0, 0, 0, false, false, false, false, 0, null); - this.doc.getElementById(targetID).dispatchEvent(localEvent); -}; - -/** - * Add object to the text box (comment box or status whiteboard) - * - * @param id String with the id of the element - * @param stuff String/Array to be added to the comment box - * - * @return none - */ -BZPage.prototype.addStuffToTextBox = function addStuffToTextBox (id, stuff) { - var textBox = this.doc.getElementById(id); - if (textBox.tagName.toLowerCase() === "textarea") { - stuff = textBox.value ? "\n\n" + stuff : stuff; - textBox.value += stuff; - } else { - textBox.value = util.addCSVValue(textBox.value,stuff); - } -}; - -/** - * Remove a keyword from the element if it is there - * - * @param id String with the id of the element - * @param stuff String/Array with keyword(s) to be removed - */ -BZPage.prototype.removeStuffFromTextBox = function removeStuffFromTextBox (id, stuff) { - var changedElement = this.getElementById(id); - changedElement.value = util.removeCSVValue(changedElement.value,stuff); -}; - -/** - * generalized hasKeyword ... search in the value of the box with given id - * - * @param id String with ID of the element we want to check - * @param str String to be searched for - * @return Boolean found? - */ -BZPage.prototype.idContainsWord = function idContainsWord (id, str) { - var kwd = ""; - try { - kwd = this.doc.getElementById(id).value; - } catch (e) { - // For those who don't have particular element at all or if it is empty - return false; - } - return (util.isInList(str, kwd.trim().split(/[,\s]+/))); -}; - -/** - * Check for the presence of a keyword - * - * @param str String with the keyword - * @return Boolean - */ -BZPage.prototype.hasKeyword = function hasKeyword (str) { - return (this.idContainsWord('keywords', str)); -}; - -/** - -@return Element with the href attribute containng the information - */ -BZPage.prototype.getOptionTableCell = function getOptionTableCell(tableId, label) { - var cleanLabelRE = /^\s*([^.:]*):?\s*$/; - label = label.trim().replace(cleanLabelRE,"$1").toLowerCase(); - - var rows = this.doc.getElementById(tableId).getElementsByTagName("tr"); - var ourLine = Array.filter(rows, function(row) { - var curLabel = row.getElementsByTagName("td")[0].textContent.toLowerCase(); - curLabel = curLabel.replace(cleanLabelRE,"$1"); - return (curLabel === label); // maybe this could be a RE match instead - }); - - if (ourLine.length > 0) { - return ourLine[0].getElementsByTagName("td")[1]. - getElementsByTagName("a")[0]; - } - return null; -}; - -/** - * Set the bug to NEEDINFO state - * - * Working function. - * @return none - * @todo TODO we may extend this to general setNeedinfo function - * with parameter [reporter|assignee|general-email-address] - */ -BZPage.prototype.setNeedinfoReporter = function setNeedinfoReporter () { - this.clickMouse("needinfo"); - this.selectOption("needinfo_role", "reporter"); -}; - -/** - * - */ -BZPage.prototype.getOwner = function getOwner () { - // TODO(maemo) doesn't work on maemo - var assigneeAElement = this.getOptionTableCell("bz_show_bug_column_1","Assigned To"); - return this.parseMailto(assigneeAElement); -}; - -/** - * Return maintainer which is per default by bugzilla - * (which is not necessarily the one who is default maintainer per component) - * - * @return String with the maintainer's email address - */ -BZPage.prototype.getDefaultBugzillaMaintainer = function getDefaultBugzillaMaintainer (component) { - var address = util.filterByRegexp(this.defBugzillaMaintainerArr, component); - return address; -}; - -/** - * Parse the row with the attachment - * - * @param DOM element to be parsed - * @return array with string name of the attachment, integer its id number, - * string of MIME type, integer of size in kilobytes, and the whole - * element itself - */ -BZPage.prototype.parseAttachmentLine = function(inElem) { - var MIMEtype = ""; - var size = 0; - - // Skip over obsolete attachments - if (inElem.getElementsByClassName("bz_obsolete").length > 0) { - return ( []); - } - - // getting name of the attachment - // TODO probably could use url.URL object - var attName = inElem.getElementsByTagName("b")[0].textContent.trim(); - - var aHrefsArr = inElem.getElementsByTagName("a"); - var aHref = Array.filter(aHrefsArr, function(x) { - return x.textContent.trim() === "Details"; - })[0]; - var id = parseInt(aHref.getAttribute("href").replace( - /^.*attachment.cgi\?id=/, ""), 10); - - // getting MIME type and size - var stringArray = inElem.getElementsByClassName("bz_attach_extra_info")[0].textContent - .replace(/[\n ()]+/g, " ").trim().split(", "); - size = parseInt(stringArray[0], 10); - MIMEtype = stringArray[1].split(" ")[0]; - - return [ attName, id, MIMEtype, size, inElem ]; -}; - -/** - * collect the list of attachments in a structured format - * - * @return Array of arrays, one for each attachments; - * each record has string name of the attachment, integer its id number, - * string of MIME type, integer of size in kilobytes, and the whole - * element itself - */ -BZPage.prototype.getAttachments = function getAttachments () { - var outAtts = []; - var atts = this.doc.getElementById("attachment_table") - .getElementsByTagName("tr"); - for ( var i = 1, ii = atts.length - 1; i < ii; i++) { - outAtts.push(this.parseAttachmentLine(atts[i])); - } - return outAtts; -}; - -/** - * Get login of the currently logged-in user. - * - * @return String with the login name of the currently logged-in user - */ -BZPage.prototype.getLogin = function getLogin () { - var lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); - var loginArr = lastLIElement.textContent.split("\n"); - var loginStr = loginArr[loginArr.length - 1].trim(); - return loginStr; -}; - -/** - * returns password from the current storage, or if there isn't - * one, then it will ask user for it. - * - * @return String with the password - */ -BZPage.prototype.getPassword = function getPassword (login) { - var passPrompt = "Enter your Bugzilla password for fixing MIME attachment types"; - var switchPrompt = "Do you want to switch off features requiring password completely?"; - var prefName = BTSPrefNS+"withoutPassowrd"; - var domain = this.win.location.protocol + "//" + this.hostname; - - var pass = passUtils.getPassword(login, domain, BTSPassRealm); - // pass === null means no appropriate password in the storage - if (!preferences.get(prefName,false) && (pass === null)) { - var passwordText = prompts.promptPassword(passPrompt); - if (passwordText && passwordText.length > 0) { - passUtils.setLogin(login, passwordText, domain, - BTSPassRealm); - return passwordText; - } else { - var switchOff = prompts.promptYesNoCancel(switchPrompt); - if (switchOff) { - preferences.set(prefName,true); - } - return null; - } - } else { - return pass; - } -}; - -/** - * - */ -BZPage.prototype.setUpLogging = function setUpLogging () { - // Protection against double-call - if (this.doc.getElementById("generateTSButton")) { - return ; - } - - // For adding additional buttons to the top toolbar - var additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); - var that = this; - - // logging all submits for timesheet - if (!this.submitHandlerInstalled) { - this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { - var resp = that.log.addLogRecord(that); - if (resp === null) { - evt.stopPropagation(); - evt.preventDefault(); - } - }, false); - this.submitHandlerInstalled = true; - } - - // (id, text, parent, callback, params, before, covered, accesskey) - this.createDeadLink("generateTSButton", "Generate TS", additionalButtons, - function(evt) { - that.log.createBlankPage.call(that.log, "TimeSheet", - that.log.generateTimeSheet); - }, [], "dash", "li"); - - this.createDeadLink("clearLogs", "Clear TS", additionalButtons, - function(evt) { - that.log.clearStore(this); - }, [], "dash", "li"); - - this.createDeadLink("importTSButton", "Import TS", additionalButtons, - function(evt) { - jsonPaths = prompts.promptFileOpenPicker(that.win); - that.log.importOtherStore(jsonPaths, clearLogAElem); - }, [], "dash", "li"); - - var clearLogAElem = this.doc.getElementById("clearLogs"); - if (this.log.isEmpty()) { - clearLogAElem.style.color = this.log.EmptyLogsColor; - clearLogAElem.style.fontWeight = "normal"; - } else { - clearLogAElem.style.color = this.log.FullLogsColor; - clearLogAElem.style.fontWeight = "bolder"; - } -}; - -BZPage.prototype.getSelectionOrClipboard = function getSelectionOrClipboard () { - var text = selection.text; - if (!text) { - text = clip.get(); - } - return text; -}; - -/** - * Opens a new tab with a query for the given text in the selected component - * - * @param text to be searched for - * @param component String with the component name (maybe latter regexp?) - * @param product (optional) string with the product name, if undefined, - * search in all products - * @return None - * - */ -BZPage.prototype.queryInNewTab = function(text, component, product) { - var urlStr = "https://bugzilla.redhat.com/buglist.cgi?query_format=advanced"; - if (product) { - urlStr += "&product=" + product.trim(); - } - if (component) { - if ("equivalentComponents" in this.constantData) { - var equivCompsArr = this.constantData["equivalentComponents"]. - filter(function (REstr) { - return new RegExp(REstr).test(this.getComponent()); - }, this); - if (equivCompsArr.length > 0) { - component = equivCompsArr[0]; - } - urlStr += "&field0-0-0=component&type0-0-0=regexp&value0-0-0=" - + component.trim(); - } else { - urlStr += "&field0-0-0=component&type0-0-0=substring&value0-0-0=" - + component.trim(); - } - } - - // using more complicated query tables here, because they can be more easily - // edited - // for further investigative searches - if (text) { - text = encodeURIComponent(text.trim()); - var searchText = "&field1-0-0=longdesc&type1-0-0=substring&value1-0-0=" - + text - + "&field1-0-1=attach_data.thedata&type1-0-1=substring&value1-0-1=" - + text - + "&field1-0-2=status_whiteboard&type1-0-2=substring&value1-0-2=" - + text; - urlStr += searchText; - tabs.open({ - url: urlStr, - inBackground: true, - onOpen: function (t) { - tabs.activeTab = t; - } - }); - } -}; - -/** - * Get the text to search for and prepare other things for the real executive - * function this.queryInNewTab, and run it. - */ -BZPage.prototype.queryForSelection = function() { - var text = this.getSelectionOrClipboard(); - if (text) { - this.queryInNewTab(text, this.getComponent()); - } -}; - -/** - * adds a person to the CC list, if it isn't already there - * - * @param who String with email address or "self" if the current user - * of the bugzilla should be added - */ -BZPage.prototype.addToCCList = function addToCCList (who) { - if (!who) { - return ; - } - if (who === "self") { - this.doc.getElementById("addselfcc").checked = true; - } else { - this.clickMouse("cc_edit_area_showhide"); - if (!util.isInList(who, this.CCList)) { - this.addStuffToTextBox("newcc",who); - } - } -}; - -/** - * a collect a list of emails on CC list - * - * @return Array with email addresses as Strings. - */ -BZPage.prototype.getCCList = function getCCList () { - var CCListSelect = this.doc.getElementById("cc"); - var outCCList = []; - if (CCListSelect) { - outCCList = Array.map(CCListSelect.options, function(item) { - return item.value; - }); - } - return outCCList; -}; - -// exports.BZPage = apiUtils.publicConstructor(BZPage); -exports.BZPage = BZPage; diff --git a/lib/color.js b/lib/color.js deleted file mode 100644 index 574ad19..0000000 --- a/lib/color.js +++ /dev/null @@ -1,236 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -// ============================================================================ -// Color management methods -// originally from -// http://www.mjijackson.com/2008/02\ -// /rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript -var Color = exports.Color = function Color(r, g, b) { - this.Luminosity = 0.85; - this.Desaturated = 0.4; - - if (r instanceof Array) { - this.r = r[0]; - this.g = r[1]; - this.b = r[2]; - } else { - this.r = r; - this.g = g; - this.b = b; - } -}; - -Color.prototype.update = function(r, g, b) { - this.r = r; - this.g = g; - this.b = b; -}; - -Color.prototype.hs = function(nStr) { - if (Number(nStr) === 0) { - return "00"; - } else if (nStr.length < 2) { - return "0" + nStr; - } else { - return nStr; - } -}; - -Color.prototype.toString = function() { - var rH = Number(this.r.toFixed()).toString(16); - var gH = Number(this.g.toFixed()).toString(16); - var bH = Number(this.b.toFixed()).toString(16); - return "#" + this.hs(rH) + this.hs(gH) + this.hs(bH); -}; - -/** - * Converts an RGB color value to HSL. Conversion formula adapted from - * http://en.wikipedia.org/wiki/HSL_color_space. Assumes r, g, and b are - * contained in the set [0, 255] and returns h, s, and l in the set [0, 1].4343 - * - * @param Number r The red color value - * @param Number g The green color value - * @param Number b The blue color value - * @return Array The HSL representation - */ -Color.prototype.hsl = function() { - var r = this.r / 255; - var g = this.g / 255; - var b = this.b / 255; - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, l = (max + min) / 2; - - if (max === min) { - h = s = 0; // achromatic - } else { - var d = max - min; - s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - switch (max) { - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break; - case g: - h = (b - r) / d + 2; - break; - case b: - h = (r - g) / d + 4; - break; - } - h /= 6; - } - - return [ h, s, l ]; -}; - -/** - * Converts an HSL color value to RGB. Conversion formula adapted from - * http://en.wikipedia.org/wiki/HSL_color_space. Assumes h, s, and l are - * contained in the set [0, 1] and returns r, g, and b in the set [0, 255]. - * - * @param Number h The hue - * @param Number s The saturation - * @param Number l The lightness - * @return Array The RGB representation - */ -Color.prototype.hslToRgb = function(h, s, l) { - function hue2rgb(p, q, t) { - if (t < 0) { - t += 1; - } - if (t > 1) { - t -= 1; - } - if (t < 1 / 6) { - return p + (q - p) * 6 * t; - } - if (t < 1 / 2) { - return q; - } - if (t < 2 / 3) { - return p + (q - p) * (2 / 3 - t) * 6; - } - return p; - } - - var r, g, b; - - if (s === 0) { - r = g = b = l; // achromatic - } else { - var q = l < 0.5 ? l * (1 + s) : l + s - l * s; - var p = 2 * l - q; - r = hue2rgb(p, q, h + 1 / 3); - g = hue2rgb(p, q, h); - b = hue2rgb(p, q, h - 1 / 3); - } - - return [ r * 255, g * 255, b * 255 ]; -}; - -/** - * Converts an RGB color value to HSV. Conversion formula adapted from - * http://en.wikipedia.org/wiki/HSV_color_space. Assumes r, g, and b are - * contained in the set [0, 255] and returns h, s, and v in the set [0, 1]. - * - * @param Number r The red color value - * @param Number g The green color value - * @param Number b The blue color value - * @return Array The HSV representation - */ -Color.prototype.hsv = function() { - var r = this.r / 255; - var g = this.g / 255; - var b = this.b / 255; - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - - var d = max - min; - s = max === 0 ? 0 : d / max; - - if (max === min) { - h = 0; // achromatic - } else { - switch (max) { - case r: - h = (g - b) / d + (g < b ? 6 : 0); - break; - case g: - h = (b - r) / d + 2; - break; - case b: - h = (r - g) / d + 4; - break; - } - h /= 6; - } - - return [ h, s, v ]; -}; - -/** - * Converts an HSV color value to RGB. Conversion formula adapted from - * http://en.wikipedia.org/wiki/HSV_color_space. Assumes h, s, and v are - * contained in the set [0, 1] and returns r, g, and b in the set [0, 255]. - * - * @param Number h The hue - * @param Number s The saturation - * @param Number v The value - * @return Array The RGB representation - */ -Color.prototype.hsvToRgb = function(h, s, v) { - var r, g, b; - - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - - switch (i % 6) { - case 0: - r = v; - g = t; - b = p; - break; - case 1: - r = q; - g = v; - b = p; - break; - case 2: - r = p; - g = v; - b = t; - break; - case 3: - r = p; - g = q; - b = v; - break; - case 4: - r = t; - g = p; - b = v; - break; - case 5: - r = v; - g = p; - b = q; - break; - } - - return [ r * 255, g * 255, b * 255 ]; -}; - -/** - * Provide - */ -Color.prototype.lightColor = function() { - var hslArray = this.hsl(); - var h = Number(hslArray[0]); - var s = Number(hslArray[1]) * this.Desaturated; - var l = this.Luminosity; - var desA = this.hslToRgb(h, s, l); - return new Color(desA[0], desA[1], desA[2]); -}; diff --git a/lib/libbugzilla.js b/lib/libbugzilla.js new file mode 100644 index 0000000..600d193 --- /dev/null +++ b/lib/libbugzilla.js @@ -0,0 +1,383 @@ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +// +"use strict"; +var preferences = require("preferences-service"); +var prompts = require("prompts"); +var clipboard = require("clipboard"); +var tabs = require("tabs"); +var logger = require("logger"); +var passUtils = require("passwords"); +var Request = require("request").Request; +var selfMod = require("self"); +var urlMod = require("url"); +var dataUtils = require("utils/data"); +var xrpc = require("xmlrpc"); +var panelMod = require("panel"); + +var JSONURLDefault = "https://fedorahosted.org/released"+ + "/bugzilla-triage-scripts/Config_data.json"; +var BTSPrefNS = "bugzilla-triage.setting."; +var BTSPassRealm = "BTSXMLRPCPass"; + +var passwords = {}; // hash of passwords indexed by a hostname +var config = exports.config = {}; + +function Message(cmd, data) { + console.log("Message: cmd = " + cmd + ", data = " + data); + this.cmd = cmd; + this.data = data; +} + +function log(msg) { + postMessage(new Message("LogMessage", msg)); +} + +/** + * parse XML object out of string working around various bugs in Gecko implementation + * see https://developer.mozilla.org/en/E4X for more information + * + * @param inStr String with unparsed XML string + * @return XML object + */ +function parseXMLfromString (inStuff) { + // if (typeof inStuff !== 'string') In future we should recognize this.response + // and get just .text property out of it. TODO + var respStr = inStuff.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); // bug 336551 + return new XML(respStr); +} + +/** + * In case URL contains alias, not the real bug number, get the real bug no + * from the XML representation. Sets correct value to this.bugNo. + * + * This is a slow variant for bugs other than actual window + */ +function getRealBugNoSlow(bugNo, location, callback) { + console.log("We have to deal with bug aliased as " + this.bugNo); + // https://bugzilla.redhat.com/show_bug.cgi?ctype=xml&id=serialWacom + Request({ + url: location.href+"&ctype=xml", + onComplete: function(response) { + if (response.status === 200) { + var xmlRepr = parseXMLfromString(response.text); + // TODO this is probably wrong, both XPath and .text attribute + var bugID = parseInt(xmlRepr.bug.bug_id.text, 10); + if (isNaN(bugID)) { + throw new Error("Cannot get bug no. even from XML representation!"); + } + console.log("The real bug no. is " + bugID); + callback(bugID) + } + } + }).get(); +} + +function getPassword(login, domain) { + var passPrompt = "Enter your Bugzilla password for fixing MIME attachment types"; + var switchPrompt = "Do you want to switch off features requiring password completely"; + var prefName = BTSPrefNS+"withoutPassowrd"; + + var pass = passUtils.getPassword(login, + domain, BTSPassRealm); + var retObject = { + password: null, // password string or null if no password provided + withoutPass: false // whether user doesn't want to use password at all + }; + + // pass === null means no appropriatjslie password in the storage + if (!preferences.get(prefName,false) && (pass === null)) { + var passwordText = prompts.promptPassword(passPrompt); + if (passwordText && passwordText.length > 0) { + passUtils.setLogin(login, passwordText, domain, + BTSPassRealm); + retObject.password = passwordText; + } else { + var switchOff = prompts.promptYesNoCancel(switchPrompt); + if (switchOff) { + preferences.set(prefName,true); + } + retObject.withoutPass = switchOff; + } + } else { + retObject.password = pass; + } + return retObject; +} + +exports.changeJSONURL = function changeJSONURL() { + var prfNm = BTSPrefNS+"JSONURL"; + var url = preferences.get(prfNm,""); + + var reply = prompts.prompt("New location of JSON configuration file", url); + if (reply) { + preferences.set(prfNm, reply.trim()); + // TODO Restartless add-on needs to resolve this. + prompts.alert("For now, you should really restart Firefox!"); + } +}; + +/** + * + libbz.getInstalledPackages(msg.data, function (pkgsMsg) { + worker.postMessage(pkgsMsg); + + locationLoginObj: { + location: window.location.href, + login: getLogin() + } + */ +exports.getInstalledPackages = function getInstalledPackages(locationLoginObj, callback) { + var installedPackages = {}; + var enabledPackages = []; + var location = locationLoginObj.location; + + if (typeof location == "string") { + location = new urlMod.URL(location); + } + + // Collect enabled packages per hostname (plus default ones) + if (config.gJSONData && ("commentPackages" in config.gJSONData)) { + if ("enabledPackages" in config.gJSONData.configData) { + var epObject = config.gJSONData.configData.enabledPackages; + if (location.host in epObject) { + enabledPackages = enabledPackages.concat(epObject[location.host].split(/[,\s]+/)); + } + if ("any" in epObject) { + enabledPackages = enabledPackages.concat(epObject.any.split(/[,\s]+/)); + } + } + + if ((enabledPackages.length === 1) && (enabledPackages[0] === "all")) { + enabledPackages = []; + for (var key in config.gJSONData.commentPackages) { + enabledPackages.push(key); + } + } + + // TODO To be decided, whether we cannot just eliminate packages in + // installedPackages and having it just as a plain list of all cmdObjects. + enabledPackages.forEach(function (pkg, idx, arr) { + if (pkg in config.gJSONData.commentPackages) { + installedPackages[pkg] = config.gJSONData.commentPackages[pkg]; + } + }); + } + + // Expand commentIdx properties into full comments + var cmdObj = {}; + for (var pkgKey in installedPackages) { + for (var cmdObjKey in installedPackages[pkgKey]) { + cmdObj = installedPackages[pkgKey][cmdObjKey]; + if ("commentIdx" in cmdObj) { + cmdObj.comment = config.gJSONData.commentStrings[cmdObj.commentIdx]; + delete cmdObj.commentIdx; + } + } + } + + if ("sentUpstreamString" in config.gJSONData.commentStrings) { + config.constantData.commentStrings = {}; + config.constantData.commentStrings.sentUpstreamString = + config.gJSONData.commentStrings["sentUpstreamString"]; + } + + var locURL = new urlMod.URL(locationLoginObj.location); + var passDomain = locURL.scheme + "://" + locURL.host; + var passwObj = getPassword(locationLoginObj.login, passDomain); + // In order to avoid sending whole password to the content script, + // we are sending just these two Booleans. + config.constantData.passwordState = { + passAvailable: (passwObj.password !== null), + withoutPass: passwObj.withoutPass + }; + + callback(new Message("CreateButtons", { + instPkgs: installedPackages, + constData: config.constantData, + config: config.configData, + kNodes: config.gJSONData.configData.killNodes + })); +}; + +exports.getClipboard = function getClipboard(cb) { + cb(clipboard.get()); +}; + +exports.getURL = function getURL(url, callback) { + Request({ + url: url, + onComplete: function(response) { + if (response.status == 200) { + callback(response.text); + } + } + }).get(); +}; + +exports.openStringInNewPanel = function openStringInNewPanel(inHTMLStr) { + openURLInNewPanel("data:text/html;charset=utf-8," + + inHTMLStr); +}; + +var openURLInNewPanel = exports.openURLInNewPanel = function openURLInNewPanel(url) { + var panel = panelMod.Panel({ + contentURL: url, + width: 640, + height: 640 + }); + panel.show(); +}; + +var openURLInNewTab = exports.openURLInNewTab = function openURLInNewTab(url) { + tabs.open({ + url: url, + inBackground: true, + onReady: function(t) { + t.activate(); + } + }); +}; + +exports.createUpstreamBug = function createUpstreamBug(urlStr, subject, comment) { + tabs.open({ + url: urlStr, + inBackground: true, + onReady: function (t) { + var otherElems = t.contentDocument.forms.namedItem("Create").elements; + // Summary + otherElems.namedItem("short_desc").value = subject; + // Comment + otherElems.namedItem("comment").value = collectComments(); + t.activate(); + } + }); +}; + +// Make a XML-RPC call ... most of the business logic should stay in the content script +exports.makeXMLRPCCall = function makeXMLRPCCall(url, login, method, params, callback) { + var urlObj = urlMod.URL(url); + var passwObj = getPassword(login, urlObj.schema + "://" + urlObj.host); + if (!passwObj.password) { + return null; // TODO this should happen, only when user presses Escape in password prompt + } + + var msg = new xrpc.XMLRPCMessage(method); + params.forEach(function (par) { + msg.addParameter(par); + }); + msg.addParameter(login); + msg.addParameter(passwObj.password); + + Request({ + url: url, + onComplete: function(response) { + if (response.status == 200) { + var resp = parseXMLfromString(response.text); + callback(resp.toXMLString()); + } + }, + content: msg.xml(), + contentType: "text/xml" + }).post(); +}; + +exports.initialize = function initialize(config, callback) { + var prefName = BTSPrefNS+"JSONURL"; + var urlStr = ""; + + if (preferences.isSet(prefName)) { + urlStr = preferences.get(prefName); + } else { + urlStr = JSONURLDefault; + preferences.set(prefName, JSONURLDefault); + } + + // Randomize URL to avoid caching + // TODO see https://fedorahosted.org/bugzilla-triage-scripts/ticket/21 + // for more thorough discussion and possible further improvement + urlStr += (urlStr.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(); + + Request({ + url: urlStr, + onComplete: function (response) { + if (response.status == 200) { + config.gJSONData = response.json; + + // Get additional tables + if ("downloadJSON" in config.gJSONData.configData) { + var URLsList = config.gJSONData.configData.downloadJSON; + var dwnldObj = ""; + URLsList.forEach(function (arr) { + var title = arr[0]; + var url = arr[1]; + Request({ + url: url, + onComplete: function(response) { + if (response.status == 200) { + config.constantData[title] = response.json; + } + } + }).get(); + }); + } + + // config.logger = new logger.Logger(JSON.parse( + // self.data.load("bugzillalabelAbbreviations.json"))); + + config.configData = {}; + config.configData.matches = config.gJSONData.configData.matches; + config.configData.skipMatches = config.configData.matches.map(function(x) { + return x.replace("show_bug.cgi.*","((process|post)_bug|attachment)\.cgi$"); + }); + + // config.objConstructor = {}; + // var bzType = config.gJSONData.configData.objectStyle; + // if (bzType === "RH") { + // config.objConstructor = require("rhbzpage").RHBugzillaPage; + // } else if (bzType === "MoFo") { + // } + // config.objConstructor = require("mozillabzpage").MozillaBugzilla; + + config.constantData = {}; + // TODO this is important and missing + if ("constantData" in config.gJSONData) { + config.constantData = config.gJSONData.constantData; + config.constantData.queryUpstreamBug = JSON.parse( + selfMod.data.load("queryUpstreamBug.json")); + config.constantData.XMLRPCData = JSON.parse( + selfMod.data.load("XMLRPCdata.json")); + config.constantData.bugzillaLabelNames = + JSON.parse(selfMod.data.load("bugzillalabelNames.json")); + config.constantData.newUpstreamBug = + JSON.parse(selfMod.data.load("newUpstreamBug.json")); + config.constantData.ProfessionalProducts = + JSON.parse(selfMod.data.load("professionalProducts.json")); + } + + if ("CCmaintainer" in config.constantData) { + config.configData.defBugzillaMaintainerArr = config.constantData.CCmaintainer; + } + + if ("suspiciousComponents" in config.gJSONData.configData) { + config.configData.suspiciousComponents = + config.gJSONData.configData.suspiciousComponents; + } + + if ("XorgLogAnalysis" in config.gJSONData.configData) { + config.configData.xorglogAnalysis = + config.gJSONData.configData.XorgLogAnalysis; + } + + if ("submitsLogging" in config.gJSONData.configData && + config.gJSONData.configData.submitsLogging) { + console.log("initialize : submitsLogin = " + + config.gJSONData.configData.submitsLogging); + logger.initialize(JSON.parse(selfMod.data.load( + "bugzillalabelAbbreviations.json"))); + } + } + callback(); + } + }).get(); +} diff --git a/lib/logger.js b/lib/logger.js index f64a6e9..94f7031 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -4,44 +4,52 @@ var urlMod = require("url"); var utilMod = require("util"); var fileMod = require("file"); -var Color = require("color").Color; var tabs = require("tabs"); var prompts = require("prompts"); var apiUtils = require("api-utils"); var xrpc = require("xmlrpc"); var myStorage = require("simple-storage"); +var libbz = require("libbugzilla"); -var Logger = exports.Logger = function Logger(abbsMap) { - this.EmptyLogsColor = new Color(0, 255, 0); - this.FullLogsColor = new Color(0, 40, 103); +var EmptyLogsColor = "rgb(0, 255, 0)"; +var FullLogsColor = "rgb(0, 40, 103)"; +var abbsMap = {}; + +exports.initialize = function initialize(aMap) { if (!myStorage.storage.logs) { myStorage.storage.logs = {}; } + abbsMap = aMap; +}; - this.abbsMap = abbsMap; +exports.addLogRecord = function addLogRecord(rec) { + if (myStorage.storage.logs[rec.key]) { + myStorage.storage.logs[rec.key].comment += "<br/>\n" + comment; + } else { + myStorage.storage.logs[rec.key] = rec; + } }; -Logger.prototype.size = function size() { +function storeSize() { var size = 0, key; for (key in myStorage.storage.logs) { size++; } return size; -}; +} -Logger.prototype.isEmpty = function isEmpty() { - return (this.size() === 0); -}; +function isEmpty() { + return (storeSize() === 0); +} -Logger.prototype.clearStore = function clearStore(clearLink) { +exports.clearTimeSheet = function clearTimeSheet() { myStorage.storage.logs = {}; - var size = this.size(); - clearLink.style.color = this.EmptyLogsColor; - clearLink.style.fontWeight = "normal"; + var size = storeSize(); }; -Logger.prototype.importOtherStore = function importOtherStore (filename, clearLink) { +exports.importTimeSheet = function importTimeSheet() { + var filename = prompts.promptFileOpenPicker(); if (fileMod.exists(filename)) { var otherTS = JSON.parse(fileMod.read(filename)); if (otherTS.logs) { @@ -54,57 +62,40 @@ Logger.prototype.importOtherStore = function importOtherStore (filename, clearLi } else { console.error("File " + filename + " doesn't exist!"); } - if (this.size() > 0) { - clearLink.style.color = this.FullLogsColor; - clearLink.style.fontWeight = "bolder"; - } }; -Logger.prototype.addLogRecord = function addLogRecord(page) { - var rec = {}; - rec.date = new Date(); - rec.url = page.doc.location.toString(); - rec.title = page.title; - var comment = prompts.prompt( - "Enter comments for this comment"); - if (comment && comment.length > 0) { - comment = comment.trim(); - rec.comment = comment; - var dateStr = utilMod.getISODate(rec.date); - var urlStr = urlMod.URL(rec.url).host; - var recKey = dateStr + "+" - + urlStr - + "+" + page.bugNo; - - if (myStorage.storage.logs[recKey]) { - myStorage.storage.logs[recKey].comment += "<br/>\n" + comment; - } else { - myStorage.storage.logs[recKey] = rec; - } - } - return comment; -}; - -Logger.prototype.getBugzillaAbbr = function(url) { +function getBugzillaAbbr(url) { // for https://bugzilla.redhat.com/show_bug.cgi?id=579123 get RH // for https://bugzilla.mozilla.org/show_bug.cgi?id=579123 get MoFo - var abbr = this.abbsMap[urlMod.URL(url).host]; - return abbr; -}; + return abbsMap[urlMod.URL(url).host]; +} + +exports.generateTimeSheet = function generateTimeSheet() { + var docHTML = timeSheetRecordsPrinter(myStorage.storage.logs); + libbz.openURLInNewTab("data:text/html;charset=utf-8," + docHTML); +}; -Logger.prototype.timeSheetRecordsPrinter = function(body, records) { - var that = this; +function timeSheetRecordsPrinter(records) { var commentBugRE = new RegExp("[bB]ug\\s+([0-9]+)","g"); // sort the records into temporary array var tmpArr = []; + var outStr = '<!DOCTYPE html PUBLIC "-//IETF//DTD HTML 2.0//EN">' + + "<html><head><title>Status report</title></head><body>" + + "<h1>TimeSheet</h1>\n"; - for ( var i in records) { + for (var i in records) { if (records.hasOwnProperty(i)) { tmpArr.push( [ i, records[i] ]); } } tmpArr.sort(function(a, b) { - return a[0] > b[0] ? 1 : -1; + if (a[0] > b[0]) { + return 1; + } else if (a[0] < b[0]) { + return -1; + } else { + return 0; + } }); var currentDay = ""; @@ -113,47 +104,24 @@ Logger.prototype.timeSheetRecordsPrinter = function(body, records) { var x = rec[1]; var dayStr = utilMod.getISODate(x.date); var host = urlMod.URL(x.url).host; - var BZName = that.getBugzillaAbbr(x.url); + var BZName = getBugzillaAbbr(x.url); var bugNo = utilMod.getBugNo(x.url); if (dayStr != currentDay) { currentDay = dayStr; - body.innerHTML += "<hr/><p><strong>" + currentDay - + "</strong></p>"; + outStr += "<hr/><p><strong>" + currentDay + + "</strong></p>\n"; } // replace "bug ####" with a hyperlink to the current bugzilla var comment = x.comment.replace(commentBugRE, "<a href='http://"+host+"/show_bug.cgi?id=$1'>$&</a>"); - body.innerHTML += "<p><em><a href='" + outStr += "<p><em><a href='" + x.url + "'>Bug " + BZName + "/" + bugNo + ": " + x.title + "</a>" - + " </em>\n<br/>" + comment + "</p>"; - }); - }; - -/** - * - */ -Logger.prototype.createBlankPage = function (ttl, bodyBuildCB) { - var title = ttl || "Yet another untitled page"; - var that = this; - - var logTab = tabs.open({ - url: "about:blank", - inBackground: true, - onOpen: function (tab) { - var otherDoc = tab.contentDocument; - otherDoc.title = title; - otherDoc.body.innerHTML = "<h1>" + title + "</h1>"; - bodyBuildCB.call(that, otherDoc.body); - tabs.activeTab = tab; - } + + " </em>\n<br/>" + comment + "</p>\n"; }); -}; - -Logger.prototype.generateTimeSheet = function(body) { - var doc = body.ownerDocument; - this.timeSheetRecordsPrinter(body, myStorage.storage.logs); -}; + outStr += "</body></html>"; + return outStr; +} diff --git a/lib/main.js b/lib/main.js index fbabd41..accaa0e 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,4 +1,3 @@ -/*jslint rhino: true, forin: true, onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: false, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false */ // Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php // @@ -11,17 +10,13 @@ // http://ehsanakhgari.org/blog/2010-05-31/my-experience-jetpack-sdk#comment-1253 // "use strict"; -var logger = require("logger"); var browser = require("tab-browser"); -var selfMod = require("self"); -var Request = require("request").Request; -var preferences = require("preferences-service"); -var BTSPrefNS = require("bzpage").BTSPrefNS; -// Use my JSON for now before it is fixed for general public -var JSONURLDefault = "https://fedorahosted.org/released"+ - "/bugzilla-triage-scripts/Config_data.json"; - -var config = {}; +var self = require("self"); +var pageMod = require("page-mod"); +var libbz = require("libbugzilla"); +var tabs = require("tabs"); +var logger = require("logger"); +var Message = require("util").Message; function isOurPage(window, matchingURLs) { var url = window.location.href; @@ -47,88 +42,103 @@ function skipThisPage(doc) { } } -function initialize(callback) { - var prefName = BTSPrefNS+"JSONURL"; - var urlStr = ""; - - if (preferences.isSet(prefName)) { - urlStr = preferences.get(prefName); - } else { - urlStr = JSONURLDefault; - preferences.set(prefName, JSONURLDefault); +var messageHandler = exports.messageHandler = function messageHandler(worker, msg) { + switch (msg.cmd) { + case "LogMessage": + console.log(msg.data); + break; + case "ExecCmd": + libbz.executeCommand(msg.data); + break; + case "AddLogRecord": + logger.addLogRecord(msg.data); + break; + case "GenerateTS": + logger.generateTimeSheet(); + break; + case "ClearTS": + logger.clearTimeSheet(); + break; + case "ImportTS": + logger.importTimeSheet(); + break; + case "GetInstalledPackages": + // send message with packages back + libbz.getInstalledPackages(msg.data, function (pkgsMsg) { + worker.postMessage(pkgsMsg); + }); + break; + case "GetClipboard": + libbz.getClipboard(function (clipboard) { + worker.postMessage(new Message(msg.data, clipboard)); + }); + break; + case "ChangeJSONURL": + libbz.changeJSONURL(); + break; + case "OpenURLinPanel": + libbz.openURLInNewPanel(msg.data); + break; + case "OpenURLinTab": + libbz.openURLInNewTab(msg.data); + break; + case "OpenStringInPanel": + libbz.openStringInNewPanel(msg.data); + break; + case "MakeXMLRPCall": + // url, login, method, params, callback + libbz.makeXMLRPCCall(msg.data.url, msg.data.login, msg.data.method, + msg.data.params, function(ret) { + worker.postMessage(new Message(msg.data.callRPC, ret)); + }); + break; + case "GetURL": + libbz.getURL(msg.data.url, function(stuff) { + worker.postMessage(new Message(msg.data.backMessage, stuff)); + }); + break; + case "OpenBugUpstream": + libbz.createUpstreamBug(msg.data.url, msg.data.subject, msg.data.comment); + break; + case "testReady": + // we ignore it here, interesting only in unit test + break; + default: + console.error(msg.toSource()); } +}; - // Randomize URL to avoid caching - // TODO see https://fedorahosted.org/bugzilla-triage-scripts/ticket/21 - // for more thorough discussion and possible further improvement - urlStr += (urlStr.match(/\?/) == null ? "?" : "&") + (new Date()).getTime(); - - Request({ - url: urlStr, - onComplete: function (response) { - if (response.status == 200) { - config.gJSONData = response.json; - - // Get additional tables - if ("downloadJSON" in config.gJSONData.configData) { - var URLsList = config.gJSONData.configData.downloadJSON; - var dwnldObj = ""; - URLsList.forEach(function (arr) { - var title = arr[0]; - var url = arr[1]; - Request({ - url: url, - onComplete: function(response) { - if (response.status == 200) { - config.gJSONData.constantData[title] = response.json; - } - } - }).get(); - }, this); - } - - config.logger = new logger.Logger(JSON.parse(selfMod.data.load("bugzillalabelAbbreviations.json"))); - - config.matches = config.gJSONData.configData.matches; - config.skipMatches = config.matches.map(function(x) { - return x.replace("show_bug.cgi.*","((process|post)_bug|attachment)\.cgi$"); - }); - - config.objConstructor = {}; - var bzType = config.gJSONData.configData.objectStyle; - if (bzType === "RH") { - config.objConstructor = require("rhbzpage").RHBugzillaPage; - } else if (bzType === "MoFo") { - config.objConstructor = require("mozillabzpage").MozillaBugzilla; - } +var contentScriptLibraries = { + "bugzilla.redhat.com": [ + self.data.url("lib/util.js"), + self.data.url("lib/color.js"), + self.data.url("lib/rhbzpage.js"), + self.data.url("lib/bzpage.js") + ] +}; - callback(config); - } +libbz.initialize(libbz.config, function () { + pageMod.PageMod({ + include: [ + "https://bugzilla.redhat.com/show_bug.cgi?id=*" + ], + contentScriptWhen: 'ready', + contentScriptFile: contentScriptLibraries["bugzilla.redhat.com"], + onAttach: function onAttach(worker, msg) { + worker.on('message', function (msg) { + messageHandler(worker, msg); + }); } - }).get(); -} + }); +}); -exports.main = function main(options, callbacks) { - initialize(function (config) { - browser.whenContentLoaded( - function(window) { - // is this good for anything? - if ("window" in window) { window = window.window; } +pageMod.PageMod({ + include: [ + "https://bugzilla.redhat.com/process_bug.cgi", + "https://bugzilla.redhat.com/attachment.cgi" + ], + contentScriptWhen: 'ready', + contentScriptFile: self.data.url("lib/skip-bug.js") +}); - if (isOurPage(window, config.matches)) { - try { - var curPage = new config.objConstructor(window, config); - } catch (ex) { - if (ex instanceof require("bzpage").NotLoggedinException) { - return ; // Bail out if the user is not logged in - } else { - throw ex; // rethrow the exception otherwise - } - } - } else if (isOurPage(window, config.skipMatches)) { - skipThisPage(window.document); - } - } - ); - }); -}; +// tabs.open("https://bugzilla.redhat.com/show_bug.cgi?id=679515"); diff --git a/lib/mozillabzpage.js b/lib/mozillabzpage.js deleted file mode 100644 index 31f6332..0000000 --- a/lib/mozillabzpage.js +++ /dev/null @@ -1,19 +0,0 @@ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -var utilMod = require("util"); -var apiUtils = require("api-utils"); -var BZPage = require("bzpage").BZPage; - -// ============================================================================ -// MozillaBugzilla object - -var MozillaBugzilla = function MozillaBugzilla (doc, config) { - BZPage.call(this, doc, config); -}; - -MozillaBugzilla.prototype = utilMod.heir(BZPage); -MozillaBugzilla.prototype.constructor = MozillaBugzilla; - -// exports.MozillaBugzilla = apiUtils.publicConstructor(MozillaBugzilla); -exports.MozillaBugzilla = MozillaBugzilla;
\ No newline at end of file diff --git a/lib/passwords.js b/lib/passwords.js index cc9c16b..af76f47 100644 --- a/lib/passwords.js +++ b/lib/passwords.js @@ -1,4 +1,3 @@ -/*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 */ // Released under the MIT/X11 license // http://www.opensource.org/licenses/mit-license.php "use strict"; diff --git a/lib/prompts.js b/lib/prompts.js index d4a5293..ed9b3e5 100644 --- a/lib/prompts.js +++ b/lib/prompts.js @@ -97,9 +97,10 @@ exports.promptYesNoCancel = function promptOKNoCancel(prompt) { * documentation is https://developer.mozilla.org/en/NsIFilePicker */ exports.promptFileOpenPicker = function promptFilePicker (win) { + var window = require("window-utils").activeWindow; var fp = Cc["@mozilla.org/filepicker;1"] .createInstance(Ci.nsIFilePicker); - fp.init(win, "JSON File Open", Ci.nsIFilePicker.modeOpen); + fp.init(window, "JSON File Open", Ci.nsIFilePicker.modeOpen); fp.appendFilter("JSON files", "*.json"); fp.appendFilters(Ci.nsIFilePicker.filterAll); fp.filterIndex = 0; diff --git a/lib/rhbzpage.js b/lib/rhbzpage.js deleted file mode 100644 index 10090a7..0000000 --- a/lib/rhbzpage.js +++ /dev/null @@ -1,1023 +0,0 @@ -/*jslint onevar: false, browser: true, evil: true, laxbreak: true, undef: true, nomen: true, eqeqeq: false, bitwise: true, maxerr: 1000, immed: false, white: false, plusplus: false, regexp: false, undef: false */ -// Released under the MIT/X11 license -// http://www.opensource.org/licenses/mit-license.php -"use strict"; -var util = require("util"); -var xrpc = require("xmlrpc"); -var apiUtils = require("api-utils"); -var self = require("self"); -var Color = require("color").Color; -var BZPage = require("bzpage").BZPage; -var Request = require("request").Request; -var url = require("url"); -var timer = require("timer"); -var selection = require("selection"); -var tabs = require("tabs"); -var NumberOfFrames = require("bzpage").NumberOfFrames; -var titleParsedAttachment = "Part of the thread where crash happened"; -// ==================================================================================== -// RHBugzillaPage object - -var RHBugzillaPage = function RHBugzillaPage(win, config) { - // inheritance ... call superobject's constructor - BZPage.call(this, win, config); - - // For identification of graphics card - this.manuChipStrs = [ [ "ATI Radeon", "ATI", "1002" ], - [ "ATI Mobility Radeon", "ATI", "1002" ], - [ "Intel Corporation", "INTEL", "8086" ], [ "NVIDIA", "NV", "10de" ] ]; - - // http://en.wikipedia.org/wiki/HSL_color_space - // when only the value of S is changed - // stupido!!! the string is value in hex for each color - this.RHColor = new Color(158, 41, 43); // RGB 158, 41, 43; HSL 359, 1, 39 - this.FedoraColor = new Color(0, 40, 103); // RGB 0, 40, 103; HSL 359, 1, 39 - this.RawhideColor = new Color(0, 119, 0); // or "green", or RGB 0, 119, 0, or - // HSL - // 120, 0, 23 - this.RHITColor = new Color(102, 0, 102); // RGB 102, 0, 102; HSL 300, 0, 20 - - this.RE = { - Comment: new RegExp("^\\s*#"), // unsused - BlankLine: new RegExp("^\\s*$"), // unused - // new line - // [ 65.631] (--) intel(0): Chipset: "845G" - Chipset: new RegExp("^\\s*\\[?[ 0-9.]*\\]?\\s*\\(--\\) "+ - "([A-Za-z]+)\\([0-9]?\\): Chipset: (.*)$"), - ATIgetID: new RegExp("^.*\\(ChipID = 0x([0-9a-fA-F]+)\\).*$"), - Abrt: new RegExp("^\\s*\\[abrt\\]"), - signalHandler: new RegExp("^\\s*#[0-9]*\\s*<signal handler called>"), - frameNo: new RegExp("^\\s*#([0-9]*)\\s"), - soughtLines: new RegExp("^\\s*(\\[[0-9 .]*\\])?\\s*(\\((EE|WW)\\)|.* [cC]hipsets?: )|\\s*Backtrace") - }; - // END OF CONSTANTS - - var that = this; - this.reqCounter = 0; - this.signaturesCounter = 0; - this.chipMagicInterestingLine = ""; - - this.product = this.doc.getElementById("product").value; - - this.maintCCAddr = null; - if (this.constantData.CCmaintainer) { - this.maintCCAddr = util.filterByRegexp(this.constantData.CCmaintainer, - this.getComponent()); - } - - var ITbutton = this.doc.getElementById("cf_issuetracker"); - this.its = ITbutton ? ITbutton.value.trim() : ""; - - if (!this.constantData.ProfessionalProducts) { - this.constantData.ProfessionalProducts = - JSON.parse(self.data.load("professionalProducts.json")); - } - - // getBadAttachments - this.XorgLogAttList = []; - this.XorgLogAttListIndex = 0; - this.attachments = this.getAttachments(); - this.markBadAttachments(this.attachments); - - this.parsedAttachments = this.attachments.filter(function (att) { - return (new RegExp(titleParsedAttachment).test(att[0])); - }); - - if (this.constantData.defaultAssignee) { - this.setDefaultAssignee(); - } - - // Dig out backtrace protection against double-firing? - this.btSnippet = ""; - - var parseAbrtBacktraces = config.gJSONData.configData.parseAbrtBacktraces; - if (parseAbrtBacktraces && this.RE.Abrt.test(this.title)) { - this.pasteBacktraceInComments(); - } - - // Find out Xorg.0.log attachment URL - this.XorgLogAttList = this.attachments.filter(function (value, index, array) { - // Xorg.0.log must be text, otherwise we cannot parse it - return (/[xX].*log/.test(value[0]) && /text/.test(value[2])); - }); - this.addCheckXorgLogLink(); - - // TODO Get compiz bugs as well - if (this.constantData.PCI_ID_Array && - (this.XorgLogAttList[0]) && - (this.maintCCAddr === "xgl-maint@redhat.com")) { - // Add find chip magic button - var whiteboard_string = this.doc.getElementById("status_whiteboard").value; - if (!/card_/.test(whiteboard_string)) { - this.fillInChipMagic(); - } - } - - // Take care of signature for Fedora bugzappers - if (config.gJSONData.configData.signature.length > 0) { - var signatureFedoraString = config.gJSONData.configData.signature; - this.doc.forms.namedItem("changeform").addEventListener("submit", - function(aEvt) { - if (that.signaturesCounter < 1) { - that.addStuffToTextBox("comment", signatureFedoraString); - that.signaturesCounter += 1; - } - }, false); - } - - this.setBranding(); - - // set default assignee on change of the component - var compElement = this.doc.getElementById("component"); - if (compElement && (compElement.options)) { - this.doc.getElementById("component").addEventListener("change", - function() { - that.changeAssignee("default"); - }, false); - } - -}; // END OF RHBugzillaPage CONSTRUCTOR - -RHBugzillaPage.prototype.toString = function toString () { - return ("[Object RHBugzillaPage]"); -}; - -RHBugzillaPage.prototype = util.heir(BZPage); -RHBugzillaPage.prototype.constructor = RHBugzillaPage; - -/** - * Find default assignee based on the current component - * - * @return String what would be a default assignee if - * we haven't set it up. - */ -RHBugzillaPage.prototype.getDefaultAssignee = function() { - return util.filterByRegexp(this.constantData.defaultAssignee, - this.getComponent()).toLowerCase(); -}; - -/** - * Set default assignee - * - * @return none - * sets this.defaultAssignee property according to defaultAssignee list - */ -RHBugzillaPage.prototype.setDefaultAssignee = function() { - this.defaultAssignee = this.getDefaultAssignee(); - var defAss = this.defaultAssignee; - - // Add setting default assignee - if ((defAss.length > 0) && (defAss !== this.getOwner())) { - this.constantData.defaultAssigneeTrigger = true; - this.createNewButton("bz_assignee_edit_container",true,"rh-common","setDefaultAssignee"); - } -}; - -/** - * Auxiliary function to compute more complicated resolution - */ -RHBugzillaPage.prototype.closeSomeRelease = function() { - // for RAWHIDE close as RAWHIDE, - // if active selection -> CURRENTRELEASE - // and put the release version to - // "Fixed in Version" textbox - // otherwise -> NEXTRELEASE - this.selectOption("bug_status", "CLOSED"); - var text = ""; - var resolution = ""; - - if (selection.text) { - text = selection.text.trim(); - } - if (text.length > 0) { - resolution = "CURRENTRELEASE"; - this.doc.getElementById("cf_fixed_in").value = text; - } else if (this.doc.getElementById("version").value === "rawhide") { - resolution = "RAWHIDE"; - } else { - resolution = "NEXTRELEASE"; - } - this.centralCommandDispatch("resolution", resolution); -}; - -/** - * Additional commands specific for this subclass, overriding superclass one. - */ -RHBugzillaPage.prototype.centralCommandDispatch = function(cmdLabel, cmdParams) { - console.log("cmdLabel = " + cmdLabel + ", cmdParams = " + cmdParams); - switch (cmdLabel) { - // Set up our own commands - case "closeUpstream": - this.addClosingUpstream(); - break; - case "computeResolution": - this.closeSomeRelease(); - break; - case "queryStringUpstreamBugzilla": - this.queryUpstream(); - break; - case "sendBugUpstream": - this.sendBugUpstream(); - break; - case "markTriaged": - this.markBugTriaged(); - break; - case "chipMagic": - var splitArr = cmdParams.split("\t"); - this.fillInWhiteBoard(splitArr[0], splitArr[1]); - break; - // If we don't have it here, call superclass method - default: - BZPage.prototype.centralCommandDispatch.call(this, cmdLabel, cmdParams); - break; - } -}; - -/** - * - * This has to stay in RHBugzillaPage because upstream doesn't have addAttachment - * XML-RPC call yet. - */ -RHBugzillaPage.prototype.addAttachment = function addAttachment(data, callback, param) { - var msg = new xrpc.XMLRPCMessage("bugzilla.addAttachment"); - var that = this; - - msg.addParameter(this.bugNo); - msg.addParameter({ - description: titleParsedAttachment, - filename: "parsed-backtrace.txt", - contenttype: "text/plain", - data: this.win.btoa(data), - nomail: true - }); - msg.addParameter(this.login); - msg.addParameter(this.password); - - Request({ - url: this.constantData.XMLRPCData[this.hostname].url, - onComplete: function(response) { - if (response.status == 200) { - var resp = util.parseXMLfromString(response.text); - var newAttachID = parseInt(resp.params.param.value.array.data.value.int, 10); - console.log("attachID = " + newAttachID); - callback.call(that, param, newAttachID, data.length); - } - }, - content: msg.xml(), - contentType: "text/xml" - }).post(); - this.reqCounter++; -}; - -/* === Bugzilla functions === */ -/** - * - */ -RHBugzillaPage.prototype.pasteBacktraceInComments = function() { - var that = this; - - /* - Let's comment it out, and we'll see if anything breaks. - TODO This paragraph looks suspicous ... what is it? - Does it belong to this function? - var notedLabel = this.doc.querySelector("label[for='newcc']"); - while (notedLabel.firstChild) { - var node = notedLabel.removeChild(notedLabel.firstChild); - notedLabel.parentNode.insertBefore(node, notedLabel); - } - notedLabel.parentNode.removeChild(notedLabel); - */ - - var abrtQueryURL = "https://bugzilla.redhat.com/buglist.cgi?" + - "cmdtype=dorem&remaction=run&namedcmd=all%20NEW%20abrt%20crashes&"+ - "sharer_id=74116"; - - var mainTitle = this.doc - .getElementsByClassName("bz_alias_short_desc_container")[0]; - - var abrtButton = this.doc.createElement("a"); - abrtButton.setAttribute("accesskey", "a"); - abrtButton.setAttribute("href", abrtQueryURL); - abrtButton.textContent = "Abrt bugs"; - mainTitle.appendChild(abrtButton); - - if (this.idContainsWord("cf_devel_whiteboard", 'btparsed')) { - this.addStuffToTextBox('status_whiteboard', 'btparsed'); - } - - if (!(this.isTriaged() || this.idContainsWord("status_whiteboard", - 'btparsed') || (this.parsedAttachments.length > 0))) { - var btAttachments = this.attachments - .filter(function(att, idx, arr) { - return (/File: backtrace/.test(att[0])); - }); - // TODO we need to go through all backtrace attachments, but - // just the first one will do for now, we would need to do async - // parsing - btAttachments.forEach(function(x) { - var attURL = "https://bugzilla.redhat.com/attachment.cgi?id=" - + x[1]; - if ((!this.btSnippet) && - (!this.idContainsWord("status_whiteboard", 'btparsed'))) { - var that = this; - Request({ - url: attURL, - onComplete: function(response) { - if (response.status == 200) { - that.btSnippet = that.parseBacktrace(response.text); - if (that.btSnippet) { - that.addCheckShowLink.call(that,x,that.btSnippet); - } - } - } - }).get(); - } - }, this); - } - // Add "show BT" links - if (this.parsedAttachments.length > 0) { - this.parsedAttachments.forEach(function (att) { - that.addShowParsedBTLink(att); - }, that); - } -}; - -/** - * Open new window with the content of the attachment. - * - * @param id Number of the attachment id - * @return none - */ -RHBugzillaPage.prototype.showAttachment = function showAttachment(id) { - var that = this; - Request({ - url: "https://" + that.hostname + "/attachment.cgi?id=" + id, - onComplete: function (response) { - if (response.status == 200) { - var infoWin = that.win.open("", "Check att. " + id, - "width=640,height=640,status=no,location=no,"+ - "titlebar=no,scrollbars=yes,resizable=yes"+ - "alwaysRaised=yes"); - var doc = infoWin.document; - doc.body.innerHTML = "<pre id='textPre'>"+ - response.text + "</pre>"; - } - } - }).get(); -}; - -/** - * add a link opening a window with the parsed backtrace - * - * @param att Attachment object - */ -RHBugzillaPage.prototype.addShowParsedBTLink = function addShowParsedBTLink(att) { - var elem = att[4].querySelector("td:last-of-type"); - this.createDeadLink("showParsedBacktraceWindow-" + att[1], "showBT", - elem, this.showAttachment, att[1], true); -}; - -/** - * Unfinished ... see above - */ -RHBugzillaPage.prototype.addNewAttachmentRow = function addNewAttachmentRow(origAtt, - newAttId, newAttSize) { - var that = this; - var oldAddBTLink = this.doc.getElementById("attachBacktraceActivator"); - oldAddBTLink.parentNode.removeChild(oldAddBTLink); - var newTRElem = origAtt[4].cloneNode(true); - - // fix number of the attachment - Array.forEach(newTRElem.getElementsByTagName("a"), function (aEl) { - aEl.setAttribute("href", - aEl.getAttribute("href").replace(origAtt[1], newAttId)); - }); - - var aElements = newTRElem.getElementsByTagName("a"); - aElements[0].setAttribute("name","parsed-backtrace.txt"); - aElements[0].getElementsByTagName("b")[0].textContent = titleParsedAttachment; - - var sizeSpan = newTRElem.getElementsByClassName("bz_attach_extra_info")[0]; - sizeSpan.textContent = "(" + (newAttSize / 1024).toFixed(2) + " KB, text/plain)"; - - // aElements[1].textContent = new Date().toString(); TODO we should add eventually, but not pressing - - var vcardSpan = newTRElem.getElementsByClassName("vcard")[0]; - if (vcardSpan !== undefined) { - var vcardSpanClassList = vcardSpan.classList; - if (/@redhat\.com/.test(this.login) && !vcardSpanClassList.contains("redhat_user")) { - vcardSpanClassList.add("redhat_user"); - } - var vcardAElem = vcardSpan.getElementsByTagName("a")[0]; - vcardAElem.setAttribute("title", this.login); - vcardAElem.setAttribute("href", "mailto:" + this.login); - vcardAElem.className = "email"; - vcardAElem.innerHTML="<span class='fn'>" + this.login + "</span>"; - } - - var elem = newTRElem.querySelector("td:last-of-type"); - this.createDeadLink("showBacktrace", "show BT", elem, - this.showAttachment, newAttId, false); - - origAtt[4].parentNode.insertBefore(newTRElem, origAtt[4].nextSibling); -}; - -/** - * Add a link to create a new attachment with a parsed backtrace - * - * @param oldAtt Object with an attachment row - * @param snippet String with parsed backtrace - * @return none - */ -RHBugzillaPage.prototype.addCheckShowLink = function addCheckShowLink(oldAtt, snippet) { - var that = this; - var elem = oldAtt[4].querySelector("td:last-of-type"); - this.createDeadLink("attachBacktraceActivator", "add parsed BT", elem, function(x) { - // pass function and parameters as two separate parameters, the function to be called from - // addAttachment - that.addAttachment.call(that, snippet, this.addNewAttachmentRow, oldAtt); - }, [], true); -}; - -/** - * Make it sailent that the some attachments with bad MIME type are present - * - * @param atts Array of attachments subarrays - * @return none - */ -RHBugzillaPage.prototype.markBadAttachments = function markBadAttachments(atts) { - var that = this; - var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; - if (!this.password) { - return ; // User didn't provide password, so whole MIME fixing business - // should be switched off. - } - - var badAttachments = atts.filter(function(att, idx, arr) { - return (util.isInList(att[2], badMIMEArray)); - }); - - if (badAttachments.length > 0) { - var titleElement = this.doc - .getElementsByClassName("bz_alias_short_desc_container")[0]; - titleElement.style.backgroundColor = "olive"; - - this.createDeadLink("fixAllButton", "Fix all", titleElement, function() { - Array.forEach(badAttachments, function(x) { - this.fixAttachById(x[1]); - }, that); - }, [], false, null, "f"); - badAttachments.forEach(function(x, i, a) { - this.addTextLink(x); - }, this); - } -}; - -/** - * Is this bug a RHEL bug? - * - * @return Boolean true if it is a RHEL bug - */ -RHBugzillaPage.prototype.isEnterprise = function() { - var prod = this.product; - var result = this.constantData.ProfessionalProducts.some(function(elem,idx,arr) { - return new RegExp(elem).test(prod); - }); - return result; -}; - -/** - * Find out whether the bug is needed an attention of bugZappers - * - * @return Boolean whether the bug has been triaged or not - */ -RHBugzillaPage.prototype.isTriaged = function() { - return this.hasKeyword("Triaged"); -}; - -/** - * Set branding colours to easily distinguish between Fedora and RHEL bugs - * - * @param brand String with product of the current bug - * @param version String with the version of the bug - * @param its String with the IsueTracker numbers - * @return none - */ -RHBugzillaPage.prototype.setBranding = function() { - var brandColor = {}; - var TriagedColor = {}; - - if (this.isEnterprise()) { - if (this.its && (this.its.length > 0)) { - brandColor = this.RHITColor; - } else { - brandColor = this.RHColor; - } - } else if (new RegExp("Fedora").test(this.product)) { - if (this.doc.getElementById("version").value === "rawhide") { - brandColor = this.RawhideColor; - } else { - brandColor = this.FedoraColor; - } - } - - // Comment each of the following lines to get only partial branding - this.doc.getElementsByTagName("body")[0].style.background = brandColor - .toString() - + " none"; - this.doc.getElementById("titles").style.background = brandColor.toString() - + " none"; - - // Remove "Bug" from the title of the bug page, so we have more space with - // plenty of tabs - var titleElem = this.doc.getElementsByTagName("title")[0]; - - titleElem.textContent = titleElem.textContent.slice(4); - var bodyTitleParent = this.doc.getElementById("summary_alias_container").parentNode; - var bodyTitleElem = bodyTitleParent.getElementsByTagName("b")[0]; - bodyTitleElem.textContent = bodyTitleElem.textContent.slice(4); - - // Make background-color of the body of bug salmon pink - // for security bugs. - if (this.hasKeyword("Security")) { - this.doc.getElementById("bugzilla-body").style.background = this.SalmonPink - .toString() + ' none'; - } - - // Make it visible whether the bug has been triaged - if (this.isTriaged()) { - this.doc.getElementById("bz_field_status").style.background = brandColor - .lightColor().toString() - + " none"; - } - - // we should make visible whether maintCCAddr is in CCList - if (util.isInList(this.maintCCAddr, this.CCList)) { - var ccEditBoxElem = this.doc.getElementById("cc_edit_area_showhide"); - ccEditBoxElem.style.color = "navy"; - ccEditBoxElem.style.fontWeight = "bolder"; - ccEditBoxElem.style.textDecoration = "underline"; - } - - // mark suspicious components - var compElems; - if (this.suspiciousComponents - && util.isInList(this.getComponent(), this.suspiciousComponents) - && (compElems = this.doc - .getElementById("bz_component_edit_container"))) { - compElems.style.background = "red none"; - } -}; - -/** - * Search simple query in the upstream bugzilla appropriate for the component - * - * @return none - */ -RHBugzillaPage.prototype.queryUpstream = function() { - var text = this.getSelectionOrClipboard(); - if (text) { - var queryUpstreamBugsURLArray = this.constantData.queryUpstreamBug; - var searchData = util.filterByRegexp(queryUpstreamBugsURLArray, this.getComponent()); - var urlBase = searchData.url; - text = searchData.searchBy+":"+searchData.fillIn+" "+text.trim(); - if (searchData.fillIn == "$$$") { - text = text.replace("$$$", this.getComponent()); - } - text = encodeURIComponent(text).replace("%20","+"); - tabs.open({ - url: urlBase + text, - inBackground: true, - onOpen: function (tab) { - tabs.activeTab = tab; - } - }); - } -}; - -/** - * Open a tab in the upstream bugzilla to create a new bug - * - * @return none - */ -RHBugzillaPage.prototype.sendBugUpstream = function() { - var that = this; - var urlStr = util.filterByRegexp(JSON.parse(self.data.load("newUpstreamBug.json")), this - .getComponent()); - - tabs.open({ - url: urlStr, - inBackground: true, - onOpen: function (tab) { - var otherDoc = tab.contentDocument; - var otherElems = otherDoc.forms.namedItem("Create").elements; - otherElems.namedItem("short_desc").value = that.doc - .getElementById("short_desc_nonedit_display").textContent - .trim(); - otherElems.namedItem("comment").value = that.collectComments(); - tabs.activeTab = tab; - } - }); -}; - -/** - * Add a link opening selected lines of Xorg.0.log - * - * @return none - */ -RHBugzillaPage.prototype.addCheckXorgLogLink = function addCheckXorgLogLink() { - var that = this; - if (this.xorglogAnalysis) { - this.XorgLogAttList.forEach(function (row) { - var elemS = row[4].getElementsByTagName("td"); - var elem = elemS[elemS.length - 1]; - that.createDeadLink("xorgLogAnalyzeLink", "check", elem, - that.analyzeXorgLog, row[1], "br"); - }); - } -}; - -/** - * Given line to be parsed, find out which chipset it is and fill in the - * whiteboard - * - * @param iLine String with the whole unparsed "interesting line" - * @param driverStr String with the driver name - * @return None - */ -RHBugzillaPage.prototype.fillInWhiteBoard = function(iLine, driverStr) { - var that = this; - - function groupIDs(manStr, cardStrID) { - var outStr = util.filterByRegexp(that.constantData.chipIDsGroupings, - manStr + "," + cardStrID); - if (outStr.length === 0) { - outStr = "UNGROUPED_" + manStr + "/" + cardStrID; - } - return outStr; - } - - /** - * Given PCI IDs for manufacturer and card ID return chipset string - * - * @param manufacturerNo String with manufacturer PCI ID - * @param cardNo String with card PCI ID - * @return Array with chip string and optinoal variants - */ - function checkChipStringFromID(manufacturerNo, cardNo) { - var soughtID = (manufacturerNo + "," + cardNo).toUpperCase(); - var outList = that.constantData.PCI_ID_Array[soughtID]; - if (outList) { - return outList; - } else { - return ""; - } - } - - var outStr = ""; - var cardIDStr = ""; - var cardIDArr = []; - - chipSwitchboard: if (driverStr === "RADEON") { - var cardID = iLine.replace(this.RE.ATIgetID, "$1"); - cardIDArr = checkChipStringFromID("1002", cardID); - if (cardIDArr.length > 0) { - cardIDStr = cardIDArr[0]; - if (cardIDArr[1]) { - optionStr = cardIDArr[1]; - outStr = groupIDs(driverStr, cardIDStr) + "/" + optionStr; - } else { - outStr = groupIDs(driverStr, cardIDStr); - optionStr = ""; - } - } else { - outStr = "**** FULLSTRING: " + iLine; - } - } else { - // Intel Corporation, NVIDIA - cardIDArr = this.manuChipStrs.filter(function(el, ind, arr) { - return new RegExp(el[0], "i").test(iLine); - }); - if (cardIDArr && (cardIDArr.length > 0)) { - cardIDArr = cardIDArr[0]; - } else { - outStr = iLine; - break chipSwitchboard; - } - // cardIDArr [0] = RE, [1] = ("RADEON","INTEL","NOUVEAU"), [2] = manu - // PCIID - iLine = iLine.replace(new RegExp(cardIDArr[0], "i")).trim(); - // nVidia developers opted-out from grouping - if (driverStr === "INTEL") { - outStr = groupIDs(cardIDArr[1], iLine); - } else { - outStr = iLine; - } - } - this.addStuffToTextBox("status_whiteboard", ("card_" + outStr).trim()); - this.doc.getElementById("chipMagic_btn").style.display = "none"; -}; - -/** - * Get attached Xorg.0.log, parse it and find the value of chip. Does not fill - * the whiteboard itself, just adds button to do so,paramList so that slow - * XMLHttpRequest is done in advance. - * - * @return None - */ -RHBugzillaPage.prototype.fillInChipMagic = function fillInChipMagic() { - var that = this; - var XorgLogURL = ""; - var XorgLogAttID = ""; - var XorgLogFound = false; - var attURL = "", interestingLine = ""; - var interestingArray = []; - - if (this.XorgLogAttList.length === 0) { - return; - } - - XorgLogAttID = this.XorgLogAttList[this.XorgLogAttListIndex][1]; - attURL = "https://bugzilla.redhat.com/attachment.cgi?id="+XorgLogAttID; - - // parse Xorg.0.log - Request({ - url: attURL, - onComplete: function (response) { - if (response.status == 200) { - var interestingLineArr = response.text.split("\n"). - filter(function (v,i,a) { - return that.RE.Chipset.test(v); - }); - if (interestingLineArr.length >0) { - // TODO we are parsing only the first found line; is it alright? - interestingArray = that.RE.Chipset.exec(interestingLineArr[0]); - interestingLine = interestingArray[2]. - replace(/[\s"]+/g," ").trim(); - // Persuade createNewButton to have mercy and to actually add - // non-default button - that.constantData.chipMagicTrigger = true; - that.packages["rh-xorg"].chipMagic.chipMagic = interestingLine+"\t"+interestingArray[1] - .toUpperCase(); - that.createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); - } - } - } - }).get(); - this.XorgLogAttListIndex++; -}; - -RHBugzillaPage.prototype.analyzeXorgLog = function analyzeXorgLog(attachID) { - var infoWin = this.win.open("", "Check att. " + attachID, - "width=640,height=640,status=no,location=no"); - var doc = infoWin.document; - doc.body.innerHTML = "<pre id='textPre'></pre>"; - // TODO var oldCursor = doc.body.style.cursor; - // doc.body.style.cursor = "wait"; - var preElem = doc.getElementById("textPre"); - - var attURL = "https://bugzilla.redhat.com/attachment.cgi?id=" + attachID; - var that = this; - Request({ - url: attURL, - onComplete: function(response) { - if (response.status == 200) { - var results = response.text.split("\n"). - filter(function(line) { - return (that.RE.soughtLines.test(line)); - }); - results.sort(); - results = util.removeDuplicates(results); - // Remove headers - if (results.length >= 1) { - results.splice(0, 1); - } - if (results.length > 0) { - results.forEach(function(l) { - preElem.innerHTML += l + "\n"; - }); - // Add a summary - preElem.innerHTML += "----------\n" + - results.length + " interesting lines found."; - } else { - preElem.innerHTML += "No matching lines found!"; - } - } - // doc.body.style.cursor = oldCursor; - } - }).get(); -}; - -/** - * Return string with the ID for the external_id SELECT for external bugzilla - * - * @param URLhostname String hostname of the external bugzilla - * @return String with the string for the external_id SELECT - */ -RHBugzillaPage.prototype.getBugzillaName = function getBugzillaName(URLhostname) { - var bugzillaID = ""; - var bzLabelNames = JSON.parse(self.data.load("bugzillalabelNames.json")); - if (bzLabelNames[URLhostname]) { - bugzillaID = bzLabelNames[URLhostname]; - } else { - bugzillaID = ""; - } - return bugzillaID; -}; - -/** - * Callback function for the XMLRPC request - * - * @param ret Object with xmlhttprequest response with attributes: - * + status -- int return code - * + statusText - * + responseHeaders - * + responseText - */ -RHBugzillaPage.prototype.XMLRPCcallback = function XMLRPCcallback() { - var that = this; - this.reqCounter--; - if (this.reqCounter <= 0) { - timer.setTimeout(function () { - that.win.location.reload(true); - }, 1000); - } -}; - -/** - * The worker function -- call XMLRPC to fix MIME type of the particular - * attachment - * - * @param id Integer with the attachment id to be fixed - * @param type String with the new MIME type, optional defaults to "text/plain" - * @param email Boolean whether email should be sent to appropriate person; - * option, defaults to false - * - * updateAttachMimeType($data_ref, $username, $password) - * - * Update the attachment mime type of an attachment. The first argument is a - * data hash containing information on the new MIME type and the attachment id - * that you want to act on. - * - * $data_ref = { "attach_id" => "<Attachment ID>", # Attachment ID to perform - * MIME type change on. "mime_type" => "<New MIME Type Value>", # Legal MIME - * type value that you want to change the attachment to. "nomail" => 0, # - * OPTIONAL Flag that is either 1 or 0 if you want email to be sent or not for - * this change }; - * - */ -RHBugzillaPage.prototype.fixAttachById = function fixAttachById(id, type, email) { - if (type === undefined) { - type = "text/plain"; - } - if (email === undefined) { - email = false; - } - - var that = this; - var msg = new xrpc.XMLRPCMessage("bugzilla.updateAttachMimeType"); - msg.addParameter( { - 'attach_id' : id, - 'mime_type' : type, - 'nomail' : !email - }); - msg.addParameter(this.login); - msg.addParameter(this.password); - - // https://bugzilla.redhat.com/\ - // docs/en/html/api/extensions/compat_xmlrpc/code/webservice.html - // test on https://bugzilla.redhat.com/show_bug.cgi?id=485145 - Request({ - url: this.constantData.XMLRPCData[this.hostname].url, - onComplete: function(response) { - if (response.status == 200) { - that.XMLRPCcallback.call(that); - } - }, - content: msg.xml(), - contentType: "text/xml" - }).post(); - this.reqCounter++; -}; - -/** - * Add a link to the bad attachment for fixing it. - * - * @param - * <TR> DOM jQuery element with a bad attachment - * @return none - */ -RHBugzillaPage.prototype.addTextLink = function addTextLink(row) { - var elemS = row[4].getElementsByTagName("td"); - var elem = elemS[elemS.length - 1]; - this.createDeadLink("addFix2TextLink", "text", elem, - this.fixAttachById, row[1], "br"); -}; - -/** - * Add information about the upstream bug upstream, and closing it. - * - * @param evt Event which called this handler - * @return none - */ -RHBugzillaPage.prototype.addClosingUpstream = function() { - var refs = this.doc.getElementById("external_bugs_table") - .getElementsByTagName("tr"); - - // that's a bad id, if there is a one. :) - var inputBox = this.doc.getElementById("inputbox"); - var externalBugID = 0; - var wholeURL = ""; - - // Fix missing ID on the external_id SELECT - this.doc.getElementsByName("external_id")[0].setAttribute("id", - "external_id"); - - if (inputBox.value.match(/^http.*/)) { - wholeURL= new url.URL(inputBox.value); - externalBugID = util.getBugNo(wholeURL); - if (externalBugID) { - inputBox.value = externalBugID; - } - // get bugzillaName and set the label - var bugzillaName = this.getBugzillaName(wholeURL.host); - this.selectOptionByLabel("external_id", bugzillaName); - } else if (!isNaN(inputBox.value)) { - externalBugID = parseInt(inputBox.value, 10); - var bugzillaHostname = this.doc.getElementById("external_id").value; - wholeURL = bugzillaHostname+"show_bug.cgi?id="+externalBugID; - } else { - // no inputBox.value -- maybe there is an external bug from - // the previous commit? - } - - // It is not good to close bug as UPSTREAM, if there is no reference - // to the upstream bug. - if ((externalBugID > 0) || (refs.length > 2)) { - var msgStr = this.commentStrings.sentUpstreamString; - msgStr = msgStr.replace("§§§", wholeURL); - this.centralCommandDispatch("comment",msgStr); - this.centralCommandDispatch("status", "CLOSED"); - this.centralCommandDispatch("resolution", "UPSTREAM"); - } else { - console.log("No external bug specified among the External References!"); - } -}; - -RHBugzillaPage.prototype.markBugTriaged = function() { - // Now we lie completely, we just set keyword Triaged, - // this is not just plain ASSIGNED, but - // modified according to - // https://fedoraproject.org/wiki/BugZappers/Meetings/Minutes-2009-Oct-27 - // and - // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ - // /fedora-meeting.2009-11-24-15.11.log.html - // and - // http://meetbot.fedoraproject.org/fedora-meeting/2009-11-24\ - // /fedora-meeting.2009-11-24-15.11.log.html - // for F13 and later, ASSIGNED is "add Triaged keyword" (as well) - // for <F13 it is "add both" (ASSIGNED status and Triaged keyword) - var ver = this.doc.getElementById("version").value; - if ((!this.isEnterprise()) && (ver <= 12)) { - this.selectOption("bug_status", "ASSIGNED"); - } - this.addStuffToTextBox("keywords","Triaged"); -}; - -/** - * - */ -RHBugzillaPage.prototype.parseBacktrace = function(ret) { - var splitArray = ret.split("\n"); - var i = 0, ii = splitArray.length; - var outStr = "", curLine = "", numStr = ""; - var lineCounter = 0, endLineNo = 0; - - while (i < ii) { - if (this.RE.signalHandler.test(splitArray[i])) { - break; - } - i++; - } - - if (i < ii) { - lineCounter = parseInt(this.RE.frameNo.exec(splitArray[i])[1], 10); - endLineNo = lineCounter + NumberOfFrames; - curLine = splitArray[i]; - while ((lineCounter < endLineNo) && (curLine.trim().length > 0) - && (i < ii)) { - outStr += curLine + '\n'; - numStr = this.RE.frameNo.exec(curLine); - if (numStr) { - lineCounter = parseInt(numStr[1], 10); - } - i++; - curLine = splitArray[i]; - } - return outStr; - } - return ""; -}; - -// exports.RHBugzillaPage = apiUtils.publicConstructor(RHBugzillaPage); -exports.RHBugzillaPage = RHBugzillaPage; diff --git a/lib/util.js b/lib/util.js index 4612b66..089e31c 100644 --- a/lib/util.js +++ b/lib/util.js @@ -9,36 +9,9 @@ var Ci = require("chrome").Ci; var urlMod = require("url"); /** - * Function for the management of the prototypal inheritace - * David Flanagan, Javascript: The Definitve Guide, - * IV. edition, O'Reilly, 2006, p. 168 - * - * @param superobject - * @return new object, it needs new prototype.constructor - * - * <pre> - * function Father(x) { - * this.family = x; - * } - * - * function Son(x,w) { - * Father.call(this,x); - * this.wife = w; - * } - * Son.prototype = heir(Father); - * Son.prototype.constructor = Son; - * </pre> - */ -exports.heir = function heir(p) { - function F() {}; - F.prototype = p.prototype; - return new F(); -}; - -/** * get parameters of URL as an object (name, value) */ -var getParamsFromURL = exports.getParamsFromURL = function getParamsFromURL (url, base) { +function getParamsFromURL (url, base) { if (!url || (url.toString().length === 0)) { throw new Error("Missing URL value!"); } @@ -50,7 +23,7 @@ var getParamsFromURL = exports.getParamsFromURL = function getParamsFromURL (url var paramsArr = url.path.split("?"); if (paramsArr.length === 1) { return {}; - } + } // get convert URL parameters to an Object var params = {}, s = []; @@ -59,21 +32,7 @@ var getParamsFromURL = exports.getParamsFromURL = function getParamsFromURL (url params[s[0]] = s[1]; }); return params; -}; - -/** - * parse XML object out of string working around various bugs in Gecko implementation - * see https://developer.mozilla.org/en/E4X for more information - * - * @param inStr String with unparsed XML string - * @return XML object - */ -exports.parseXMLfromString = function parseXMLfromString (inStuff) { - // if (typeof inStuff !== 'string') In future we should recognize this.response - // and get just .text property out of it. TODO - var respStr = inStuff.replace(/^<\?xml\s+version\s*=\s*(["'])[^\1]+\1[^?]*\?>/, ""); // bug 336551 - return new XML(respStr); -}; +} /** * Get a bug no from URL ... fails with aliases @@ -106,139 +65,13 @@ exports.getISODate = function getISODate(dateStr) { }; /** - * Check whether an item is member of the list. Idea is just to make long if - * commands slightly more readable. - * - * @param mbr string to be searched in the list - * @param list list - * @return position of the string in the list, or -1 if none found. - */ -var isInList = exports.isInList = function isInList(mbr, list) { - if (!list) { - return false; - } - return (list.indexOf(mbr) !== -1); -}; - -/** - * Make sure value returned is Array - * - * @param Array/String - * @return Array - * - * If something else than Array or String is passed to the function - * the result will be untouched actual argument of the call. - */ -var valToArray = exports.valToArray = function valToArray(val) { - var isArr = val && - val.constructor && - val.constructor.name === "Array"; - return isArr ? val : [val]; -}; - -/** - * Merges two comma separated string as a list and returns new string - * - * @param str String with old values - * @param value String/Array with other values - * @return String with merged lists - */ -exports.addCSVValue = function addCSVValue(str, value) { - var parts = (str.trim().length > 0 ? str.split(/[,\s]+/) : []); - if (!value) { - return str; - } - if (!isInList(value, parts)) { - var newValue = valToArray(value); - parts = parts.concat(newValue); - } - // this is necessary to get comma-space separated string even when - // value is an array already - parts = parts.join(",").split(","); - return parts.join(", "); -}; - -/** - * Treats comma separated string as a list and removes one item from it - * - * @param str String treated as a list - * @param value String with the value to be removed from str - * @return String with the resulting list comma separated - */ -exports.removeCSVValue = function removeCSVValue(str, value) { - str = str.trim(); - var parts = str ? str.split(/[,\s]+/) : []; - var valueArr = value instanceof Array ? value : value.split(/[,\s]+/); - parts = parts.filter(function (e, i, a) { - return (!isInList(e, valueArr)); - }); - return parts.join(", "); -}; - -/** - * select element of the array where regexp in the first element matches second - * parameter of this function - * - * @param list Array with regexps and return values - * @param chosingMark String by which the element of array is to be matched - * @return Object chosen element + * object to pack messaging. Use as in + postMessage(new Message("GetPassword", { + login: login, + hostname: location.hostname + })); */ -var filterByRegexp = exports.filterByRegexp = - function filterByRegexp(list, chosingMark) { - var chosenPair = []; - if (list.length > 0) { - chosenPair = list.filter(function (pair) { - return new RegExp(pair.regexp, "i").test(chosingMark); - }); - } - if (chosenPair.length > 0) { - return chosenPair[0].addr; - } else { - return ""; - } -}; - -/** - * remove elements from the page based on their IDs - * - * @param doc Document object - * @param target String/Array with ID(s) - * @param remove Boolean indicating whether the node should be - * actually removed or just hidden. - * @return none - * TODO remove parameter could be replaced by function which would - * do actual activity. - */ -exports.killNodes = function killNodes(doc, target, remove) { - target = target.trim(); - var targetArr = target instanceof Array ? target : target.split(/[,\s]+/); - targetArr.forEach(function(x) { - if (remove) { - var targetNode = doc.getElementById(x); - targetNode.parentNode.removeChild(targetNode); - } else { - x.style.display = "none"; - } - }); -}; - -exports.getObjectKeys = function getObjectKeys(obj) { - var keys = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - keys.push(key); - } - } - return keys; -}; - -exports.removeDuplicates = function removeDuplicates (arr) { - for (var i = 0; i < arr.length; i++) { - for (var j = i + 1; j < arr.length; j++) { - if (arr[i] == arr[j]) { - arr.splice (j, 1); - } - } - } - return arr; +exports.Message = function Message(cmd, data) { + this.cmd = cmd; + this.data = data; }; diff --git a/lib/xmlrpc.js b/lib/xmlrpc.js index e883d91..0b84c07 100644 --- a/lib/xmlrpc.js +++ b/lib/xmlrpc.js @@ -1,4 +1,3 @@ -/*jslint rhino: true, forin: true, onevar: false, browser: true */ /*global exports: false */ "use strict"; // Modification of Matěj Cepl released under the MIT/X11 license |