diff options
author | Matěj Cepl <mcepl@redhat.com> | 2010-05-31 19:03:04 +0200 |
---|---|---|
committer | Matěj Cepl <mcepl@redhat.com> | 2010-05-31 19:03:04 +0200 |
commit | 2447e31a804eb7d7fe5ef01b974cba3b615e0594 (patch) | |
tree | f4487a9c0c10b476caa3c74f9871bd3e6617472d /lib/rhbzpage.js | |
parent | 951ef0d578130106e303e5586f53404a900ccf9c (diff) | |
download | bugzilla-triage-2447e31a804eb7d7fe5ef01b974cba3b615e0594.tar.gz |
First attempt to split main script into modules.
Diffstat (limited to 'lib/rhbzpage.js')
-rw-r--r-- | lib/rhbzpage.js | 1072 |
1 files changed, 1072 insertions, 0 deletions
diff --git a/lib/rhbzpage.js b/lib/rhbzpage.js new file mode 100644 index 0000000..f43c1be --- /dev/null +++ b/lib/rhbzpage.js @@ -0,0 +1,1072 @@ +/*jslint 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 */ +/*global jetpack */ +// Released under the MIT/X11 license +// http://www.opensource.org/licenses/mit-license.php +"use strict"; +// var TriagedDistro = 13; +// var NumberOfFrames = 7; +// var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; +// var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; + +// ==================================================================================== +// RHBugzillaPage object + +exports.RHBugzillaPage = function RHBugzillaPage(doc) { + // For identification of graphics card + const 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 + // END OF CONSTANTS + + // Prepare for query buttons + // FIXME getting null for commentArea sometimes + let commentArea = doc.getElementById("comment_status_commit"); + if (commentArea) { + let brElementPlacer = commentArea.getElementsByTagName("br")[0]; + brElementPlacer.setAttribute("id","brElementPlacer_location"); + brElementPlacer.parentNode.insertBefore(doc.createElement("br"), + brElementPlacer); + } + + // inheritance ... call superobject's constructor + BZPage.call(this,doc); + + let that = this; + this.reqCounter = 0; + this.signaturesCounter = 0; + this.chipMagicInterestingLine = ""; + + this.login = this.getLogin(); + this.password = this.getPassword(); + + let ITbutton = this.doc.getElementById("cf_issuetracker"); + this.its = ITbutton ? ITbutton.value.trim() : ""; + + // set default assignee on change of the component + this.doc.getElementById("component").addEventListener("change", + function() { + that.component = that.getOptionValue("component"); + that.changeAssignee("default"); + }, false); + + // getBadAttachments + this.XorgLogAttList = []; + this.XorgLogAttListIndex = 0; + this.attachments = this.getAttachments(); + this.markBadAttachments(); + this.setDefaultAssignee(); + + // Dig out backtrace + this.btSnippet = ""; + + let parseAbrtBacktraces = config.gJSONData.configData.parseAbrtBacktraces; + if (parseAbrtBacktraces && this.RE.Abrt.test(this.title)) { + this.pasteBacktraceInComments(); + } + + // Take care of signature for Fedora bugzappers + if (config.gJSONData.configData.signature.length > 0) { + let signatureFedoraString = config.gJSONData.configData.signature; + this.doc.forms.namedItem("changeform").addEventListener("submit", + function() { + if (this.signaturesCounter < 1) { + that.addStuffToTextBox("comment", signatureFedoraString); + this.signaturesCounter += 1; + } + }, false); + } + + this.setBranding(); + this.checkComments(); + + // TODO Get compiz bugs as well + if ((config.gJSONData.configData.PCIIDsURL + && (config.PCI_ID_Array.length > 0)) + && this.maintCCAddr === "xgl-maint@redhat.com") { + // Add find chip magic button + let whiteboard_string = this.doc.getElementById("status_whiteboard").value; + if (!/card_/.test(whiteboard_string)) { + this.fillInChipMagic(); + } + } +} // END OF RHBugzillaPage CONSTRUCTOR + +RHBugzillaPage.prototype = hlpr.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 this.filterByRegexp(this.constantData.defaultAssignee, + this.component).toLowerCase(); +} + +/** + * Set default assignee + * + * @return none + * sets this.defaultAssignee property according to defaultAssignee list + */ +RHBugzillaPage.prototype.setDefaultAssignee = function() { + this.defaultAssignee = this.getDefaultAssignee(); + let 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 computer 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 + let verNo = this.getVersion(); + this.selectOption("bug_status", "CLOSED"); + let text = ""; + let resolution = ""; + + if (jetpack.selection.text) { + text = jetpack.select.text.trim(); + } + if (text.length > 0) { + resolution = "CURRENTRELEASE"; + this.doc.getElementById("cf_fixed_in").value = text; + } else if (verNo === 999) { + resolution = "RAWHIDE"; + } else { + resolution = "NEXTRELEASE"; + } + this.centralCommandDispatch("resolution", resolution); +}; + +/** + * Additional commands specific for this subclass, overriding superclass one. + */ +RHBugzillaPage.prototype.centralCommandDispatch = function(cmdLabel, cmdParams) { + switch (cmdLabel) { + // Set up our own commands + case "closeUpstream": + this.addClosingUpstream(); + break; + case "computeResolution": + this.closeSomeRelease(); + break; + case "queryStringOurBugzilla": + this.queryForSelection(); + break; + case "queryUpstreamBugzilla": + this.queryUpstream(); + break; + case "sendBugUpstream": + this.sendBugUpstream(); + break; + case "markTriaged": + this.markBugTriaged(); + break; + case "chipMagic": + let 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; + } +}; + + +/* Offline supporting functions */ +/** + * + * @todo FIXME this probably makes a closure and a memory leak name='changeform' + * investigate + * https://developer.mozilla.org/en/How_to_Turn_Off_Form_Autocompletion + * + * <form method="post" action="process_bug.cgi" autocomplete="off"> + * + * Reading + * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.13 + * random notes: - 17.13.3 provides all steps necessary - enctype != + * application/x-www-form-urlencoded => SHOULD fails (no further questions + * needed) - http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2.1. is + * nice explanation (albeit quite dated) - on multiple values + * http://www.w3.org/TR/1999/REC-html401-19991224/interact/forms.html#h-17.6.1 - + * příliš jednoduché + * http://www.innovation.ch/java/HTTPClient/emulating_forms.html - + */ +RHBugzillaPage.prototype.serializeForm = function(form) { + let serialForm = { + dataOut : "", + name : form.name, + method : form.method, + acceptCharset : form.acceptCharset, + action : form.action, // TODO shouldn't we get a non-relative URL? + enctype : form.enctype, + cookie : this.doc.cookie, + autocomplete : form.getAttribute("autocomplete"), + bugNo : this.bugNo + }; + + function genURIElement(sName, sValue) { + return encodeURIComponent(sName) + "=" + encodeURIComponent(sValue); + } + + /** + * @param o + * control to be serialized + * @return String with the serialized control + */ + function serializeControl(element) { + let val = element.value; + // console.log("val.toSource() = " + val.toSource()); + /* + * on HTMLSelectElement we have an attribute 'type' of type DOMString, + * readonly The type of this form control. This is the string + * "select-multiple" when the multiple attribute is true and the string + * "select-one" when false. + */ + if ((val == null) || (val == undefined) || (val == "")) { + return; + } else if (val instanceof Array) { + return val.map(function(x) { + return genURIElement(element.name, x.value); + }).join("&"); + } else if (val instanceof String) { + return genURIElement(element.name, val); + } else { // assume HTMLCollection + return Array.map(val, function(x) { + return genURIElement(element.name, x.value); + }).join("&"); + } + } + + serialForm.dataOut = Array.filter(form.elements,function(el) { + return !el.disabled && el.name && + // FIXME shouldn't I just add && el.value here? + (el.checked || /select|textarea/i.test(el.nodeName) || + /text|hidden|password|search/i.test(el.type)); + }).map(serializeControl).join("&"); + return serialForm; +}; + +//RHBugzillaPage.prototype.submitCallback = function(evt) { +// console.log("Submit Callback!"); +// if (jetpack.__parent__.navigator.onLine) { +// let serForm = this +// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms +// .namedItem("changeform")); +//// console.log("serForm:\n" + serForm.toSource()); +// } else { +// let serForm = this +// .serializeForm(jetpack.tabs.focused.contentWindow.document.forms +// .namedItem("changeform")); +// myStorage.forms[this.bugNo] = serForm; +// evt.stopPropagation(); +// evt.preventDefault(); +// } +//}; + +/** + * + * + * Yes, this is correct, this is NOT method of RHBugzillaPage! + */ +/*function onlineCallback() { + function deserializeAndSend(formData) { + // FIXME notImplemented + // is it enough to just + // run XMLHttpRequest? Probably yes, this is just a form + // and this is just a HTTP request + // it is probably better to get already processed + // application/x-www-form-urlencoded + // see http://htmlhelp.com/reference/html40/forms/form.html for details + // and also https://developer.mozilla.org/en/AJAX/Getting_Started + // what's? + // https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference\ + // /Global_Functions/encodeURI & co. + // this seems to be also interesting + // https://developer.mozilla.org/en/Code_snippets/Post_data_to_window + console.error("Sending bugs not implemented yet!"); + return ""; // FIXME check other HTTP headers to be set + + let bugID = formData.bugNo; + let req = new XMLHttpRequest(); + req.open("POST", formData.action, true); + // FIXME co očekávám za odpověď? req.overrideMimeType("text/xml"); + // * Accept-Encoding + // * Accept-Language + // * Accept (MIME types) + req.setRequestHeader("Connection", "keep-alive"); + req.setRequestHeader("Keep-Alive", 300); + req.setRequestHeader("Content-Type", formData.enctype); + req.setRequestHeader("Referer", bugURL + bugID); + req.setRequestHeader("Accept-Charset", formData.acceptCharset); + req.setRequestHeader("Cookie", formData.cookie); + req.onreadystatechange = function(aEvt) { + if (req.readyState == 4) { + if (req.status == 200) { + console.log("Sent form for bug " + bugID); + delete myStorage.forms[bugID]; + } else { + console.error("Sending form for bug " + bugID + "failed!"); + } + } + }; + req.send(formData.data); + } + + if (myStorage.forms.length > 0) { + myStorage.forms.forEach(function(x) { + deserializeAndSend(x); + }); + } +} +*/ +/* Bugzilla functions. */ + +RHBugzillaPage.prototype.ProfessionalProducts = [ + "Red Hat Enterprise Linux", + "Red Hat Enterprise MRG" +]; + +/** + * + */ +RHBugzillaPage.prototype.pasteBacktraceInComments = function() { + // FIXME This paragraph looks suspicous ... what is it? + // Does it belong to this function? + let notedLabel = this.doc.querySelector("label[for='newcc']"); + while (notedLabel.firstChild) { + let node = notedLabel.removeChild(notedLabel.firstChild); + notedLabel.parentNode.insertBefore(node, notedLabel); + } + notedLabel.parentNode.removeChild(notedLabel); + + let abrtQueryURL = "https://bugzilla.redhat.com/buglist.cgi?" + + "cmdtype=dorem&remaction=run&namedcmd=all%20NEW%20abrt%20crashes&sharer_id=74116"; + + let mainTitle = this.doc + .getElementsByClassName("bz_alias_short_desc_container")[0]; + let 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'))) { + let btAttachments = this.attachments + .filter(function(att, idx, arr) { + return (/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) { + attURL = "https://bugzilla.redhat.com/attachment.cgi?id=" + + x[1]; + if (!this.btSnippet) { + let btRaw = hlpr.loadText(attURL, function(ret) { + this.btSnippet = this.parseBacktrace(ret); + if (this.btSnippet) { + this.addStuffToTextBox("comment", this.btSnippet); + this.addStuffToTextBox("status_whiteboard", + "btparsed"); + } + }, this); + } + }, this); + } +}; + +/** + * + */ +RHBugzillaPage.prototype.markBadAttachments = function() { + let badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; + + let badAttachments = this.attachments.filter(function(att, idx, arr) { + return (hlpr.isInList(att[2], badMIMEArray)); + }); + + if (badAttachments.length > 0) { + let titleElement = this.doc + .getElementsByClassName("bz_alias_short_desc_container")[0]; + titleElement.style.backgroundColor = "olive"; + titleElement.appendChild(this.createFixAllButton(badAttachments)); + 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() { + let prod = this.product; + let result = this.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() { + // First excceptions + if (this.version > 7 && this.version < 12) { + return this.doc.getElementById("bug_status").value.toUpperCase() !== "NEW"; + } else { // and then the rule + 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() { + let brandColor = {}; + let TriagedColor = {}; + + if (this.isEnterprise()) { + console.log("This is an enterprise bug."); + if (this.its && (this.its.length > 0)) { + brandColor = this.RHITColor; + } else { + brandColor = this.RHColor; + } + } else if (new RegExp("Fedora").test(this.product)) { + console.log("This is NOT an enterprise bug."); + if (this.version == 999) { + 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 + let titleElem = this.doc.getElementsByTagName("title")[0]; + + titleElem.textContent = titleElem.textContent.slice(4); + let bodyTitleParent = this.doc.getElementById("summary_alias_container").parentNode; + let 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 (hlpr.isInList(this.maintCCAddr, this.CCList)) { + let ccEditBoxElem = this.doc.getElementById("cc_edit_area_showhide"); + // ccEditBoxElem.textContent = "*"+ccEditBoxElem.textContent; + ccEditBoxElem.style.color = "navy"; + ccEditBoxElem.style.fontWeight = "bolder"; + ccEditBoxElem.style.textDecoration = "underline"; + } + + // mark suspicious components + let compElems; + let suspiciousComponents = config.gJSONData.configData.suspiciousComponents; + if (suspiciousComponents + && hlpr.isInList(this.component, suspiciousComponents) + && (compElems = this.doc + .getElementById("bz_component_edit_container"))) { + compElems.style.background = "red none"; + } +}; + +/** + * 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) { + let that = this; + + function groupIDs(manStr, cardStrID) { + let outStr = that.filterByRegexp(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) { + let soughtID = (manufacturerNo + "," + cardNo).toUpperCase(); + let outList = config.PCI_ID_Array[soughtID]; + if (outList) { + return outList; + } else { + return ""; + } + } + ; + + let outStr = ""; + let cardIDStr = ""; + let cardIDArr = []; + + chipSwitchboard: if (driverStr === "RADEON") { + let 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 = 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").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 () { + let XorgLogURL = ""; + let XorgLogAttID = ""; + let XorgLogFound = false; + let attURL = "", interestingLine = ""; + let interestingArray = []; + + + // 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])); + }); + if (this.XorgLogAttList.length === 0) { + return; + } + + XorgLogAttID = this.XorgLogAttList[this.XorgLogAttListIndex][1]; + attURL = "https://bugzilla.redhat.com/attachment.cgi?id="+XorgLogAttID; + that = this; + + let req = new XMLHttpRequest(); + req.open("GET",attURL,true); + req.onreadystatechange = function (aEvt) { + if (req.readyState == 4) { + if (req.status == 200) { + let ret = req.responseText; + let interestingLineArr = ret.split("\n"). + filter(function (v,i,a) { + return this.RE.Chipset.test(v); + }); + if (interestingLineArr.length >0) { + interestingArray = this.RE.Chipset.exec(interestingLineArr[0]); + interestingLine = interestingArray[2]. + replace(/[\s"]+/g," ").trim(); + // Persuade createNewButton to have mercy and to actually add + // non-default button + this.constantData.chipMagicTrigger = true; + this.chipMagicInterestingLine = interestingLine+"\t"+interestingArray[1] + .toUpperCase(); + this.createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); + } + } else { + throw "Getting attachment " + attURL + "failed!"; + } + } + }; + req.send(""); + + this.XorgLogAttListIndex++; +}; + +/** + * 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 + * + */ +RHBugzillaPage.prototype.queryInNewTab = function(text, component, product) { + console.log("queryInNewTab / text = " + text); + console.log("queryInNewTab / component = " + component); + console.log("queryInNewTab / product = " + product); + let url = "https://bugzilla.redhat.com/buglist.cgi?query_format=advanced"; + if (product) { + url += "&product=" + product.trim(); + } + if (component) { + url += "&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()); + let 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; + url += searchText; + jetpack.tabs.open(url); + // Don't do it ... b.m.o is apparently not powerful enough to sustain + // the weight + // of the search + if (false) { + url = "https://bugzilla.mozilla.org/buglist.cgi?query_format=advanced" + + "field0-0-0=product;type0-0-0=regexp;" + + "value0-0-0=thunderbird|firefox|xulrunner" + + searchText.replace("&", ";"); + jetpack.tabs.open(url); + } + } +}; + +/** + * Get the text to search for and prepare other things for the real executive + * function this.queryInNewTab, and run it. + */ +RHBugzillaPage.prototype.queryForSelection = function() { + let text = jetpack.selection.text; + console.log("selection = " + text); + if (!text) { + text = jetpack.clipboard.get(); + console.log("clipboard = " + text); + } + console.log("text = " + text); + if (text) { + this.queryInNewTab(text, this.component); + } +}; + +/** + * Search simple query in the upstream bugzilla appropriate for the component. + */ +RHBugzillaPage.prototype.queryUpstream = function() { + let text = jetpack.selection.text; + if (!text) { + text = jetpack.clipboard.get(); + } + if (text) { + let text = encodeURIComponent(text.trim()); + let queryUpstreamBugsURLArray = this.constantData.queryUpstreamBug; + let url = this.filterByRegexp(queryUpstreamBugsURLArray, this.component); + jetpack.tabs.open(url + text); + } +} + +/** + * + */ +RHBugzillaPage.prototype.sendBugUpstream = function() { + let url = this.filterByRegexp(this.constantData.newUpstreamBug, this + .getOptionValue("component")); + + let ret = jetpack.tabs.open(url); + let that = this; + jetpack.tabs.onReady(function() { + let otherDoc = ret.contentDocument; + let 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(); + ret.focus(); + }); +}; + +/** + * 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 + */ +RHBugzillaPage.prototype.parseAttachmentLine = function(inElem) { + let MIMEtype = ""; + let size = 0; + + // Skip over obsolete attachments + if (inElem.getElementsByClassName("bz_obsolete").length > 0) { + return ( []); + } + + // getting name of the attachment + let attName = inElem.getElementsByTagName("b")[0].textContent.trim(); + + let aHrefsArr = inElem.getElementsByTagName("a"); + let aHref = Array.filter(aHrefsArr, function(x) { + return x.textContent.trim() == "Details"; + })[0]; + let id = parseInt(aHref.getAttribute("href").replace( + /^.*attachment.cgi\?id=/, ""), 10); + + // getting MIME type and size + let 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 ]; +}; + +/** + * Add accesskey to the particular element + * + * @param rootElement Element to which the new text object will be attached + * @param beforeText Text before the accesskey character + * @param accKey what will be the accesskey itself + * @param afterText text after the accesskey character + * @return modified element with the fixed accesskey + */ +RHBugzillaPage.prototype.fixElement = function(elem, beforeText, accKey, afterText) { + elem.setAttribute("accesskey", accKey.toLowerCase()); + elem.innerHTML = beforeText + "<b><u>" + accKey + "</u></b>" + afterText; + return elem; +}; + +/** + * 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(URLhostname) { + let bugzillaID = ""; + if (this.constantData.bugzillalabelNames[URLhostname]) { + bugzillaID = this.constantData.bugzillalabelNames[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.fixingMIMECallBack = function(data, textStatus) { + let that = this; + if (--this.reqCounter <= 0) { + setTimeout(function () { + that.doc.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(id, type, email) { + if (type === undefined) { + type = "text/plain"; + } + if (email === undefined) { + email = false; + } + + let msg = new XMLRPCMessage("bugzilla.updateAttachMimeType"); + msg.addParameter( { + 'attach_id' : id, + 'mime_type' : type, + 'nomail' : !email + }); + msg.addParameter(this.login); + msg.addParameter(this.password); + + let req = new XMLHttpRequest(); + let that = this; + req.open("POST", XMLRPCurl, true); + req.overrideMimeType("text/xml"); + req.setRequestHeader("Content-type", "text/xml"); + req.onreadystatechange = function(aEvt) { + if (req.readyState == 4) { + if (req.status == 200) { + console.log("Fixing attachment MIME type success!"); + that.fixingMIMECallBack(); + } else { + console.error("Fixing MIME type attachment failed!"); + } + } + }; + req.send(msg.xml()); + this.reqCounter++; +}; + +/** + * Create a button for fixing all bad attachments. + * + * @param list Array of all bad attachmentss + * @return button fixing all bad Attachments + */ +RHBugzillaPage.prototype.createFixAllButton = function(list) { + if (!XMLRPCMessage) { + return; + } + let that = this; + let elem = this.doc.createElement("a"); + elem.setAttribute("href", ""); + elem.setAttribute("accesskey", "f"); + elem.innerHTML = "<b>F</b>ix all"; + elem.addEventListener("click", function() { + Array.forEach(list, function(x) { + this.fixAttachById(x[1]); + }, that); + }, false); + return elem; +}; + +/** + * 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(row) { + let that = this; + let elemS = row[4].getElementsByTagName("td"); + let elem = elemS[elemS.length - 1]; + elem.innerHTML += "<br/><a href=''>Text</a>"; + elem.addEventListener("click", function(x) { + that.fixAttachById(row[1], "text/plain"); + }, false); +}; + +/** + * Add information about the upstream bug upstream, and closing it. + * + * @param evt Event which called this handler + * @return none + */ +RHBugzillaPage.prototype.addClosingUpstream = function() { + let refs = this.doc.getElementById("external_bugs_table") + .getElementsByTagName("tr"); + // that's a bad id, if there is a one. :) + let inputBox = this.doc.getElementById("inputbox"); + let externalBugID = 0; + let wholeURL = ""; + + // Fix missing ID on the external_id SELECT + this.doc.getElementsByName("external_id")[0].setAttribute("id", + "external_id"); + + if (inputBox.value.match(/^http.*/)) { + let helpAElem = this.doc.createElement("a"); + wholeURL = inputBox.value; + helpAElem.setAttribute("href", wholeURL); + let paramsArr = helpAElem.search.replace(/^\?/, '').split('&'); + // get convert URL parameters to an Object + let params = {}, s = []; + paramsArr.forEach(function(par, idx, arr) { + s = par.split('='); + params[s[0]] = s[1]; + }); + if (params.id) { + externalBugID = parseInt(params.id, 10); + inputBox.value = externalBugID; + } + // get host and bugzillaName + let bugzillaName = this.getBugzillaName(helpAElem.hostname); + this.selectOption("external_id", bugzillaName); + } else if (!isNaN(inputBox.value)) { + externalBugID = parseInt(inputBox.value, 10); + let 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? + } + + // FIXME THis is not good, we don't have a feedback for other commands, + // not to be run, if this fails. + + // It is not good to close bug as UPSTREAM, if there is no reference + // to the upstream bug. + if ((externalBugID > 0) || (refs.length > 2)) { + let 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) + let ver = this.getVersion(); + let assignee = this.getOwner(); + if ((!this.isEnterprise()) && (ver < TriagedDistro)) { + this.selectOption("bug_status", "ASSIGNED"); + } + this.addStuffToTextBox("keywords","Triaged"); +} + +/** + * + */ +RHBugzillaPage.prototype.parseBacktrace = function(ret) { + let splitArray = ret.split("\n"); + let i = 0, ii = splitArray.length; + let outStr = "", curLine = "", numStr = ""; + let 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; +}; |