Initial work.
authorShawn Wilsher <me@shawnwilsher.com>
Fri, 09 Oct 2009 11:45:58 -0700
changeset 0 42721a0437390536a92d37dcacdc66e8acbc7759
child 1 344c797df89c409529dfadc0403d08b25b5f92d1
push id1
push usersdwilsh@shawnwilsher.com
push dateFri, 09 Oct 2009 18:52:49 +0000
Initial work.
chrome.manifest
content/overlay.js
content/overlay.xul
content/reply.css
content/reply.js
content/reply.xul
install.rdf
new file mode 100644
--- /dev/null
+++ b/chrome.manifest
@@ -0,0 +1,2 @@
+overlay chrome://messenger/content/messenger.xul chrome://bugzilla-reply/content/overlay.xul
+content bugzilla-reply content/
new file mode 100644
--- /dev/null
+++ b/content/overlay.js
@@ -0,0 +1,146 @@
+var BugzillaReply = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// Constants
+
+  kFromAddress: "bugzilla-daemon@mozilla.org",
+  kSubjectRegEx: /^\[Bug \d+\]/,
+  kBugURIRegEx: /^https:\/\/bugzilla.mozilla.org\/show_bug.cgi\?id=(\d+)/,
+  kCommentStartRegEx: /^------- Comment #(\d+) from .+ -------\s+$/,
+  kCommentEndRegEx: /^--\s+$/,
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// Functions
+
+  initialize: function BR_initialize()
+  {
+    // Replace MsgReplySender with our own function only if it hasn't already
+    // happened.
+    if (MsgReplySender !== BugzillaReply.MsgReplySender) {
+      this.oldMsgReplySender = MsgReplySender;
+      MsgReplySender = BugzillaReply.MsgReplySender;
+    }
+  },
+
+  MsgReplySender: function BR_MsgReplySender(aEvent)
+  {
+    // Determine if this is a reply to a bugzilla message.  If it is, we'll
+    // intercept this!
+    let msgURIs = gFolderDisplay.selectedMessageUris;
+    // We only support replying to one message.
+    if (msgURIs.length == 1) {
+      let msgURI = msgURIs[0];
+      let header = messenger.msgHdrFromURI(msgURI);
+      if (header.author == BugzillaReply.kFromAddress &&
+          header.subject.match(BugzillaReply.kSubjectRegEx)) {
+        // We will now intercept the normal reply action here.
+
+        let account = header.recipients;
+        let body = BugzillaReply.getMessageBody(header);
+        let [bug, number, comment] = BugzillaReply.getComment(body);
+        if (comment) {
+          BugzillaReply.openReply(bug, number, comment);
+          return;
+        }
+      }
+    }
+
+    // We did not intercept this reply, so carry on like normal.
+    this.oldMsgReplySender(aEvent);
+  },
+
+  getMessageBody: function BR_getMessageBody(aMessageHeader)
+  {
+    let listener = Components.classes["@mozilla.org/network/sync-stream-listener;1"]
+                             .createInstance(Components.interfaces.nsISyncStreamListener);
+    let uri = aMessageHeader.folder.getUriForMsg(aMessageHeader);
+    messenger.messageServiceFromURI(uri)
+             .streamMessage(uri, listener, null, null, false, "");
+    let folder = aMessageHeader.folder;
+    return folder.getMsgTextFromStream(listener.inputStream,
+                                       aMessageHeader.Charset,
+                                       65536,
+                                       32768,
+                                       false,
+                                       true,
+                                       { });
+  },
+
+  getComment: function BR_getComment(aMessageBody)
+  {
+    let message = aMessageBody.replace("\r\n", "\n");
+
+    let lines = message.split("\n");
+    let foundURI = false;
+    let foundHeader = false;
+    let foundEnd = false;
+    let bug, commentNumber;
+    let commentLines = lines.filter(function(line) {
+      // If we have already found the end, we don't want this line.
+      if (foundEnd)
+        return false;
+
+      if (!foundURI) {
+        let match = BugzillaReply.kBugURIRegEx.exec(line);
+        if (!match)
+          return false;
+        bug = match[1];
+        foundURI = true;
+        return false;
+      }
+
+      // If we haven't found the header yet, check for it.
+      if (!foundHeader) {
+        let match = BugzillaReply.kCommentStartRegEx.exec(line);
+        if (!match)
+          return false;
+        commentNumber = match[1];
+        foundHeader = true;
+        return false;
+      }
+
+      // We have found our header, so make sure this isn't the end.
+      if (BugzillaReply.kCommentEndRegEx.test(line)) {
+        foundEnd = true;
+        return false;
+      }
+      return true;
+    });
+
+    if (commentLines.length == 0 || !foundURI || !foundHeader || !foundEnd)
+      return null;
+    return [bug, commentNumber, commentLines.join("\n").trimRight()];
+  },
+
+  openReply: function BR_openReplyWindow(aBugNumber,
+                                         aCommentNumber,
+                                         aCommentBody)
+  {
+    // Build our array of arguments.
+    let params = Components.classes["@mozilla.org/array;1"]
+                           .createInstance(Components.interfaces.nsIMutableArray);
+    let str = Components.Constructor("@mozilla.org/supports-string;1",
+                                     "nsISupportsString");
+    let args = [
+      aBugNumber,
+      aCommentNumber,
+      aCommentBody,
+    ];
+    args.forEach(function(arg) {
+      let item = new str;
+      item.data = arg;
+      params.appendElement(item, false);
+    });
+
+    // Open the window.
+    // TODO tab if they have the right prefs set
+    let ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
+                       .getService(Components.interfaces.nsIWindowWatcher);
+    ww.openWindow(window,
+                  "chrome://bugzilla-reply/content/reply.xul",
+                  null,
+                  "chrome,dialog=yes,resizable,centerscreen",
+                  params);
+  },
+};
+
+window.addEventListener("load", BugzillaReply.initialize, false);
new file mode 100644
--- /dev/null
+++ b/content/overlay.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<overlay id="bugzilla-helper-overlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="text/javascript"
+          src="chrome://bugzilla-reply/content/overlay.js"/>
+
+</overlay>
new file mode 100644
--- /dev/null
+++ b/content/reply.css
@@ -0,0 +1,3 @@
+dialog {
+  font-family:monospace;
+}
new file mode 100644
--- /dev/null
+++ b/content/reply.js
@@ -0,0 +1,42 @@
+////////////////////////////////////////////////////////////////////////////////
+//// Constants
+
+const kDestinationURI = "https://api-dev.bugzilla.mozilla.org/0.1/";
+
+////////////////////////////////////////////////////////////////////////////////
+//// Dialog Controller
+
+var ReplyDialog = {
+  initialize: function RD_initialize()
+  {
+    let label = document.getElementById("comment-label");
+    label.value = "Add a comment to bug " + window.arguments[0] + ":";
+    let comment = document.getElementById("comment");
+    let lines = ["(In reply to comment #" + window.arguments[1] +")"];
+    window.arguments[2].split("\n").forEach(function(line) {
+      lines.push("> " + line);
+    });
+    comment.value = lines.join("\n") + "\n";
+    comment.focus();
+  },
+
+  onAccept: function RD_onAccept()
+  {
+    // TODO handle offline!
+    alert("sumbitting the following comment:\n" + ReplyDialog.createJSON());
+    return true;
+  },
+
+  createJSON: function RD_createJSON()
+  {
+    let comment = document.getElementById("comment").value;
+    let private = document.getElementById("private").checked;
+    let data = {
+      is_private: private ? 1 : 0,
+      text: comment,
+    };
+    return JSON.stringify(data);
+  },
+};
+
+window.addEventListener("DOMContentLoaded", ReplyDialog.initialize, false);
new file mode 100644
--- /dev/null
+++ b/content/reply.xul
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet type="text/css"
+                 href="chrome://bugzilla-reply/content/reply.css"?>
+
+<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+
+<dialog id="bugzilla-reply-window"
+        buttons="accept,cancel"
+        buttonlabelaccept="Commit"
+        buttonlabelcancel="Cancel"
+        ondialogaccept="ReplyDialog.onAccept();"
+        height="600"
+        persist="width height screenX screenY"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <script type="text/javascript"
+          src="chrome://bugzilla-reply/content/reply.js"/>
+
+  <hbox>
+    <label id="comment-label"
+           for="comment"/>
+    <spacer flex="1"/>
+    <checkbox id="private"/>
+    <label id="private-label"
+           for="private"
+           value="Private"/>
+  </hbox>
+  <textbox id="comment"
+           rows="10"
+           cols="80"
+           multiline="true"/>
+</dialog>
new file mode 100644
--- /dev/null
+++ b/install.rdf
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>bugzilla-reply@forerunnerdesigns.com</em:id>
+    <em:name>Bugzilla Reply</em:name>
+    <em:version>0.1</em:version>
+    <em:type>2</em:type>
+    <em:creator>Shawn Wilsher</em:creator>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
+        <em:minVersion>3.0b1</em:minVersion>
+        <em:maxVersion>3.0.0.*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+  </Description>
+</RDF>