Bug 573392 - Port |Bug 522761 Need option 'keep folders scheme' under the archive feature to don't forget the messages organization| to SeaMonkey. Also fixes Bug 494266 - [IMAP] Archive subfolders not shown in folder pane directly after their creation. r=Mnyromyr sr=Neil
authorJens Hatlak <jh@junetz.de>
Mon, 28 Jun 2010 19:06:53 +0200
changeset 5905 74ccec25e4a9a7c5817b368657ec18c208a5df9a
parent 5904 b016d4eabd861b33412843e348868da117a2f852
child 5906 30d7f2e796c0ceff2db4b0bbc7105bf4ea4aa261
push idunknown
push userunknown
push dateunknown
reviewersMnyromyr, Neil
bugs573392, 522761, 494266
Bug 573392 - Port |Bug 522761 Need option 'keep folders scheme' under the archive feature to don't forget the messages organization| to SeaMonkey. Also fixes Bug 494266 - [IMAP] Archive subfolders not shown in folder pane directly after their creation. r=Mnyromyr sr=Neil
suite/mailnews/mail3PaneWindowCommands.js
suite/mailnews/mailWindowOverlay.js
--- a/suite/mailnews/mail3PaneWindowCommands.js
+++ b/suite/mailnews/mail3PaneWindowCommands.js
@@ -342,17 +342,28 @@ var DefaultController =
         }
         return false;
       case "cmd_printSetup":
         return true;
       case "cmd_markAsFlagged":
       case "button_file":
       case "cmd_file":
       case "cmd_archive":
-        return (GetNumSelectedMessages() > 0);
+        let selectedMessages = gFolderDisplay.selectedMessages;
+        if (selectedMessages.length == 0)
+          return false;
+        // If the server of the first message's folder is configured to keep
+        // the folder structure on archiving, check that we're not in the Archive
+        // folder or below it. Otherwise allow re-archiving, e.g. in case the
+        // user switched from monthly to yearly archiving.
+        if (selectedMessages[0].folder && selectedMessages[0].folder.server &&
+            selectedMessages[0].folder.server.archiveKeepFolderStructure)
+          return gFolderDisplay.displayedFolder && !gFolderDisplay.displayedFolder
+            .isSpecialFolder(Components.interfaces.nsMsgFolderFlags.Archive, true);
+        return true;
       case "cmd_markAsJunk":
       case "cmd_markAsNotJunk":
         if (gDBView)
           gDBView.getCommandStatus(nsMsgViewCommandType.junk, enabled, checkStatus);
         return enabled.value;
       case "cmd_recalculateJunkScore":
         // We're going to take a conservative position here, because we really
         // don't want people running junk controls on folders that are not
--- a/suite/mailnews/mailWindowOverlay.js
+++ b/suite/mailnews/mailWindowOverlay.js
@@ -26,16 +26,17 @@
  *   Håkan Waara <hwaara@chello.se>
  *   Jan Varga <varga@ku.sk>
  *   Seth Spitzer <sspitzer@netscape.com>
  *   David Bienvenu <bienvenu@netscape.com>
  *   Ian Neal <iann_bugzilla@blueyonder.co.uk>
  *   Karsten Düsterloh <mnyromyr@tprac.de>
  *   Christopher Thomas <cst@yecc.com>
  *   Jeremy Morton <bugzilla@game-point.net>
