diff options
-rw-r--r-- | jsons/RH_Data-packages.json | 391 | ||||
-rw-r--r-- | lib/bzpage.js | 704 | ||||
-rw-r--r-- | lib/main.js | 142 | ||||
-rw-r--r-- | lib/puvodni/bzpage.js | 729 | ||||
-rw-r--r-- | lib/puvodni/clipboard.js (renamed from lib/clipboard.js) | 36 | ||||
-rw-r--r-- | lib/puvodni/color.js (renamed from lib/color.js) | 0 | ||||
-rw-r--r-- | lib/puvodni/logger.js (renamed from lib/logger.js) | 13 | ||||
-rw-r--r-- | lib/puvodni/mozillabzpage.js (renamed from lib/mozillabzpage.js) | 4 | ||||
-rw-r--r-- | lib/puvodni/offline-support.js (renamed from lib/offline-support.js) | 0 | ||||
-rw-r--r-- | lib/puvodni/old-main.js | 106 | ||||
-rw-r--r-- | lib/puvodni/rhbzpage.js | 941 | ||||
-rw-r--r-- | lib/puvodni/skip-process-bug.js (renamed from lib/skip-process-bug.js) | 0 | ||||
-rw-r--r-- | lib/puvodni/xmlrpc.js (renamed from lib/xmlrpc.js) | 0 | ||||
-rw-r--r-- | lib/rhbzpage.js | 946 | ||||
-rw-r--r-- | lib/util.js | 114 | ||||
-rw-r--r-- | package.json | 3 | ||||
-rw-r--r-- | tests/test-clipboard.js | 9 |
17 files changed, 2268 insertions, 1870 deletions
diff --git a/jsons/RH_Data-packages.json b/jsons/RH_Data-packages.json new file mode 100644 index 0000000..761158c --- /dev/null +++ b/jsons/RH_Data-packages.json @@ -0,0 +1,391 @@ +{ + "commentPackages": { + "test": { + "test-all": { + "name": "Test lots of things", + "comment": "some comment", + "position": "topRow,bottomRow,dropDown,[+-]ID", + "product": "Thunderbird", + "component": "Internal Components", + "priority": "P1", + "severity": "major", + "assignee": "mschroeder", + "qacontact": "foobar", + "url": "http://example.com", + "addDependsOn": "123", + "removeDependsOn": "456", + "addBlocks": "789", + "removeBlocks": "101112", + "addWhiteboard": [ + "a", + "b", + "c" + ], + "removeWhiteboard": [ + "a", + "b" + ], + "addKeyword": [ + "d", + "e" + ], + "removeKeyword": "e", + "url": "http://example.com", + "status": "RESOLVED", + "resolution": "INCOMPLETE", + "version": "Lightning 0.3", + "os": "All", + "platform": "All", + "setNeedinfo": true + } + }, + "rh-common": { + "markTriaged": { + "position": "-commit_middle", + "name": "Mark Triaged", + "markTriaged": true, + "assignee": null + }, + "addnoresponse": { + "name": "NoRespns", + "position": "topRow", + "commentIdx": "noResponseString", + "status": "CLOSED", + "resolution": "INSUFFICIENT_DATA" + }, + "addcloseupstream": { + "name": "CloseUP", + "position": "topRow", + "closeUpstream": true + }, + "moveyourbutt": { + "name": "InfRemind", + "position": "topRow", + "commentIdx": "moveYourButString", + "setNeedinfo": true + }, + "needinforbtn": { + "name": "NEEDINFO", + "position": "bottomRow", + "setNeedinfo": true + }, + "closeoldrhel": { + "name": "old", + "position": "bottomRow", + "commentIdx": "obsoleteRHELString", + "status": "CLOSED", + "resolution": "WONTFIX" + }, + "notriagebtn": { + "name": "noTriage", + "position": "bottomRow", + "comment": "This bug has been triaged", + "status": "ASSIGNED", + "addCC": "self" + }, + "nodebugsymbols": { + "name": "noDbg", + "position": "bottomRow", + "commentIdx": "noDebugSymbols", + "status": "CLOSED", + "resolution": "INSUFFICIENT_DATA" + }, + "issuetrackerbtn": { + "position": "bottomRow", + "name": "RHEL", + "commentIdx": "RHELString", + "setNeedinfo": true + }, + "closeOnRetest": { + "position": "bottomRow", + "name": "Retest", + "commentIdx": "thankLettingUsKnow", + "status": "CLOSED", + "computeResolution": true + }, + "queryButton": { + "position": "-brElementPlacer_location", + "name": "Query for string", + "queryStringOurBugzilla": true + }, + "sendUpstream": { + "position": "-brElementPlacer_location", + "name": "Send upstream", + "sendBugUpstream": true + }, + "upstreamQuery": { + "position": "-brElementPlacer_location", + "name": "Query upstream", + "ifExist": "queryUpstreamBug", + "queryStringUpstreamBugzilla": true + }, + "setDefaultAssignee": { + "position": "+bz_assignee_edit_container", + "name": "Def. Assignee", + "ifExist": "defaultAssigneeTrigger", + "assignee": "default" + } + }, + "rh-gecko": { + "addgetffrepro": { + "name": "Repro", + "position": "topRow", + "commentIdx": "getAbrtReproInfo", + "setNeedinfo": true + }, + "addgetfirefoxinfo": { + "name": "GeckInf", + "position": "topRow", + "commentIdx": "getFirefoxBacktraceString", + "setNeedinfo": true + }, + "webfailure": { + "name": "Bad site", + "position": "topRow", + "commentIdx": "websiteFailure", + "status": "CLOSED", + "resolution": "NOTABUG" + }, + "crashInFlash": { + "name": "flashCrash", + "position": "topRow", + "commentIdx": "flashCrashString", + "status": "CLOSED", + "resolution": "CANTFIX" + } + }, + "rh-xorg": { + "addgetlogstext": { + "name": "X logs", + "position": "topRow", + "commentIdx": "getLogsString", + "setNeedinfo": true + }, + "closenvidia": { + "name": "nVidia", + "position": "bottomRow", + "commentIdx": "cantfixNvidiaString", + "status": "CLOSED", + "resolution": "CANTFIX" + }, + "xloglivecdbtn": { + "position": "bottomRow", + "name": "InstFail", + "commentIdx": "getLogsLiveCDString", + "setNeedinfo": true + }, + "chipMagic": { + "position": "+status_whiteboard", + "name": "Fill In", + "ifExist": "chipMagicTrigger", + "chipMagic": true + } + } + }, + "commentStrings": { + "noRHEL4String": "I don't think this will be fixed for RHEL4, which support covers now only security and major bugs.\n\nClosing as WONTFIX.", + "thankLettingUsKnow": "Thank you for letting us know.", + "noDebugSymbols": "Thank you for taking the time to report this bug report. Unfortunately, that stack trace is not very useful in determining the cause of the crash, because there are no debugging symbols loaded (probably abrt failed to load them).\n\nUnfortunately, we cannot use this backtrace.\n\nClosing as INSUFFICIENT_DATA.", + "noTriageString": "There is nothing to triage here.\n\nSwitching to ASSIGNED so that developers have responsibility to do whatever they want to do with it.", + "getAbrtReproInfo": "Reporter, could you please describe us what you have done to get to this point, and how we can reproduce this issue here? Is there anything special about your system, network, configuration which we need to replicate here in order to reproduce your problem please?", + "getLogsString": "Thanks for the bug report. We have reviewed the information you have provided above, and there is some additional information we require that will be helpful in our diagnosis of this issue.\n\nPlease add drm.debug=0x04 to the kernel command line, restart computer, and attach\n\n* your X server config file (/etc/X11/xorg.conf, if available),\n* X server log file (/var/log/Xorg.*.log)\n* output of the dmesg command, and\n* system log (/var/log/messages)\n\nto the bug report as individual uncompressed file attachments using the bugzilla file attachment link above.\n\nWe will review this issue again once you've had a chance to attach this information.\n\nThanks in advance.", + "getLogsLiveCDString": "Thanks for the bug report. We have reviewed the information you have provided above, and there is some additional information especially concerning your hardware we require that will be helpful in our diagnosis of this issue.\n\nIf the anaconda crashes during the switching to the graphic mode, most likely the problem lies in Xorg support for your graphics chip. There are couple of options how we can obtain information necessary for resolving the issue.\n\nIf the computer is not completely frozen when installation fails, switch to the console (Ctrl+Alt+F2) and copy /tmp/X* and /var/log/anaconda.xlog to some other place -- USB stick, some other computer via network, somewhere on the Internet, and please attach it to the bug report as individual uncompressed file attachments using the bugzilla file attachment link above.\n\nIf the computer is completely useless after installation fails, you can also install Fedora with a VESA mode driver (see http://docs.fedoraproject.org/en-US/Fedora/13/html/Installation_Guide/\nfor more information on that). Then after successful installation you can collect /var/log/anaconda.xlog, /var/log/Xorg.0.log, and the output of the program dmesg instead.\n\nOr you can install Fedora in a text mode completely, and then start X after that. If it fails, still /var/log/Xorg.0.log and the output of dmesg program from the failed attempt to start X would be useful.\n\nWe will review this issue again once you've had a chance to attach this information.\n\nThank you very much in advance.", + "getFirefoxBacktraceString": "Thanks for the bug report. We have reviewed the information you have provided above, and there is some additional information we require that will be helpful in our diagnosis of this issue.\n\nFirst of all, could we get output of the command\n\n\u0009rpm -qa \\*xulrun\\* \\*firefox\\* \\*mozilla\\* \\*flash\\* \\*plugin\\*\n\nPlease also install firefox-debuginfo (debuginfo-install is from\nyum-utils package).\n\n\u0009debuginfo-install firefox\n\nThen run firefox with a parameter -g. That will start firefox running inside of gdb debugger. Then use command run and do whatever you did to make firefox crash. When it happens, you should go back to the gdb and run\n\n\u0009(gdb) thread apply all backtrace\n\nThis produces usually many screens of the text. Copy all of them into a text editor and attach the file to the bug as an uncompressed attachment.\n\nPlease, install also valgrind (from valgrind package) and run\n\n\u0009valgrind --trace-children=yes --log-file=/tmp/firefox-valgrind-log.txt /usr/bin/firefox\n\n(that's one line command, browser breaking this line into two notwithstanding)\n\nPlease, attach the file /tmp/firefox-valgrind-log.txt to this bug as an attachment as well.\n\nWe will review this issue again once you've had a chance to attach this information.\n\nThanks in advance.", + "noResponseString": "Since there are insufficient details provided in this report for us to investigate the issue further, and we have not received feedback to the information we have requested above, we will assume the problem was not reproducible, or has been fixed in one of the updates we have released for the reporter's distribution.\n\nUsers who have experienced this problem are encouraged to upgrade to the latest update of their distribution, and if this issue turns out to still be reproducible in the latest update, please reopen this bug with additional information.\n\nClosing as INSUFFICIENT_DATA.", + "websiteFailure": "To report a broken website, first please make sure that you are running the latest release of Firefox (i.e., updated from updates-testing repository for your distribution or Rawhide).\n\nThen, if you can still reproduce the issue, pull down the 'Help' menu and select 'Report Broken Web Site'. This will start a wizard which will prompt you for the information required (privacy policy). This information is sent to a database and aggregated, so we have a good idea of which sites the most people are having problems with.\n\nIf you are interested, you can search the database of reports (http://reporter.mozilla.org/).\n\nClosing this bug as NOTABUG, if you don't agree with this resolution, please, reopen this bug and attach any relevant information to this bug.", + "sentUpstreamString": "We filed this bug in the upstream database (§§§) and believe that it is more appropriate to let it be resolved upstream.\n\nWe will continue to track the issue in the centralized upstream bug tracker, and will review any bug fixes that become available for consideration in future updates.\n\nThank you for the bug report.", + "moveYourButString": "Reporter, could you please reply to the previous question? If you won't reply in one month, I will have to close this bug as INSUFFICIENT_DATA. Thank you.", + "RHELString": "For official Red Hat Enterprise Linux support, please log into the Red Hat support website at http://www.redhat.com/support and file a support ticket. To understand how your bug is tracked through our system, please see https://www.redhat.com/support/process/production/#howrh", + "cantfixNvidiaString": "Thanks for the report. We are sorry that we cannot help you with your problem, but we are not able to support binary-only drivers. If you would be able to reproduce this issue using only open source software, please, reopen this bug with the additional information, but in meantime I have no choice than to close this bug as CANTFIX (because we really cannot fix it).\n\nThe open source 'nouveau' driver (in package xorg-x11-drv-nouveau) is the recommended alternative for users of Nvidia graphic chips. It is used by default in Fedora 11 and later if you remove any customizations that explicitly set the video driver. The older \"nv\" driver may be needed in some cases. It is also available in older Fedora releases. Install the packages xorg-x11-drv-nouveau or xorg-x11-drv-nv and override the X server's default choice if necessary. See https://fedoraproject.org/wiki/Features/NouveauAsDefault for more information.\n\nIf you used a non-packaged version of the driver from the Nvidia website please clean your system from additional libraries and software it installed. For users who are experiencing problems installing, configuring, or using the unsupported 3rd party proprietary \"nvidia\" video driver, Nvidia provides indirect customer support via an online web based support forum. Nvidia monitors these web forums for commonly reported problems and passes them on to Nvidia engineers for investigation. Once they've isolated a particular problem, it is often fixed in a future video driver update.\n\nThe NVNews Nvidia Linux driver forum is located at:\n\n\u0009http://www.nvnews.net/vbulletin/forumdisplay.php?s=&forumid=14\n\nOnce you have reported this issue in the Nvidia web forums, others who may have experienced the particular problem may be able to assist. If there is a real bug occuring, Nvidia will be able to determine this, and will likely resolve the issue in a future driver update for the operating system releases that they officially support.\n\nWhile we does not support the proprietary nvidia driver, users requiring technical support may also find the various X.Org, XFree86, and Red Hat/Fedora mailing lists helpful in finding assistance:\n\nX.Org mailing lists:\n\u0009http://www.freedesktop.org/XOrg/XorgMailingLists\n\nXFree86 mailing lists:\n\u0009http://www.xfree86.org/sos/lists.html\n\nRed Hat/Fedora mailing lists:\n\u0009https://listman.redhat.com/mailman/listinfo", + "flashCrashString": "Unfortunately, crash here happened in the binary-only flash player for which we don't have any source code, so unfortunately we cannot help you with it.\n\nClosing as CANTFIX (because that's our situation)", + "obsoleteRHELString": "This bug is filed against release of RHEL, which is in maintenance phase. During the maintenance phase, only security errata and select mission critical bug fixes will be released for enterprise products. Since this bug does not meet that criteria, it is now being closed.\n\nFor more information of the RHEL errata support policy, please visit:\n\nhttp://www.redhat.com/security/updates/errata/\n\nIf you feel this bug is indeed mission critical, please contact your support representative. You may be asked to provide detailed information on how this bug is affecting you.", + "getEOL": "Thank you for your bug report.\nWe are sorry, but the Fedora Project no longer maintains this version of Fedora. Please upgrade to the latest version and reopen this bug against that version if this bug exists there.\nAs a result we are setting this bug to CLOSED:WONTFIX" + }, + "configData": { + "queryButton": true, + "upstreamButton": true, + "parseAbrtBacktraces": true, + "submitsLogging": true, + "suspiciousComponents": [ + "xorg-x11", + "xorg-x11-drivers" + ], + "PCIIDsURL": "http://mcepl.fedorapeople.org/scripts/drm_pciids.json", + "objectStyle": "RH", + "matches": [ + "https://bugzilla.redhat.com/show_bug.cgi.*", + "https://bugzilla.mozilla.org/show_bug.cgi.*" + ], + "signature": "" + }, + "constantData": { + "bugzillalabelNames": { + "bugzilla.mozilla.org": "Mozilla Foundation", + "bugs.freedesktop.org": "FreeDesktop.org", + "bugs.eclipse.org": "Eclipse Project", + "bugzilla.gnome.org": "GNOME Desktop", + "bugzilla.redhat.com": "Red Hat" + }, + "bugzillalabelAbbreviations": { + "bugzilla.mozilla.org": "MoFo", + "bugs.freedesktop.org": "FDo", + "bugs.eclipse.org": "Eclipse", + "bugzilla.gnome.org": "bgo", + "bugzilla.redhat.com": "RH" + }, + "defaultAssignee": [ + { + "regexp": "xorg-x11-drv-(ati|mach.*|r128)", + "addr": "jglisse@redhat.com" + }, + { + "regexp": "xorg-x11-drv-intel", + "addr": "ajax@redhat.com" + }, + { + "regexp": "xorg-x11-(server.*|drv-vesa|drv-mga)", + "addr": "ajax@redhat.com" + }, + { + "regexp": "xorg-x11-drv-(keyboard|mouse|evdev)|xkeyboard-config|linuxwacom", + "addr": "peter.hutterer@redhat.com" + }, + { + "regexp": "(nss.*)", + "addr": "kengert@redhat.com" + }, + { + "regexp": "thunderbird", + "addr": "jhorak@redhat.com" + }, + { + "regexp": "xorg-x11-init", + "addr": "sandmann@redhat.com" + }, + { + "regexp": "(seamonkey|firefox|xulrunner)", + "addr": "stransky@redhat.com" + }, + { + "regexp": "xorg-x11-drv-(nv|nouveau)", + "addr": "bskeggs@redhat.com" + } + ], + "CCmaintainer": [ + { + "regexp": "xorg|X11|compiz|chkfontpath|imake|libdmx|libdrm|libfontenc|libFS|libICE|libSM|libwnck|libxkbfile|mesa|pyxf86config|system-config-display|xkeyboard-config|xrestop|xsri", + "addr": ["xgl-maint@redhat.com"] + }, + { + "regexp": "epiphany.*|firefox|galeon|gecko-sharp2|htmlview|^mozilla|seamonkey|thunderbird|xulrunner|nspluginwrapper", + "addr": ["gecko-bugs-nobody@fedoraproject.org" , "gecko-bugs-nobody@redhat.com" ] + } + ], + "newUpstreamBug": [ + { + "regexp": "thunderbird", + "addr": "https://bugzilla.mozilla.org/enter_bug.cgi?product=Thunderbird" + }, + { + "regexp": "firefox", + "addr": "https://bugzilla.mozilla.org/enter_bug.cgi?product=Firefox" + }, + { + "regexp": "xulrunner", + "addr": "https://bugzilla.mozilla.org/enter_bug.cgi?product=Core" + }, + { + "regexp": "xorg-x11.*", + "addr": "https://bugs.freedesktop.org/enter_bug.cgi?product=xorg" + } + ], + "queryUpstreamBug": [ + { + "regexp": "thunderbird|firefox|xulrunner", + "addr": "https://bugzilla.mozilla.org/buglist.cgi?quicksearch=" + }, + { + "regexp": "xorg-x11.*", + "addr": "https://bugs.freedesktop.org/buglist.cgi?quicksearch=product%3Axorg+" + } + ], + "chipIDsGroupings": [ + { + "regexp": "RADEON,R(100|[VS][12]00|S200M|S250|N50)", + "addr": "R100" + }, + { + "regexp": "RADEON,R(200|V2[58]0|C350|S3[05]0)", + "addr": "R200" + }, + { + "regexp": "RADEON,R(3[056]0|V3[56][10])", + "addr": "R300" + }, + { + "regexp": "RADEON,RV3[78]0", + "addr": "R300e" + }, + { + "regexp": "RADEON,RS4[08][025]", + "addr": "IGP300" + }, + { + "regexp": "RADEON,R(4[123]0|481)", + "addr": "R400" + }, + { + "regexp": "RADEON,R(V410|423|4[38]0)", + "addr": "R420" + }, + { + "regexp": "RADEON,R(V51[56]|V53[05]|V5[67]0|5[28]0|580\\+)", + "addr": "R500" + }, + { + "regexp": "RADEON,RS(6[09]|74)0C?", + "addr": "IGP600" + }, + { + "regexp": "RADEON,R(V6[1273][50]|R6[80]0).*", + "addr": "R600" + }, + { + "regexp": "RADEON,RS([78]80)[CD]?", + "addr": "IGP700" + }, + { + "regexp": "RADEON,R(V7[13479]0.*|700)", + "addr": "R700" + }, + { + "regexp": "RADEON,(Juniper|Cypress|Hemlock).*", + "addr": "R800" + }, + { + "regexp": "RADEON,M(9|10).*", + "addr": "FireGL" + }, + { + "regexp": "NOUVEAU,NV[13456]", + "addr": "preGeForce" + }, + { + "regexp": "NOUVEAU,NV10", + "addr": "GeForce" + }, + { + "regexp": "NOUVEAU,NV1[156]", + "addr": "GeForce2" + }, + { + "regexp": "NOUVEAU,NV20", + "addr": "GeForce3" + }, + { + "regexp": "INTEL,865PE", + "addr": "AGP8x" + } + ] + } +} diff --git a/lib/bzpage.js b/lib/bzpage.js index aebcac6..dcaa43c 100644 --- a/lib/bzpage.js +++ b/lib/bzpage.js @@ -4,707 +4,27 @@ // http://www.opensource.org/licenses/mit-license.php "use strict"; var util = require("util"); +var apiUtils = require("api-utils"); var simpleStorage = require("simple-storage"); -var TriagedDistro = 13; -var NumberOfFrames = 7; -var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; -var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; - // ==================================================================================== // BZPage's methods -exports.BZPage = function BZPage(doc) { - // 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 +var BZPage = function BZPage(doc, config) { // initialize dynamic properties - this.doc = doc; - this.packages = this.getInstalledPackages(); - if ("commentStrings" in config.gJSONData) { - this.commentStrings = config.gJSONData.commentStrings; - } - - - if ("constantData" in config.gJSONData) { - // this is wrong, I shouldn't bother anybody with my Xorg data, and - // I should load it from URL - // var stuff = JSON.parse(self.data.load("chiIDsGroupings.json")); - this.constantData = config.gJSONData.constantData; - } - - if ("CCmaintainer" in config.gJSONData.constantData) { - this.defBugzillaMaintainerArr = config.gJSONData.constantData.CCmaintainer; - } - - if ("submitsLogging" in config.gJSONData.configData && - config.gJSONData.configData.submitsLogging) { - this.log = config.logger; - this.setUpLogging(); - } - - this.submitHandlerInstalled = false; - this.bugNo = util.getBugNo(this.doc.location.toString()); - this.reporter = this.getReporter(); - this.product = this.getOptionValue("product"); - this.component = this.getOptionValue("component"); - this.version = this.getVersion(); - this.title = this.doc.getElementById("short_desc_nonedit_display").textContent; - this.CCList = this.getCCList(); - - this.generateButtons(); -} - -/** - * - */ -BZPage.prototype.getInstalledPackages = function() { - let installedPackages = {}; - if (config.gJSONData && ("commentPackages" in config.gJSONData)) { - let enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/); - for each (let pkg in enabledPackages) { - if (pkg in config.gJSONData.commentPackages) { - installedPackages[pkg] = config.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 (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": - let 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; - // 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. - */ -BZPage.prototype.executeCommand = function(cmd) { - let [pkg, id] = cmd.split("//"); - let commentObj = this.packages[pkg][id]; - - for (let key in commentObj) { - this.centralCommandDispatch(key,commentObj[key]); - } -}; - -/** - * Add XGL to the CC list - * - * @param evt Event which made this function active - * @return none - */ -BZPage.prototype.changeAssignee = function(newAssignee) { - let defAssigneeButton = null; - this.addToCCList(this.owner); - if (newAssignee === null) { - this.doc.getElementById("set_default_assignee").removeAttribute( - "checked"); - return ; - } - - if (this.getDefaultAssignee) { - if (newAssignee == "default") { - let 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; - if (defAssigneeButton = this.doc - .getElementById("setDefaultAssignee_btn")) { - 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(pkg, cmd) { - let select = this.doc.getElementById("comment_action"); - if (!select) { - let 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 () { - let valueElement = that.doc.getElementById("comment_action"); - if (valueElement) { - let value = valueElement.getAttribute("value"); - } else { - return; - } - that.executeCommand(value); - }, false); - } - - let opt = that.doc.createElement("option"); - opt.value = pkg + "//" + cmd; - opt.textContent = this.packages[pkg][cmd].name; - select.appendChild(opt); -}; - -/** - * 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(location, after, pkg, id) { - let that = this; - let cmdObj = this.packages[pkg][id]; - let newId = id + "_btn"; - let label = cmdObj["name"]; - - // protection against double-firings - if (this.doc.getElementById(newId)) { - console.error("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 ; - } - } - - let 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); - - let originalLocation = this.doc.getElementById(location); - - 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); - } -}; - -/** - * - */ -BZPage.prototype.generateButtons = function() { - let topRowPosition = "topRowPositionID"; - let bottomRowPosition = "commit"; - - // create anchor for the top toolbar - let commentBox = this.doc.getElementById("comment"); - let brElement = this.doc.createElement("br"); - brElement.setAttribute("id",topRowPosition); - commentBox.parentNode.normalize(); - commentBox.parentNode.insertBefore(brElement, commentBox); - - for (let pkg in this.packages) { - for (let cmdIdx in this.packages[pkg]) { - let 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 - let firstChr = cmdObj.position.charAt(0); - let newId = cmdObj.position.substr(1); - this.createNewButton(newId, firstChr == "+", pkg, cmdIdx); - break; - } - } - } -}; - -/** - * Get the current email of the reporter of the bug. - * - * @return string - */ -BZPage.prototype.getReporter = function() { - return this.doc - .querySelector("#bz_show_bug_column_2 > table .vcard:first-of-type > a").textContent; -}; - -/** - * Get the current version of the Fedora release ... even if changed meanwhile - * by bug triager. - * - * @return string (integer for released Fedora, float for RHEL, rawhide) - */ -BZPage.prototype.getVersion = function() { - let verStr = this.getOptionValue("version").toLowerCase(); - let verNo = 0; - if (/rawhide/.test(verStr)) { - verNo = 999; - } else { - verNo = Number(verStr); - } - return verNo; -}; - -BZPage.prototype.commentsWalker = function(fce) { - let 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() { - let that = this; - this.commentsWalker(function(x) { - let email = x.getElementsByClassName("vcard")[0] - .getElementsByTagName("a")[0].textContent; - if (new RegExp(that.reporter).test(email)) { - x.style.backgroundColor = that.ReporterColor.toString(); - } - }); + this.doc = doc; + console.log("Now we are inside!"); + console.log("this = " + this); }; -BZPage.prototype.collectComments = function() { - let outStr = ""; - this.commentsWalker(function(x) { - outStr += x.getElementsByTagName("pre")[0].textContent + "\n"; - }); - return outStr.trim(); +BZPage.prototype.getURL = function getURL () { + console.log("url = " + this.doc.location.href); + return this.doc.location.href; }; - -/** - * Select option with given label on the <SELECT> element with given id. - * - * Also execute change HTMLEvent, so that the form behaves accordingly. - * - * @param id - * @param label - * @return none - * - * FIXME bugzilla-comments version has this signature: - * selectOption = function selectOption(select, value) { - let doc = select[0].ownerDocument; - select.val(value); - */ -BZPage.prototype.selectOption = function(id, label) { - let sel = this.doc.getElementById(id); - sel.value = label; - let 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(targetID) { - let 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(id, stuff) { - let 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(id, stuff) { - let 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(id, str) { - try { - var 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 (kwd.trim().indexOf(str) != -1); -}; - -/** - * Check for the presence of a keyword - * - * @param str String with the keyword - * @return Boolean - */ -BZPage.prototype.hasKeyword = function(str) { - return (this.idContainsWord('keywords', str)); -}; - -/** - * - */ -BZPage.prototype.getOptionValue = function(id) { - // Some special bugs don't have version for example - let element = this.doc.getElementById(id); - if (element) { - return element.value; - } else { - console.log("Failed to find element with id = " + id); - return "#NA"; - } -}; - -/** - * 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() { - this.clickMouse("needinfo"); - this.selectOption("needinfo_role", "reporter"); -}; - -/** - * - */ -BZPage.prototype.getOwner = function() { - let priorityParent = this.doc.querySelector("label[for~='target_milestone']") - .parentNode.parentNode.parentNode; - let assigneeAElement = priorityParent.querySelector("tr:nth-of-type(1) a.email"); - let assgineeHref = decodeURI(assigneeAElement.getAttribute("href")); - let email = assgineeHref.split(":")[1]; - return email; +BZPage.prototype.getBugId = function getBugId () { + return util.getBugNo(this.doc.location.href); }; -/** - * Get login of the currently logged-in user. - * - * @return String with the login name of the currently logged-in user - */ -BZPage.prototype.getLogin = function () { - let lastLIElement = this.doc.querySelector("#header ul.links li:last-of-type"); - let loginArr = lastLIElement.textContent.split("\n"); - let loginStr = loginArr[loginArr.length - 1].trim(); - return loginStr; -}; - -/** - * 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(component) { - let address = util.filterByRegexp(this.defBugzillaMaintainerArr, component); - return address; -} - -/** - * 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() { - let outAtts = []; - let atts = this.doc.getElementById("attachment_table") - .getElementsByTagName("tr"); - for ( let i = 1, ii = atts.length - 1; i < ii; i++) { - outAtts.push(this.parseAttachmentLine(atts[i])); - } - return outAtts; -}; - -/** - * 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() { - if (jetpack.storage.settings.BZpassword) { - return jetpack.storage.settings.BZpassword; - } else { - let prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] - .getService(Components.interfaces.nsIPromptService); - let password = { - value : "" - }; // default the password to pass - let check = { - value : true - }; // default the checkbox to true - let result = prompts.promptPassword(null, "Title", "Enter password:", - password, null, check); - // result is true if OK was pressed, false if cancel was pressed. - // password.value is - // set if OK was pressed. The checkbox is not displayed. - if (result) { - let passwordText = password.value; - jetpack.storage.settings.BZpassword = passwordText; - jetpack.storage.simple.sync(); - return passwordText; - } - } - return null; -}; - -/** - * - */ -BZPage.prototype.setUpLogging = function() { - // For adding additional buttons to the top toolbar - let additionalButtons = this.doc.querySelector("#bugzilla-body *.related_actions"); - let that = this; - - // logging all submits for timesheet - // FIXME we should merge in functionality of RHBugzillaPage.submitCallback - // and actually make it working - // Maybe rewriting whole offline capability into a separate object? - if (!this.submitHandlerInstalled) { - console.log("Installing submit callback!"); - this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { - console.log("Submit callback!"); - let resp = that.log.addLogRecord(that); - console.log("resp = " + resp); - if (resp === null) { - console.log("Avoiding submitting!"); - // FIXME doesn't work ... still submitting' - evt.stopPropagation(); - evt.preventDefault(); - } - }, false); - this.submitHandlerInstalled = true; - } - - let generateTimeSheetUI = this.doc.createElement("li"); - generateTimeSheetUI.innerHTML = "\u00A0-\u00A0<a href='#' id='generateTSButton'>" - + "Generate timesheet</a>"; - additionalButtons.appendChild(generateTimeSheetUI); - this.doc.getElementById("generateTSButton").addEventListener( - "click", - function(evt) { - that.log.createBlankPage.call(that.log, "TimeSheet", - that.log.generateTimeSheet); - evt.stopPropagation(); - evt.preventDefault(); - }, false); - - let clearLogsUI = this.doc.createElement("li"); - clearLogsUI.innerHTML = "\u00A0-\u00A0<a href='#' id='clearLogs'>" - + "Clear logs</a>"; - additionalButtons.appendChild(clearLogsUI); - let clearLogAElem = this.doc.getElementById("clearLogs"); - clearLogAElem.addEventListener("click", function() { - that.log.store = {}; - jetpack.storage.simple.sync(); - this.style.color = that.log.EmptyLogsColor; - this.style.fontWeight = "normal"; - console.log("this.store wiped out!"); - }, false); - - if (this.log.store.length > 0) { - clearLogAElem.style.color = this.log.FullLogsColor; - clearLogAElem.style.fontWeight = "bolder"; - } else { - clearLogAElem.style.color = this.log.EmptyLogsColor; - clearLogAElem.style.fontWeight = "normal"; - } -}; - -/** - * 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(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() { - let CCListSelect = this.doc.getElementById("cc"); - outCCList = []; - if (CCListSelect) { - outCCList = Array.map(CCListSelect.options, function(item) { - return item.value; - }); - } - return outCCList; -}; +//exports.BZPage = apiUtils.publicConstructor(BZPage); +exports.BZPage = BZPage;
\ No newline at end of file diff --git a/lib/main.js b/lib/main.js index ac2fd04..cb803f8 100644 --- a/lib/main.js +++ b/lib/main.js @@ -13,126 +13,48 @@ // "use strict"; var util = require("util"); -var logger = require("logger"); var file = require("file"); - -// FIXME this apparently is not good, properties of the object -// itself must be set, not variable referring to it var myStorage = require("simple-storage").storage; -// FIXME we should follow private browsing state -/* -simpleStorage.storage.history = []; -var privateBrowsing = require("private-browsing"); -if (!privateBrowsing.active) { - var url = getSelectedTabURL(); - simpleStorage.storage.history.push(url); -} - -*/ - -var TriagedDistro = 13; -var NumberOfFrames = 7; -var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; -var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; - -// ============================================================== -// https://wiki.mozilla.org/Labs/Jetpack/JEP/24 -var manifest = { - settings : [ - { - name : "BZpassword", - type : "password", - label : "Bugzilla password" - }, - { - name : "JSONURL", - type : "text", - label : "Configuration file URL", - "default" : "http://mcepl.fedorapeople.org/scripts/BugZappers_data.json" - }, - { - name : "enabledPacks", - type : "text", - label : "comment packs which should be enabled", - "default" : "" - } - ] -}; -jetpack.future.import("storage.settings"); +var browser = require("tab-browser"); +var JSONURL = "http://matej.ceplovi.cz/progs/data/RH_Data-packages.json"; +var config = {}; -// TODO we should have an array SpecialFlags instead of multiple Boolean -// variables -var logSubmits = false; -var chipIDsGroupings = []; -var AddrArray = []; -var topRow = {}; -var bottomRow = {}; +var matches = [ + "https://bugzilla.redhat.com/show_bug.cgi.*", + "https://bugzilla.mozilla.org/show_bug.cgi.*" +]; -// ///////////////////////////////////////////////////////////////////////////// -function doReplace(window) { - for (var node in require("text-node").iterator(window.document.body)) { - var origText = node.textContent; - var newText = origText.replace("friend", "acquaintance", "g"); - newText = newText.replace("Friend", "Acquaintance", "g"); - if (newText != origText) { - node.textContent = newText; - } - } +function initialize(callback) { + util.loadJSON(JSONURL, function(parsedData) { + config = {}; + callback(config); + }, this); } -//////////////////////////////////////////////////////////////////////////////// -let config = {}; -config.matches = [ - "https://bugzilla.redhat.com/show_bug.cgi", - "https://bugzilla.mozilla.org/show_bug.cgi" -]; -util.loadJSON(jetpack.storage.settings.JSONURL, function(parsedData) { - config.gJSONData = parsedData; - - // Get card translation table - let keys = ""; - for (let key in config.gJSONData) { - keys += key + " "; +function isOurPage(window, matchingURLs) { + if ("window" in window) { + window = window.window; } - console.log("keys = " + keys); + var url = window.location.href; - if ("PCIIDsURL" in config.gJSONData.configData) { - util.loadJSON(config.gJSONData.configData.PCIIDsURL, function(response) { - config.PCI_ID_Array = response; - }); - } - - if (!myStorage.logs) { - console.log("No store for logs defined!"); - myStorage.logs = {}; - } - - config.logger = new logger.Logger(myStorage.logs, - config.gJSONData.constantData.bugzillalabelAbbreviations); - - let callback = function(doc) { - if (config.gJSONData.configData.objectStyle = "RH") { - let curPage = new RHBugzillaPage(doc); - } else if (config.gJSONData.configData.objectStyle = "MoFo") { - let curPage = new MozillaBugzilla(doc); - } - }; - - jetpack.pageMods.add(callback, config); -}, this); - -//////////////////////////////////////////////////////////////// + return matchingURLs.some(function (element,i,a) { + return new RegExp(element).test(url); + }); +} -// FIXME What are the real values of options and callbacks parameters? exports.main = function main(options, callbacks) { - require("tab-browser").whenContentLoaded( + initialize(function (config) { + browser.whenContentLoaded( function(window) { - if (window.location.protocol == "http:" && - // options.matches - window.location.host.match(/facebook.com$/)) - require("persistent-page-mod").register(window, doReplace); - } - ); -};s
\ No newline at end of file + var doc = window.document; + var construct = require("rhbzpage").RHBugzillaPage; + if (isOurPage(window, matches)) { + var curPage = new construct(doc, config); + } else { + console.log("Not our page: " + window.location.href); + } + }); + }); +}; diff --git a/lib/puvodni/bzpage.js b/lib/puvodni/bzpage.js new file mode 100644 index 0000000..12b2fb0 --- /dev/null +++ b/lib/puvodni/bzpage.js @@ -0,0 +1,729 @@ +/*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 util = require("util"); +var simpleStorage = require("simple-storage"); +var Color = require("color").Color; + +var TriagedDistro = 13; +var NumberOfFrames = 7; +var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; +var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; + +// ==================================================================================== +// BZPage's methods + +var BZPage = exports.BZPage = function BZPage(doc, config) { + console.log("doc = " + doc); + console.log("config = " + 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 + this.doc = doc; + console.log("this.doc = " + this.doc); + + this.submitHandlerInstalled = false; + this.bugNo = util.getBugNo(this.doc.location.toString()); + + var keys = ""; + for (var key in this.prototype) { + keys += key + ", "; + } + console.log("keys = " + keys); + + this.reporter = this.getReporter(); + this.product = this.getOptionValue("product"); + this.component = this.getOptionValue("component"); + this.version = this.getVersion(); + this.title = this.doc.getElementById("short_desc_nonedit_display").textContent; + this.CCList = this.getCCList(); + + this.packages = this.getInstalledPackages(); + + if ("commentStrings" in config.gJSONData) { + this.commentStrings = config.gJSONData.commentStrings; + } + + if ("constantData" in config.gJSONData) { + // this is wrong, I shouldn't bother anybody with my Xorg data, and + // I should load it from URL + // var stuff = JSON.parse(self.data.load("chiIDsGroupings.json")); + this.constantData = config.gJSONData.constantData; + } + + if ("CCmaintainer" in config.gJSONData.constantData) { + this.defBugzillaMaintainerArr = config.gJSONData.constantData.CCmaintainer; + } + + if ("submitsLogging" in config.gJSONData.configData && + config.gJSONData.configData.submitsLogging) { + this.log = config.logger; + this.setUpLogging(); + } + + this.generateButtons(); +}; + +/** + * + */ +BZPage.prototype.getInstalledPackages = function getInstalledPackages() { + var installedPackages = {}; + if (config.gJSONData && ("commentPackages" in config.gJSONData)) { + var enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/); + enabledPackages.forEach(function (pkg, idx, arr) { + if (pkg in config.gJSONData.commentPackages) { + installedPackages[pkg] = config.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; + // 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. + */ +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]); + } +}; + +/** + * Add XGL to the CC list + * + * @param evt Event which made this function active + * @return none + */ +BZPage.prototype.changeAssignee = function changeAssignee (newAssignee) { + var defAssigneeButton = null; + this.addToCCList(this.owner); + 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 () { + var value = ""; + var valueElement = that.doc.getElementById("comment_action"); + if (valueElement) { + value = valueElement.getAttribute("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); +}; + +/** + * 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.error("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); + + 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); + } +}; + +/** + * + */ +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; + } + } + } +}; + +/** + * Get the current email of the reporter of the bug. + * + * @return string + */ +BZPage.prototype.getReporter = function getReporter () { + return this.doc + .querySelector("#bz_show_bug_column_2 > table .vcard:first-of-type > a").textContent; +}; + +/** + * Get the current version of the Fedora release ... even if changed meanwhile + * by bug triager. + * + * @return string (integer for released Fedora, float for RHEL, rawhide) + */ +BZPage.prototype.getVersion = function getVersion () { + var verStr = this.getOptionValue("version").toLowerCase(); + var verNo = 0; + if (/rawhide/.test(verStr)) { + verNo = 999; + } else { + verNo = Number(verStr); + } + return verNo; +}; + +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; + this.commentsWalker(function(x) { + var email = x.getElementsByClassName("vcard")[0] + .getElementsByTagName("a")[0].textContent; + if (new RegExp(that.reporter).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 label on the <SELECT> element with given id. + * + * Also execute change HTMLEvent, so that the form behaves accordingly. + * + * @param id + * @param label + * @return none + * + * FIXME bugzilla-comments version has this signature: + * selectOption = function selectOption(select, value) { + var doc = select[0].ownerDocument; + select.val(value); + */ +BZPage.prototype.selectOption = function selectOption (id, label) { + var sel = this.doc.getElementById(id); + sel.value = label; + 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 (kwd.trim().indexOf(str) !== -1); +}; + +/** + * 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)); +}; + +/** + * + */ +BZPage.prototype.getOptionValue = function getOptionValue (id) { + // Some special bugs don't have version for example + var element = this.doc.getElementById(id); + if (element) { + return element.value; + } else { + console.log("Failed to find element with id = " + id); + return "#NA"; + } +}; + +/** + * 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 () { + var priorityParent = this.doc.querySelector("label[for~='target_milestone']") + .parentNode.parentNode.parentNode; + var assigneeAElement = priorityParent.querySelector("tr:nth-of-type(1) a.email"); + var assgineeHref = decodeURI(assigneeAElement.getAttribute("href")); + var email = assgineeHref.split(":")[1]; + return email; +}; + +/** + * 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; +}; + +/** + * 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; +}; + +/** + * 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; +}; + +/** + * 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 () { + if (jetpack.storage.settings.BZpassword) { + return jetpack.storage.settings.BZpassword; + } else { + var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] + .getService(Components.interfaces.nsIPromptService); + var password = { + value : "" + }; // default the password to pass + var check = { + value : true + }; // default the checkbox to true + var result = prompts.promptPassword(null, "Title", "Enter password:", + password, null, check); + // result is true if OK was pressed, false if cancel was pressed. + // password.value is + // set if OK was pressed. The checkbox is not displayed. + if (result) { + var passwordText = password.value; + jetpack.storage.settings.BZpassword = passwordText; + jetpack.storage.simple.sync(); + return passwordText; + } + } + return null; +}; + +/** + * + */ +BZPage.prototype.setUpLogging = function setUpLogging () { + // 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 + // FIXME we should merge in functionality of RHBugzillaPage.submitCallback + // and actually make it working + // Maybe rewriting whole offline capability into a separate object? + if (!this.submitHandlerInstalled) { + console.log("Installing submit callback!"); + this.doc.forms.namedItem("changeform").addEventListener("submit",function (evt) { + console.log("Submit callback!"); + var resp = that.log.addLogRecord(that); + console.log("resp = " + resp); + if (resp === null) { + console.log("Avoiding submitting!"); + // FIXME doesn't work ... still submitting' + evt.stopPropagation(); + evt.preventDefault(); + } + }, false); + this.submitHandlerInstalled = true; + } + + var generateTimeSheetUI = this.doc.createElement("li"); + generateTimeSheetUI.innerHTML = "\u00A0-\u00A0<a href='#' id='generateTSButton'>" + + "Generate timesheet</a>"; + additionalButtons.appendChild(generateTimeSheetUI); + this.doc.getElementById("generateTSButton").addEventListener( + "click", + function(evt) { + that.log.createBlankPage.call(that.log, "TimeSheet", + that.log.generateTimeSheet); + evt.stopPropagation(); + evt.preventDefault(); + }, false); + + var clearLogsUI = this.doc.createElement("li"); + clearLogsUI.innerHTML = "\u00A0-\u00A0<a href='#' id='clearLogs'>" + + "Clear logs</a>"; + additionalButtons.appendChild(clearLogsUI); + var clearLogAElem = this.doc.getElementById("clearLogs"); + clearLogAElem.addEventListener("click", function() { + that.log.store = {}; + jetpack.storage.simple.sync(); + this.style.color = that.log.EmptyLogsColor; + this.style.fontWeight = "normal"; + console.log("this.store wiped out!"); + }, false); + + if (!this.log.store) { + console.log("No this.store defined!"); + this.log.store = {}; + } + + if (this.log.store.length > 0) { + clearLogAElem.style.color = this.log.FullLogsColor; + clearLogAElem.style.fontWeight = "bolder"; + } else { + clearLogAElem.style.color = this.log.EmptyLogsColor; + clearLogAElem.style.fontWeight = "normal"; + } +}; + +/** + * 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"); + outCCList = []; + if (CCListSelect) { + outCCList = Array.map(CCListSelect.options, function(item) { + return item.value; + }); + } + return outCCList; +}; diff --git a/lib/clipboard.js b/lib/puvodni/clipboard.js index b87c477..0051a55 100644 --- a/lib/clipboard.js +++ b/lib/puvodni/clipboard.js @@ -6,12 +6,8 @@ * @return string with the content of the clipboard or "" if empty. * originally from * https://developer.mozilla.org/en/Using_the_Clipboard - * + * https://wiki.mozilla.org/Labs/Jetpack/JEP/10 */ -// TODO to-be-replaced by -// var contents = jetpack.clipboard.get(); -// http://hsivonen.iki.fi/kesakoodi/clipboard/ -// https://wiki.mozilla.org/Labs/Jetpack/JEP/10 function getClipboard() { var clip = Cc["@mozilla.org/widget/clipboard;1"]. @@ -103,12 +99,7 @@ var setMethod = exports.set = function setMethod(content, flavor) { return true; }; -function createSupportsWString() { - return Cc["@mozilla.org/supports-wstring;1"]. - createInstance(Ci.nsISupportsWString); -} - -var flavorsMethod = exports.getCurrentFlavors = function flavorsMethod() { +var flavorsMethod = exports.getCurrentFlavors = function flavorsMethod(test) { // currently the only possible flavors in Jetpack-prototype are "plain" and // "html", i.e., "text/plain" (or text/unicode?) and "text/html" (or // application/xml+xhtml?) @@ -117,13 +108,18 @@ var flavorsMethod = exports.getCurrentFlavors = function flavorsMethod() { "text/plain": "plain", "text/html": "html" }; - var flavourArray = []; - - for (mime in possibleTypes) { - var kSuppString = createSupportsWString(); - kSuppString.data = mime; - // FIXME Missing some kind of if (mime in flavors) ... - flavourArray.add(possibleTypes[mime]); + var flavorArray = []; + var clip = getClipboard(); + + for (var flavor in possibleTypes) { + var presentFlavor = clip.hasDataMatchingFlavors( + [flavor], + 1, + clip.kGlobalClipboard + ); + if (presentFlavor) { + flavorArray.push(possibleTypes[flavor]) + } } - return flavourArray; -}; + return flavorArray; +};
\ No newline at end of file diff --git a/lib/color.js b/lib/puvodni/color.js index 2da2fa7..2da2fa7 100644 --- a/lib/color.js +++ b/lib/puvodni/color.js diff --git a/lib/logger.js b/lib/puvodni/logger.js index 8481f66..cc3f213 100644 --- a/lib/logger.js +++ b/lib/puvodni/logger.js @@ -5,8 +5,6 @@ var urlMod = require("url"); var urilMod = require("util"); var Logger = exports.Logger = function Logger(store, abbsMap) { - this.EmptyLogsColor = new Color(0, 255, 0); - this.FullLogsColor = new Color(0, 40, 103); this.store = store; this.abbsMap = abbsMap; @@ -25,21 +23,18 @@ Logger.prototype.addLogRecord = function(that) { let recKey = utilMod.getISODate(rec.date) + "+" + urlMod.parse(rec.url).host + "+" + that.bugNo; - console.log("rec = " + rec.toSource()); - let clearLogAElem = that.doc.getElementById("clearLogs"); - if (clearLogAElem.style.color != this.FullLogsColor) { - clearLogAElem.style.color = this.FullLogsColor; - clearLogAElem.style.fontWeight = "bolder"; - } + clearLogAElem.style.fontWeight = "bolder"; if (this.store[recKey]) { this.store[recKey].comment += "<br/>\n" + comment; } else { this.store[recKey] = rec; } jetpack.storage.simple.sync(); + return rec; + } else { + return comment; } - return comment; }; Logger.prototype.getBugzillaAbbr = function(url) { diff --git a/lib/mozillabzpage.js b/lib/puvodni/mozillabzpage.js index 6eaa869..7efaf16 100644 --- a/lib/mozillabzpage.js +++ b/lib/puvodni/mozillabzpage.js @@ -6,8 +6,8 @@ var utilMod = require("util"); // ============================================================================ // MozillaBugzilla object -exports.MozillaBugzilla = function MozillaBugzilla (doc) { - BZPage.call(this, doc) +exports.MozillaBugzilla = function MozillaBugzilla (doc, config) { + BZPage.call(this, doc, config) }; MozillaBugzilla.prototype = utilMod.heir(BZPage); diff --git a/lib/offline-support.js b/lib/puvodni/offline-support.js index 4849bd3..4849bd3 100644 --- a/lib/offline-support.js +++ b/lib/puvodni/offline-support.js diff --git a/lib/puvodni/old-main.js b/lib/puvodni/old-main.js new file mode 100644 index 0000000..db81448 --- /dev/null +++ b/lib/puvodni/old-main.js @@ -0,0 +1,106 @@ +/*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 +// +// Links to read through +// http://ehsanakhgari.org/blog/2010-01-07/bugzilla-tweaks-enhanced +// http://hg.mozilla.org/users/ehsan.akhgari_gmail.com/extensions/file/tip/bugzillatweaks +// http://hg.mozilla.org/users/ehsan.akhgari_gmail.com/extensions/file/ecfa0f028b81/bugzillatweaks/lib/main.js +// http://hg.mozilla.org/users/avarma_mozilla.com/atul-packages/file/42ac1e99a107/packages\ +// /facebook-acquaintances/lib/main.js#l11 +// http://ehsanakhgari.org/blog/2010-05-31/my-experience-jetpack-sdk#comment-1253 +// +"use strict"; +var util = require("util"); +var logger = require("logger"); +var file = require("file"); +var myStorage = require("simple-storage").storage; + +var TriagedDistro = 13; +var NumberOfFrames = 7; +var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; + +let config = {}; +config.matches = [ + "https://bugzilla.redhat.com/show_bug.cgi", + "https://bugzilla.mozilla.org/show_bug.cgi" +]; + + +// ============================================================== +// https://wiki.mozilla.org/Labs/Jetpack/JEP/24 +var manifest = { + settings : [ + { + name : "BZpassword", + type : "password", + label : "Bugzilla password" + }, + { + name : "JSONURL", + type : "text", + label : "Configuration file URL", + "default" : "http://mcepl.fedorapeople.org/scripts/BugZappers_data.json" + }, + { + name : "enabledPacks", + type : "text", + label : "comment packs which should be enabled", + "default" : "" + } + ] +}; +jetpack.future.import("storage.settings"); + +// ///////////////////////////////////////////////////////////////////////////// +function isOurPage(window) { + if ("window" in window) { + window = window.window; + } + + if (window.location.protocol == "https:") { + // like ["name1": "url1", "name2":"url2"] + // FIXME the real name of bugzillaPageModLocation array + for (var loc in bugzillaPageModLocation) { + if (bugzillaPageModLocation[loc].test(window.location.href)) { + return true; + } + } + } + // we haven't found a conforming bugzilla + return false; +} + + +function initialize() { + +} + +util.loadJSON(jetpack.storage.settings.JSONURL, function(parsedData) { + config.gJSONData = parsedData; + + // Get card translation table + let keys = ""; + for (let key in config.gJSONData) { + keys += key + " "; + } + if ("PCIIDsURL" in config.gJSONData.configData) { + util.loadJSON(config.gJSONData.configData.PCIIDsURL, function(response) { + config.PCI_ID_Array = response; + }); + } + + config.logger = new logger.Logger(myStorage.logs, + config.gJSONData.constantData.bugzillalabelAbbreviations); + + let callback = function(doc) { + if (config.gJSONData.configData.objectStyle = "RH") { + let curPage = new RHBugzillaPage(doc); + } else if (config.gJSONData.configData.objectStyle = "MoFo") { + let curPage = new MozillaBugzilla(doc); + } + }; + + jetpack.pageMods.add(callback, config); +}, this); diff --git a/lib/puvodni/rhbzpage.js b/lib/puvodni/rhbzpage.js new file mode 100644 index 0000000..5acc1d3 --- /dev/null +++ b/lib/puvodni/rhbzpage.js @@ -0,0 +1,941 @@ +/*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 util = require("util"); +var xrpc = require("xmlrpc"); +var xhr = require("xhr"); +var clip = require("clipboard"); +var Color = require("color").Color; +var BZPage = require("bzpage").BZPage; +// 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 + +var RHBugzillaPage = exports.RHBugzillaPage = function RHBugzillaPage(doc, config) { + // inheritance ... call superobject's constructor + BZPage.call(this, doc, config); + + // For identification of graphics card + var 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") + }; + // END OF CONSTANTS + + var that = this; + this.reqCounter = 0; + this.signaturesCounter = 0; + this.chipMagicInterestingLine = ""; + + this.login = this.getLogin(); + this.password = this.getPassword(); + + var ITbutton = this.doc.getElementById("cf_issuetracker"); + this.its = ITbutton ? ITbutton.value.trim() : ""; + + // Prepare for query buttons + // FIXME getting null for commentArea sometimes + var commentArea = this.doc.getElementById("comment_status_commit"); + if (commentArea) { + var brElementPlacer = commentArea.getElementsByTagName("br"); + console.log("brElementPlacer.length = " + brElementPlacer.length); + brElementPlacer = brElementPlacer[0]; + if (brElementPlacer) { + brElementPlacer.setAttribute("id","brElementPlacer_location"); + brElementPlacer.parentNode.insertBefore(this.doc.createElement("br"), + brElementPlacer); + } + } + + // 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 = ""; + + var 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) { + var 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 + var whiteboard_string = this.doc.getElementById("status_whiteboard").value; + if (!/card_/.test(whiteboard_string)) { + this.fillInChipMagic(); + } + } +}; // END OF RHBugzillaPage CONSTRUCTOR + +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.component).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 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 + var verNo = this.getVersion(); + this.selectOption("bug_status", "CLOSED"); + var text = ""; + var 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) { + console.log("cmdLabel = " + cmdLabel + ", cmdParams = " + 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": + 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; + } +}; + + +/* 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? + 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'))) { + var 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) { + var btRaw = util.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() { + var badMIMEArray = [ "application/octet-stream", "text/x-log", "undefined" ]; + + var badAttachments = this.attachments.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"; + 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() { + var prod = this.product; + var 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() { + var brandColor = {}; + var 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 + 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.textContent = "*"+ccEditBoxElem.textContent; + ccEditBoxElem.style.color = "navy"; + ccEditBoxElem.style.fontWeight = "bolder"; + ccEditBoxElem.style.textDecoration = "underline"; + } + + // mark suspicious components + var compElems; + var suspiciousComponents = config.gJSONData.configData.suspiciousComponents; + if (suspiciousComponents + && util.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) { + var that = this; + + function groupIDs(manStr, cardStrID) { + var outStr = util.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) { + var soughtID = (manufacturerNo + "," + cardNo).toUpperCase(); + var outList = config.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 = 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 () { + var that = this; + var XorgLogURL = ""; + var XorgLogAttID = ""; + var XorgLogFound = false; + var attURL = "", interestingLine = ""; + var 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; + + // parse Xorg.0.log + util.loadText(attURL, function(ret){ + var interestingLineArr = ret.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.chipMagicInterestingLine = interestingLine+"\t"+interestingArray[1] + .toUpperCase(); + that.createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); + } + }); + 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); + var 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()); + 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; + 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() { + var text = jetpack.selection.text; + console.log("selection = " + text); + if (!text) { + text = clip.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() { + console.log("Querying upstream!"); + var text = jetpack.selection.text; + console.log("Selection = |" + text + "|"); + if (!text) { + text = clip.get(); + } + if (text) { + text = encodeURIComponent(text.trim()); + var queryUpstreamBugsURLArray = this.constantData.queryUpstreamBug; + var url = util.filterByRegexp(queryUpstreamBugsURLArray, this.component); + jetpack.tabs.open(url + text); + } +}; + +/** + * + */ +RHBugzillaPage.prototype.sendBugUpstream = function() { + var url = util.filterByRegexp(this.constantData.newUpstreamBug, this + .getOptionValue("component")); + + var ret = jetpack.tabs.open(url); + var that = this; + jetpack.tabs.onReady(function() { + var otherDoc = ret.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(); + 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) { + var MIMEtype = ""; + var size = 0; + + // Skip over obsolete attachments + if (inElem.getElementsByClassName("bz_obsolete").length > 0) { + return ( []); + } + + // getting name of the attachment + 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 ]; +}; + +/** + * 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) { + var 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) { + var 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; + } + + 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); + + var req = new XMLHttpRequest(); + var 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; + } + var that = this; + var 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) { + var that = this; + var elemS = row[4].getElementsByTagName("td"); + var 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() { + 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.*/)) { + var helpAElem = this.doc.createElement("a"); + wholeURL = inputBox.value; + helpAElem.setAttribute("href", wholeURL); + var paramsArr = helpAElem.search.replace(/^\?/, '').split('&'); + // get convert URL parameters to an Object + var 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 + var bugzillaName = this.getBugzillaName(helpAElem.hostname); + this.selectOption("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? + } + + // 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)) { + 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.getVersion(); + var assignee = this.getOwner(); + if ((!this.isEnterprise()) && (ver < TriagedDistro)) { + 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; +}; diff --git a/lib/skip-process-bug.js b/lib/puvodni/skip-process-bug.js index 3f82578..3f82578 100644 --- a/lib/skip-process-bug.js +++ b/lib/puvodni/skip-process-bug.js diff --git a/lib/xmlrpc.js b/lib/puvodni/xmlrpc.js index 69bb77e..69bb77e 100644 --- a/lib/xmlrpc.js +++ b/lib/puvodni/xmlrpc.js diff --git a/lib/rhbzpage.js b/lib/rhbzpage.js index 9f8a39f..21d0546 100644 --- a/lib/rhbzpage.js +++ b/lib/rhbzpage.js @@ -4,940 +4,34 @@ // http://www.opensource.org/licenses/mit-license.php "use strict"; var util = require("util"); -var xrpc = require("xmlrpc"); -var xhr = require("xhr"); -var clip = require("clipboard"); -var selection = require("selection"); -// var TriagedDistro = 13; -// var NumberOfFrames = 7; -// var XMLRPCurl = "https://bugzilla.redhat.com/xmlrpc.cgi"; -// var bugURL = "https://bugzilla.redhat.com/show_bug.cgi?id="; +var apiUtils = require("api-utils"); +var BZPage = require("bzpage").BZPage; // ==================================================================================== // 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 - - 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") - }; - - // 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); - } - +var RHBugzillaPage = function RHBugzillaPage(doc, config) { // 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); - } + BZPage.call(this, doc, config); - 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 = 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.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 = ""; + console.log("location = " + this.doc.location); - 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) { - console.log("cmdLabel = " + cmdLabel + ", cmdParams = " + cmdParams); - switch (cmdLabel) { - // Set up our own commands - case "closeUpstream": - this.addClosingUpstream(); - break; - case "computeResolution": - this.closeSomeRelease(); - break; - case "queryStringOurBugzilla": - this.queryForSelection(); - break; - case "queryStringUpstreamBugzilla": - 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; - } -}; - - -/* Bugzilla functions. */ + //this.bugId = this.getBugId(); + this.bugId = util.getBugNo(this.doc.location.href); + console.log("doc = " + this.doc.location); + console.log("bug number = " + this.bugId); + console.log("bug URL = " + this.getURL()); + this.bugId = this.getBugId(); + console.log("bug# = " + this.getBugId()); + console.log("Now we are outside!"); +}; // END OF RHBugzillaPage CONSTRUCTOR -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 = util.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 (util.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"); - } +RHBugzillaPage.prototype.toString = function toString () { + return ("[Object RHBugzillaPage]"); }; -/** - * 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 (util.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 - && util.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 = util.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 that = this; - 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; - - // parse Xorg.0.log - util.loadText(attURL, function(ret){ - let interestingLineArr = ret.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.chipMagicInterestingLine = interestingLine+"\t"+interestingArray[1] - .toUpperCase(); - that.createNewButton("status_whiteboard", true, "rh-xorg", "chipMagic"); - } - }); - 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.getSelectionOrClipboard = function() { - var text = selection.text; - console.log("selection = " + text); - if (!text) { - text = clip.get(); - console.log("clipboard = " + text); - } - console.log("text = " + text); - return text; -}; - -/** - * Get the text to search for and prepare other things for the real executive - * function this.queryInNewTab, and run it. - */ -RHBugzillaPage.prototype.queryForSelection = function() { - var text = this.getSelectionOrClipboard(); - if (text) { - this.queryInNewTab(text, this.component); - } -}; - -/** - * Search simple query in the upstream bugzilla appropriate for the component. - */ -RHBugzillaPage.prototype.queryUpstream = function() { - var text = this.getSelectionOrClipboard(); - if (text) { - text = encodeURIComponent(text.trim()); - let queryUpstreamBugsURLArray = this.constantData.queryUpstreamBug; - let url = util.filterByRegexp(queryUpstreamBugsURLArray, this.component); - jetpack.tabs.open(url + text); - } -} - -/** - * - */ -RHBugzillaPage.prototype.sendBugUpstream = function() { - var url = util.filterByRegexp(this.constantData.newUpstreamBug, - this.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 xrpc.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++; - } +RHBugzillaPage.prototype = util.heir(BZPage); +RHBugzillaPage.prototype.constructor = RHBugzillaPage; - 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; -}; +//exports.RHBugzillaPage = apiUtils.publicConstructor(RHBugzillaPage); +exports.RHBugzillaPage = RHBugzillaPage;
\ No newline at end of file diff --git a/lib/util.js b/lib/util.js index 3ce50c5..e14fcb1 100644 --- a/lib/util.js +++ b/lib/util.js @@ -30,123 +30,17 @@ var urlMod = require("url"); */ exports.heir = function heir(p) { function f() {}; - f.prototype = p.prototype; + f.prototype = p.prototype; return new f(); }; -/** - * 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); -}; - -/** - * 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 - */ -exports.filterByRegexp = function filterByRegexp(list, chosingMark) { - let 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 ""; - } -}; - -/** - * format date to be in ISO format (just day part) - * - * @param date - * @return string with the formatted date - */ -exports.getISODate = function getISODate(dateStr) { - function pad(n) { - return n < 10 ? '0' + n : n; - } - var date = new Date(dateStr); - return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + - pad(date.getDate()); -}; - -/** - * 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(", "); -}; -/** - * - */ var getBugNo = exports.getBugNo = function getBugNo(url) { var re = new RegExp(".*id=([0-9]+).*$"); var bugNo = null; + if (!url) { + throw new Error("Missing URL value!"); + } var reResult = re.exec(url); if (reResult[1]) { bugNo = reResult[1]; diff --git a/package.json b/package.json index 5c8fe4d..96db7e7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { + "id": "jid0-uXmbeWgOltUUuqrHKhrR7hW3IQY", "dependencies": [ "jetpack-core" ], "description": "Help for triage on bugzilla", - "author": "Matěj Cepl (http://matej.ceplovi.cz)" + "author": "Mat\u011bj Cepl (http://matej.ceplovi.cz)" } diff --git a/tests/test-clipboard.js b/tests/test-clipboard.js index 56cfeb8..917376d 100644 --- a/tests/test-clipboard.js +++ b/tests/test-clipboard.js @@ -40,3 +40,12 @@ exports.ensureClipboardUnicode = function ensureClipboard(test) { test.assertEqual(text, pushkinTestString, "checking set and get clipboard methods (Unicode)"); }; + +exports.ensureClipboardGetFlavors = function ensureClipboardGetFlavors(test) { + clip.set(pushkinTestString); + var available = clip.getCurrentFlavors(); + test.assertEqual(JSON.stringify(available),'["plain"]', + "checking getFlavors method"); +}; + +// TODO: test for some weird mimeType which is not supported |