aboutsummaryrefslogblamecommitdiffstats
path: root/bugzillaComments.js
blob: 6ae3e810436281ba74e2a2871159b57dcf72c2a7 (plain) (tree)
























































































































                                                                              
                                       





















                                                                                   
                                       














                                                                         
                                                                       





































































































































































                                                                                                                                                
                                                            















                                                                  
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Bugzilla Comments Jetpack code.
 *
 * The Initial Developer of the Original Code is
 *   Philipp Kewisch <mozilla@kewis.ch>
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Matěj Cepl <cepl@redhat.com>
 *   Mark Banner <bugzilla@standard8.plus.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

var manifest = {
  settings: [
    {
      name: "jsonURL",
      type: "text",
      label: "URL for Comments Pack"
    },
    {
      name: "enabledPacks",
      type: "text",
      label: "Enabled Comment Packages"
    }
  ]
};
jetpack.future.import("jetpack.storage.settings");
jetpack.future.import("jetpack.pageMods");

// Container for loaded package json data.
var gJSONData = null;

////////////////////////////////////////////////////////////////
// Helper functions
////////////////////////////////////////////////////////////////
var hlpr = {
    valToArray: function valToArray(val) {
      let arr = [];
      if (typeof val == "string") {
        arr = [val];
      } else if ($.isArray(val)) {
        arr = val;
      }
      return arr;
    },

    addCSVValue: function addCSVValue(str, value) {
      let parts = (str.trim().length > 0 ? str.split(",") : []);
      if (parts.indexOf(value) < 0) {
        parts.concat(hlpr.valToArray(value));
      }
      return parts.join(",");
    },

    removeCSVValue: function removeCSVValue(str, value) {
      let parts = (str.trim().length > 0 ? str.split(",") : []);
      for each (let val in hlpr.valToArray(value)) {
        let pos = parts.indexOf(val);
        if (pos > -1) {
          parts.splice(pos, 1);
        }
      }
      return parts.join(",");
    },

    selectOption: function selectOption(select, value) {
      let doc = select[0].ownerDocument;
      select.val(value);
      
      let e = doc.createEvent("HTMLEvents");
      e.initEvent("change", true, true);
      select[0].dispatchEvent(e);
    },

    clickElement: function clickElement(element) {
      let doc = element[0].ownerDocument;
      let e = doc.createEvent("MouseEvents");
      e.initMouseEvent("click", true, true, doc.defaultView,
                       0, 0, 0, 0, 0, false, false, false, false, 0, null);
      element[0].dispatchEvent(e);
    }
};

////////////////////////////////////////////////////////////////
// Basic Bugzilla modifications
////////////////////////////////////////////////////////////////

function BaseBugzilla(doc) {
  this.doc = doc;
  this.init();
}

BaseBugzilla.prototype = {
    doc: null,

    get: function installedPackages() {
      // TODO does this really need to by dynamic?
      let installedPackages = {};
      if (gJSONData && ("commentPackages" in gJSONData)) {
        let enabledPackages = jetpack.storage.settings.enabledPacks.split(/[, ]/);
        for each (let pkg in enabledPackages) {
          if (pkg in gJSONData.commentPackages) {
            installedPackages[pkg] = gJSONData.commentPackages[pkg];
          }
        }
      }
      return installedPackages;
    },

    init: function base_init() {
        this.initCommentsDropdown();
    },

    initCommentsDropdown: function base_initCommentsDropdown() {
        let ca = $("<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>", this.doc);
        $("#comments", this.doc).append(ca);

        let select = $("#comment_action", this.doc);

        let packages = this.installedPackages;

        for (let pkg in packages) {
          for (let commentId in packages[pkg]) {
            let comment = packages[pkg][commentId];
            let value = pkg + "//" + commentId;
            select.append($("<option/>", this.doc).attr({ value: value })
                                             .text(comment.name));
          }
        }
        let self = this;
        select.change(function() { self.onCommentsDropdownChange(); });
    },

    onCommentsDropdownChange: function onCommentsDropdownChange() {
        let value = $("select#comment_action", this.doc).attr("value");
        if (!value) {
          return;
        }
        let [pkg, id] = value.split("//");
        let commentObj = this.installedPackages[pkg][id];

        let commentField = $("#comment", this.doc);
        let keywordsInput = $("#keywords", this.doc);
        let whiteboardsInput = $("#status_whiteboard", this.doc);
        let dependson = $("#dependson", this.doc);
        let blocks = $("#blocked", this.doc);


        // TODO make these getters/setters on the bz object instead
        console.log("0" + $("#bug_status", this.doc));
        if ("status" in commentObj) hlpr.selectOption($("#bug_status", this.doc), commentObj.status);
        console.log("1");
        if ("resolution" in commentObj) hlpr.selectOption($("#resolution", this.doc), commentObj.resolution);
        console.log("2");

        if ("addKeyword" in commentObj)  keywordsInput.val(this.addKeyword(keywordsInput.val(), commentObj.addKeyword));
        if ("removeKeyword" in commentObj) keywordsInput.val(this.removeKeyword(keywordsInput.val(), commentObj.removeKeyword));
        if ("addWhiteboard" in commentObj) whiteboardsInput.val(this.addWhiteboard(whiteboardsInput.val(), commentObj.addWhiteboard));
        if ("removeWhiteboard" in commentObj)  whiteboardsInput.val(this.removeWhiteboard(whiteboardsInput.val(), commentObj.removeWhiteboard));
        if ("product" in commentObj)  hlpr.selectOption($("#product", this.doc), commentObj.product);
        if ("component" in commentObj)  hlpr.selectOption($("#component", this.doc), commentObj.component);
        if ("version" in commentObj) hlpr.selectOption($("#version", this.doc), commentObj.version);
        if ("platform" in commentObj) hlpr.selectOption($("#rep_platform", this.doc), commentObj.platform);
        if ("os" in commentObj) hlpr.selectOption($("#op_sys", this.doc), commentObj.os);

        if ("priority" in commentObj)  hlpr.selectOption($("#priority", this.doc), commentObj.priority);
        if ("severity" in commentObj)  hlpr.selectOption($("#bug_severity", this.doc), commentObj.severity);
        if ("target" in commentObj) hlpr.selectOption($("#target_milestone", this.doc), commentObj.target);

        if ("assignee" in commentObj) {
          hlpr.clickElement($("#bz_assignee_edit_action", this.doc));
          $("#assigned_to", this.doc).val(commentObj.assignee);
        }

        if ("qacontact" in commentObj) {
          hlpr.clickElement($("#bz_qa_contact_edit_action", this.doc));
          $("#qa_contact", this.doc).val(commentObj.qacontact);
        }

        if ("url" in commentObj) {
          hlpr.clickElement($("#bz_url_edit_action", this.doc));
          $("#bug_file_loc", this.doc).val(commentObj.url);
        }

        // TODO dependson/blocked doesn't work. Find out why.
        if ("addDependsOn" in commentObj) {
          hlpr.clickElement($("#dependson_edit_action", this.doc));
          dependson.val(hlpr.addCSVValue(dependson.val(), commentObj.addDependsOn));
        }
        if ("removeDependsOn" in commentObj) {
          hlpr.clickElement($("#dependson_edit_action", this.doc));
          dependson.val(hlpr.addCSVValue(dependson.val(), commentObj.removeDependsOn));
        }

        if ("addBlocks" in commentObj) {
          hlpr.clickElement($("#blocked_edit_action", this.doc));
          blocks.val(hlpr.addCSVValue(blocks.val(), commentObj.addBlocks));
        }
        if ("removeBlocks" in commentObj) {
          hlpr.clickElement($("#blocked_edit_action", this.doc));
          blocks.val(hlpr.removeCSVValue(blocks.val(), commentObj.removeBlocks));
        }

        if ("comment" in commentObj) commentField.val(commentField.val() + commentObj.comment);


        // TODO cclist, flags, see also

        if (("commit" in commentObj) && commentObj.commit) {
          // Directly commit the form
          hlpr.clickElement($("#commit"));
        }
    },

    addKeyword: function addKeyword(str, keyword) {
      return hlpr.addCSVValue(str, keyword);
    },

    removeKeyword: function removeKeyword(str, keyword) {
      return hlpr.removeCSVValue(str, keyword);
    },

    addWhiteboard: function addWhiteboard(str, wbFlag) {
      return hlpr.addCSVValue(str, wbFlag);
    },

    removeWhiteboard: function removeWhiteboard(str, wbFlag) {
      return hlpr.removeCSVValue(str, wbFlag);
    }
};

