From 42c361a1f15e5a0552c7753ff105bc71bccae45d Mon Sep 17 00:00:00 2001 From: Michael M Slusarz Date: Fri, 4 Dec 2009 10:28:25 -0700 Subject: [PATCH] Load folders on-demand for DIMP sidebar. --- imp/ajax.php | 32 +++++++++++++++- imp/docs/CHANGES | 1 + imp/js/DimpBase.js | 102 ++++++++++++++++++++++++++++++++++++++------------ imp/lib/Imap/Tree.php | 39 +++++++++++++++---- 4 files changed, 142 insertions(+), 32 deletions(-) diff --git a/imp/ajax.php b/imp/ajax.php index d16941407..0c33dc0a0 100644 --- a/imp/ajax.php +++ b/imp/ajax.php @@ -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)) { diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index ddcbc9fe7..e5141b558 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -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. diff --git a/imp/js/DimpBase.js b/imp/js/DimpBase.js index 3009b9b86..ea504c391 100644 --- a/imp/js/DimpBase.js +++ b/imp/js/DimpBase.js @@ -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. */ diff --git a/imp/lib/Imap/Tree.php b/imp/lib/Imap/Tree.php index e0945f9f9..d1db49284 100644 --- a/imp/lib/Imap/Tree.php +++ b/imp/lib/Imap/Tree.php @@ -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. * * @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; } /** -- 2.11.0