+ *   Jens Hatlak <jh@junetz.de>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either of 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
@@ -1169,16 +1170,18 @@ function MsgReplyToSenderAndGroup(aEvent
 
 
 // Message Archive function
 
 function BatchMessageMover()
 {
   this._batches = {};
   this._currentKey = null;
+  this._dstFolderParent = null;
+  this._dstFolderName = null;
 }
 
 BatchMessageMover.prototype =
 {
   archiveMessages: function(aMsgHdrs)
   {
     if (!aMsgHdrs.length)
       return;
@@ -1189,49 +1192,61 @@ BatchMessageMover.prototype =
     // processNextBatch.
     SetNextMessageAfterDelete();
     this.messageToSelectAfterWereDone = gNextMessageViewIndexAfterDelete;
     gNextMessageViewIndexAfterDelete = -2;
 
     for (let i = 0; i < aMsgHdrs.length; ++i)
     {
       let msgHdr = aMsgHdrs[i];
+      let server = msgHdr.folder.server;
       let msgDate = new Date(msgHdr.date / 1000);  // convert date to JS date object
       let msgYear = msgDate.getFullYear().toString();
-      let dstFolderName = msgDate.toLocaleFormat("%Y-%m");
-      let copyBatchKey = msgHdr.folder.URI + '\000' + dstFolderName;
+      let monthFolderName = msgDate.toLocaleFormat("%Y-%m");
+
+      // RSS servers don't have an identity so we special case the archives URI.
+      let archiveFolderUri;
+      if (server.type == 'rss')
+        archiveFolderUri = server.serverURI + "/Archives";
+      else
+        archiveFolderUri = GetIdentityForHeader(msgHdr,
+          Components.interfaces.nsIMsgCompType.ReplyAll).archiveFolder;
+      let archiveFolder = GetMsgFolderFromUri(archiveFolderUri, false);
+      let afServer = archiveFolder.server;
+
+      let copyBatchKey = msgHdr.folder.URI + '\000' + monthFolderName;
       if (!(copyBatchKey in this._batches))
-        this._batches[copyBatchKey] = [msgHdr.folder, msgYear, dstFolderName];
+        this._batches[copyBatchKey] = [msgHdr.folder,
+                                       archiveFolderUri,
+                                       afServer.archiveGranularity,
+                                       afServer.archiveKeepFolderStructure,
+                                       msgYear,
+                                       monthFolderName];
       this._batches[copyBatchKey].push(msgHdr);
     }
+
+    let notificationService = Components.classes["@mozilla.org/messenger/msgnotificationservice;1"]
+                                        .getService(Components.interfaces.nsIMsgFolderNotificationService);
+    notificationService.addListener(this, notificationService.folderAdded);
+
     // Now we launch the code iterating over all message copies, one in turn.
     this.processNextBatch();
   },
 
   processNextBatch: function()
   {
     for (let key in this._batches)
     {
       this._currentKey = key;
       let batch = this._batches[key];
-      let srcFolder = batch[0];
-      let msgYear = batch[1];
-      let msgMonth = batch[2];
-      let msgs = batch.slice(3);
-      // RSS servers don't have an identity so we special case the archives URI.
-      let archiveFolderUri;
-      if (srcFolder.server.type == 'rss')
-        archiveFolderUri = srcFolder.server.serverURI + "/Archives";
-      else
-        archiveFolderUri = GetIdentityForHeader(msgs[0],
-          Components.interfaces.nsIMsgCompType.ReplyAll).archiveFolder;
+      let [srcFolder, archiveFolderUri, granularity, keepFolderStructure, msgYear, msgMonth] = batch;
+      let msgs = batch.slice(6);
 
       let archiveFolder = GetMsgFolderFromUri(archiveFolderUri, false);
       let dstFolder = archiveFolder;
-      let granularity = archiveFolder.server.archiveGranularity;
       // For imap folders, we need to create the sub-folders asynchronously,
       // so we chain the urls using the listener called back from 
       // createStorageIfMissing. For local, createStorageIfMissing is
       // synchronous.
       let isImap = archiveFolder.server.type == "imap";
       if (!archiveFolder.parent)
       {
         archiveFolder.setFlag(Components.interfaces.nsMsgFolderFlags.Archive);
@@ -1258,30 +1273,75 @@ BatchMessageMover.prototype =
         dstFolder = GetMsgFolderFromUri(archiveFolderUri, false);
         if (!dstFolder.parent)
         {
           dstFolder.createStorageIfMissing(this);
           if (isImap)
             return;
         }
       }
+
+      // Create the folder structure in Archives
+      // For imap folders, we need to create the sub-folders asynchronously,
+      // so we chain the actions using the listener called back from 
+      // createSubfolder. For local, createSubfolder is synchronous.
+      if (archiveFolder.canCreateSubfolders && keepFolderStructure)
+      {
+        // Collect in-order list of folders of source folder structure,
+        // excluding top-level INBOX folder
+        let folderNames = [];
+        let rootFolder = srcFolder.server.rootFolder;
+        let inboxFolder = GetInboxFolder(srcFolder.server);
+        let folder = srcFolder;
+        while (folder != rootFolder && folder != inboxFolder)
+        {
+          folderNames.unshift(folder.name);
+          folder = folder.parent;
+        }
+        // Determine Archive folder structure
+        for (let i = 0; i < folderNames.length; ++i)
+        {
+          let folderName = folderNames[i];
+          if (!dstFolder.containsChildNamed(folderName))
+          {
+            // Create Archive sub-folder (IMAP: async) 
+            if (isImap)
+            {
+              this._dstFolderParent = dstFolder;
+              this._dstFolderName = folderName;
+            }
+            dstFolder.createSubfolder(folderName, msgWindow);
+            if (isImap)
+              return;
+          }
+          dstFolder = dstFolder.getChildNamed(folderName);
+        }
+      }
+
       if (dstFolder != srcFolder)
       {
+        // Make sure the target folder is visible in the folder tree.
+        EnsureFolderIndex(GetFolderTree().builderView, dstFolder);
+
         let array = Components.classes["@mozilla.org/array;1"]
                               .createInstance(Components.interfaces.nsIMutableArray);
         msgs.forEach(function(item){array.appendElement(item, false);});
         // If the source folder doesn't support deleting messages, we
         // make archive a copy, not a move.
         gCopyService.CopyMessages(srcFolder, array, dstFolder,
                                   srcFolder.canDeleteMessages, this, msgWindow, true);
         return; // only do one.
       }
       delete this._batches[key];
     }
 
+    Components.classes["@mozilla.org/messenger/msgnotificationservice;1"]
+              .getService(Components.interfaces.nsIMsgFolderNotificationService)
+              .removeListener(this);
+
     // We're just going to select the message now.
     let treeView = gDBView.QueryInterface(Components.interfaces.nsITreeView);
     treeView.selection.select(this.messageToSelectAfterWereDone);
     treeView.selectionChanged();
   },
 
   // This also implements nsIUrlListener, but we only care about the
   // OnStopRunningUrl (createStorageIfMissing callback).
@@ -1321,20 +1381,35 @@ BatchMessageMover.prototype =
       this.processNextBatch();
     }
     else
     {
       this._batches = null;
     }
   },
 
+  // This also implements nsIMsgFolderListener, but we only care about the
+  // folderAdded (createSubfolder callback).
+  folderAdded: function(aFolder)
+  {
+    // Check that this is the folder we're interested in.
+    if (aFolder.parent == this._dstFolderParent &&
+        aFolder.name == this._dstFolderName)
+    {
+      this._dstFolderParent = null;
+      this._dstFolderName = null;
+      this.processNextBatch();
+    }
+  },
+
   QueryInterface: function(aIID)
   {
     if (aIID.equals(Components.interfaces.nsIUrlListener) ||
         aIID.equals(Components.interfaces.nsIMsgCopyServiceListener) ||
+        aIID.equals(Components.interfaces.nsIMsgFolderListener) ||
         aIID.equals(Components.interfaces.nsISupports))
       return this;
     throw Components.results.NS_ERROR_NO_INTERFACE;
   }
 }
 
 function MsgArchiveSelectedMessages(aEvent)
 {