Load folders on-demand for DIMP sidebar.
authorMichael M Slusarz <slusarz@curecanti.org>
Fri, 4 Dec 2009 17:28:25 +0000 (10:28 -0700)
committerMichael M Slusarz <slusarz@curecanti.org>
Tue, 8 Dec 2009 02:07:44 +0000 (19:07 -0700)
imp/ajax.php
imp/docs/CHANGES
imp/js/DimpBase.js
imp/lib/Imap/Tree.php

index d169414..0c33dc0 100644 (file)
@@ -315,7 +315,37 @@ case 'ListFolders':
     if (Horde_Util::getPost('unsub')) {
         $mask |= IMP_Imap_Tree::FLIST_UNSUB;
     }
-    $result = IMP_Dimp::getFolderResponse($imptree, array('a' => $imptree->folderList($mask), 'c' => array(), 'd' => array()));
+
+    if (!Horde_Util::getPost('all')) {
+        $mask |= IMP_Imap_Tree::FLIST_NOCHILDREN;
+        if (Horde_Util::getPost('initial') || Horde_Util::getPost('reload')) {
+            $mask |= IMP_Imap_Tree::FLIST_ANCESTORS | IMP_Imap_Tree::FLIST_SAMELEVEL;
+        }
+    }
+
+    $folder_list = array();
+    foreach (Horde_Serialize::unserialize($mbox, Horde_Serialize::JSON) as $val) {
+        $folder_list += $imptree->folderList($mask, $val);
+    }
+
+    /* Add special folders explicitly to the initial folder list, since they
+     * are ALWAYS displayed and may appear outside of the folder slice
+     * requested. */
+    if (Horde_Util::getPost('initial')) {
+        foreach ($imptree->getSpecialMailboxes() as $val) {
+            if (!is_array($val)) {
+                $val = array($val);
+            }
+
+            foreach ($val as $val2) {
+                if (!isset($folder_list[$val2])) {
+                    $folder_list[$val2] = $imptree->element($val2);
+                }
+            }
+        }
+    }
+
+    $result = IMP_Dimp::getFolderResponse($imptree, array('a' => array_values($folder_list), 'c' => array(), 'd' => array()));
 
     $quota = _getQuota();
     if (!is_null($quota)) {
index ddcbc9f..e5141b5 100644 (file)
@@ -2,6 +2,7 @@
 v5.0-git
 --------
 
+[mms] Load folders on-demand in sidebar (DIMP).
 [mms] Add priority setting to DIMP.
 [mms] Simplify and improve priority header generation/display.
 [mms] Add Face: header support to IMP.
index 3009b9b..ea504c3 100644 (file)
@@ -731,9 +731,7 @@ var DimpBase = {
 
         case 'ctx_folderopts_expand':
         case 'ctx_folderopts_collapse':
-            $('normalfolders').select('LI.folder').each(function(f) {
-                this._toggleSubFolder(f, id == 'ctx_folderopts_expand' ? 'exp' : 'col', true);
-            }, this);
+            this._toggleSubFolder($('normalfolders'), id == 'ctx_folderopts_expand' ? 'expall' : 'colall', true);
             break;
 
         case 'ctx_folderopts_reload':
@@ -744,10 +742,7 @@ var DimpBase = {
         case 'ctx_container_collapse':
         case 'ctx_folder_expand':
         case 'ctx_folder_collapse':
-            tmp = baseelt.up('LI');
-            [ tmp, $(this.getSubFolderId(tmp.readAttribute('id'))).select('LI.folder') ].flatten().each(function(f) {
-                this._toggleSubFolder(f, (id == 'ctx_container_expand' || id == 'ctx_folder_expand') ? 'exp' : 'col', true);
-            }, this);
+            this._toggleSubFolder(baseelt.up('LI').next(), (id == 'ctx_container_expand' || id == 'ctx_folder_expand') ? 'expall' : 'colall', true);
             break;
 
         case 'ctx_message_spam':
@@ -2060,10 +2055,14 @@ var DimpBase = {
         }
     },
 
-    _folderLoadCallback: function(r)
+    _folderLoadCallback: function(r, callback)
     {
         this._folderCallback(r);
 
+        if (callback) {
+            callback();
+        }
+
         var nf = $('normalfolders'),
             nfheight = nf.getStyle('max-height');
 
@@ -2118,17 +2117,48 @@ var DimpBase = {
 
     _toggleSubFolder: function(base, mode, noeffect)
     {
-        // Make sure all subfolders are expanded.
-        // The last 2 elements of ancestors() are the BODY and HTML tags -
-        // don't need to parse through them.
-        var subs = (mode == 'exp')
-            ? base.ancestors().slice(0, -2).reverse().findAll(function(n) { return n.hasClassName('subfolders'); })
-            : [ base.next('.subfolders') ];
+        var need = [], subs = [];
 
-        subs.compact().each(function(s) {
+        if (mode == 'expall' || mode == 'colall') {
+            if (base.hasClassName('subfolders')) {
+                subs.push(base);
+            }
+            subs = subs.concat(base.select('.subfolders'));
+        } else if (mode == 'exp') {
+            // If we are explicitly expanding ('exp'), make sure all parent
+            // subfolders are expanded.
+            // The last 2 elements of ancestors() are the BODY and HTML tags -
+            // don't need to parse through them.
+            subs = base.ancestors().slice(0, -2).reverse().findAll(function(n) { return n.hasClassName('subfolders'); });
+        } else {
+            subs = [ base.next('.subfolders') ];
+        }
+
+        if (!subs) {
+            return;
+        }
+
+        if (mode == 'tog' || mode == 'expall') {
+            subs.compact().each(function(s) {
+                if (!s.visible() && !s.down().childElements().size()) {
+                    need.push(s.previous().retrieve('mbox'));
+                }
+            });
+
+            if (need.size()) {
+                this._listFolders({
+                    all: Number(mode == 'expall'),
+                    callback: this._toggleSubFolder.bind(this, base, mode, noeffect),
+                    view: need
+                });
+                return;
+            }
+        }
+
+        subs.each(function(s) {
             if (mode == 'tog' ||
-                (mode == 'exp' && !s.visible()) ||
-                (mode == 'col' && s.visible())) {
+                ((mode == 'exp' || mode == 'expall') && !s.visible()) ||
+                ((mode == 'col' || mode == 'colall') && s.visible())) {
                 s.previous().down().toggleClassName('exp').toggleClassName('col');
 
                 if (noeffect) {
@@ -2143,7 +2173,28 @@ var DimpBase = {
                     });
                 }
             }
-        }, this);
+        });
+    },
+
+    _listFolders: function(params)
+    {
+        var cback;
+
+        params = params || {};
+        params.unsub = Number(this.showunsub);
+        if (!Object.isArray(params.view)) {
+            params.view = [ params.view ];
+        }
+        params.view = params.view.toJSON();
+
+        if (params.callback) {
+            cback = function(func, r) { this._folderLoadCallback(r, func); }.bind(this, params.callback);
+            delete params.callback;
+        } else {
+            cback = this._folderLoadCallback.bind(this);
+        }
+
+        DimpCore.doAction('ListFolders', params, { callback: cback });
     },
 
     // Folder actions.
@@ -2159,6 +2210,10 @@ var DimpBase = {
             submbox = $(submboxid),
             title = ob.t || ob.m;
 
+        if ($(fid)) {
+            return;
+        }
+
         if (ob.v) {
             ftype = ob.co ? 'scontainer' : 'virtual';
             title = label;
@@ -2356,7 +2411,7 @@ var DimpBase = {
             this.deleteFolderElt(elt.readAttribute('id'), true);
         }, this);
 
-        DimpCore.doAction('ListFolders', { unsub: Number(this.showunsub) }, { callback: this._folderLoadCallback.bind(this) });
+        this._listFolders({ reload: 1, view: this.folder });
     },
 
     subscribeFolder: function(f, sub)
@@ -2631,10 +2686,6 @@ var DimpBase = {
 
         $('dimpmain').setStyle({ left: ($('sidebar').clientWidth + this.splitbar.clientWidth) + 'px' });
 
-        /* Create the folder list. Any pending notifications will be caught
-         * via the return from this call. */
-        DimpCore.doAction('ListFolders', {}, { callback: this._folderLoadCallback.bind(this) });
-
         /* Init quicksearch. These needs to occur before loading the message
          * list since it may be disabled if we are in a search mailbox. */
         if ($('qsearch')) {
@@ -2651,6 +2702,7 @@ var DimpBase = {
         if (!tmp.empty() && tmp.startsWith('#')) {
             tmp = (tmp.length == 1) ? "" : tmp.substring(1);
         }
+
         if (!tmp.empty()) {
             this.go(unescape(tmp));
         } else if (DIMP.conf.login_view == 'inbox') {
@@ -2660,6 +2712,10 @@ var DimpBase = {
             this.loadMailbox('INBOX', { background: true });
         }
 
+        /* Create the folder list. Any pending notifications will be caught
+         * via the return from this call. */
+        this._listFolders({ initial: 1, view: this.folder} );
+
         this._setQsearchText(true);
 
         /* Add popdown menus. Check for disabled compose at the same time. */
index e0945f9..d1db492 100644 (file)
@@ -52,6 +52,8 @@ class IMP_Imap_Tree
     const FLIST_OB = 8;
     const FLIST_ELT = 16;
     const FLIST_NOCHILDREN = 32;
+    const FLIST_ANCESTORS = 64;
+    const FLIST_SAMELEVEL = 128;
 
     /* Add null to folder key since it allows us to sort by name but
      * never conflict with an IMAP mailbox. */
@@ -1713,15 +1715,19 @@ class IMP_Imap_Tree
      * IMP_Imap_Tree::FLIST_OB - Return full tree object.
      * IMP_Imap_Tree::FLIST_ELT - Return element object.
      * IMP_Imap_Tree::FLIST_NOCHILDREN - Don't show child elements.
+     * IMP_Imap_Tree::FLIST_ANCESTORS - Include ancestors.
+     * IMP_Imap_Tree::FLIST_SAMELEVEL - Also return mailboxes at the same
+     *                                  level as $base.
      * </pre>
      * @param string $base  Return all mailboxes below this element.
      *
      * @return array  Either an array of IMAP mailbox names or an array of
-     *                objects (if FLIST_OB ot FLIST_ELT is specified).
+     *                objects (if FLIST_OB ot FLIST_ELT is specified). Keys
+     *                are the mailbox name.
      */
     public function folderList($mask = 0, $base = null)
     {
-        $baseindex = null;
+        $baseelt = $baseindex = null;
         $ret_array = array();
 
         $diff_unsub = (($mask & self::FLIST_UNSUB) != $this->_showunsub)
@@ -1733,14 +1739,28 @@ class IMP_Imap_Tree
 
         // Search to base element.
         if (!is_null($base)) {
-            while ($mailbox && $mailbox['v'] != $base) {
+            while ($mailbox && strcasecmp($base, $mailbox['v']) !== 0) {
                 $mailbox = $this->next(self::NEXT_SHOWCLOSED);
             }
+
             if ($mailbox) {
                 $baseindex = count($this->_currstack);
-                $baseparent = $this->_currparent;
-                $basekey = $this->_currkey;
-                $mailbox = $this->next(self::NEXT_SHOWCLOSED);
+
+                if ($mask & self::FLIST_SAMELEVEL) {
+                    --$baseindex;
+                    if ($baseindex >= 0) {
+                        $basekey = $this->_currstack[$baseindex]['k'];
+                        $baseparent = $this->_currstack[$baseindex]['p'];
+                        $baseelt = $mailbox = $this->_tree[$this->_parent[$this->_currparent][0]];
+                    } else {
+                        $mailbox = $this->reset();
+                    }
+                    $this->_currkey = 0;
+                } else {
+                    $basekey = $this->_currkey;
+                    $baseparent = $this->_currparent;
+                    $mailbox = $this->next(self::NEXT_SHOWCLOSED);
+                }
             }
         }
 
@@ -1752,6 +1772,7 @@ class IMP_Imap_Tree
         if ($mailbox) {
             do {
                 if (!is_null($baseindex) &&
+                    ($baseindex >= 0) &&
                     (!isset($this->_currstack[$baseindex]) ||
                      ($this->_currstack[$baseindex]['k'] != $basekey) ||
                      ($this->_currstack[$baseindex]['p'] != $baseparent))) {
@@ -1762,7 +1783,7 @@ class IMP_Imap_Tree
                      !$this->isContainer($mailbox)) &&
                     (($mask & self::FLIST_VFOLDER) ||
                      !$this->isVFolder($mailbox))) {
-                    $ret_array[] = ($mask & self::FLIST_OB)
+                    $ret_array[$mailbox['v']] = ($mask & self::FLIST_OB)
                         ? $mailbox
                         : (($mask & self::FLIST_ELT) ? $this->element($mailbox['v']) : $mailbox['v']);
                 }
@@ -1773,7 +1794,9 @@ class IMP_Imap_Tree
             $this->showUnsubscribed($diff_unsub);
         }
 
-        return $ret_array;
+        return (!is_null($baseelt) && ($mask & self::FLIST_ANCESTORS))
+            ? array_merge($this->folderList($mask, $baseelt['p']), $ret_array)
+            : $ret_array;
     }
 
     /**