////////////////////////////////////////////////////////////////
// Mozilla Specific Bugzilla modifications
////////////////////////////////////////////////////////////////

function MozillaBugzilla(doc) {
    BaseBugzilla.apply(this, arguments);
}

MozillaBugzilla.prototype = {
    __proto__: BaseBugzilla.prototype,

    addWhiteboard: function(str, wbFlagVal) {
      // At least calendar uses [foo] for whiteboard flags, do this differently for moz
      // TODO make this based on current component, if needed.
      for each (let wbFlag in hlpr.valToArray(wbFlagVal)) {
        let fullWbFlag = "[" + wbFlag + "]";
        if (str.indexOf(fullWbFlag) < 0) {
          str += fullWbFlag;
        }
      }
      return str;
    },

    removeWhiteboard: function removeWhiteboard(str, wbFlagVal) {
      for each (let wbFlag in hlpr.valToArray(wbFlagVal)) {
        let fullWbFlag = "[" + wbFlag + "]";
        str = str.replace(fullWbFlag, "");
      }
      return str;
    }
};

////////////////////////////////////////////////////////////////
// Redhat Specific Bugzilla modifications
////////////////////////////////////////////////////////////////
function RedhatBugzilla(doc) {
    BaseBugzilla.apply(this, arguments);
}

RedhatBugzilla.prototype = {
  __proto__: BaseBugzilla

  // TODO integrate mcepl's code
};

////////////////////////////////////////////////////////////////
// Initialize the JSON Data on each jetpack reload
////////////////////////////////////////////////////////////////
(function initJSONData() {
  // Load json file into gJSONData
  if (jetpack.storage.settings.jsonURL) {
    let req = new XMLHttpRequest();
    req.open("GET", jetpack.storage.settings.jsonURL, false);
    req.overrideMimeType("application/json");
    req.send(null);
    if (req.status == 200 || req.status == 0) {
      gJSONData = JSON.parse(req.responseText);
    }
  }
})();

////////////////////////////////////////////////////////////////
// Initialize page modifications
////////////////////////////////////////////////////////////////
const bzMap = {
  "https://bugzilla.mozilla.org/show_bug.cgi": MozillaBugzilla,
  "https://bugzilla.redhat.com/show_bug.cgi": RedhatBugzilla
};

function figureOutBugzilla(doc) {
  // We need the url without the query params, strip them.
  let href = doc.location.href;
  href = href.substr(0, href.length - doc.location.search.length);

  // Now check if we have a custom implementation
  let impl = (href in bzMap ? bzMap[href] : BaseBugzilla);

  // Instanciate, this starts modifying the page.
  new impl(doc);
}

jetpack.pageMods.add(figureOutBugzilla,
                     { matches: [ uri for (uri in bzMap) ] });