From aa07b2290640061a65c9f90d5f30cfc5d4ada195 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Tue, 08 Nov 2011 06:22:14 -0500
Subject: [PATCH] - Fix so folders with \Noinferiors attribute aren't listed in parent selector - Add LIST result and folder attributes cache - rcmail_render_folder_tree_select(): fix 'exceptions' parameter, add 'skip_noinferiors' option

---
 CHANGELOG                              |    1 
 program/steps/settings/edit_folder.inc |    3 
 program/steps/settings/folders.inc     |    4 
 program/include/main.inc               |   50 ++++++++++------
 program/include/rcube_imap.php         |   69 ++++++++++++++--------
 program/include/rcube_imap_generic.php |   12 +--
 6 files changed, 84 insertions(+), 55 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 0e72c41..ffd8498 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Fix so folders with \Noinferiors attribute aren't listed in parent selector
 - Fix handling of curly brackets in URLs (#1488168)
 - Fix handling of dates (birthday/anniversary) in contact data (#1488147)
 - Fix error on opening searched LDAP contact (#1488144)
diff --git a/program/include/main.inc b/program/include/main.inc
index 7e31c01..95d422d 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -1232,7 +1232,7 @@
   if ($p['noselection'])
     $select->add($p['noselection'], '');
 
-  rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p['exceptions']);
+  rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p);
 
   return $select;
 }
@@ -1281,9 +1281,9 @@
   $path .= $prefix.$currentFolder;
 
   if (!isset($arrFolders[$currentFolder])) {
-    // Check \Noselect option (if options are in cache)
-    if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) {
-      $virtual = in_array('\\Noselect', $opts);
+    // Check \Noselect attribute (if attributes are in cache)
+    if (!$virtual && ($attrs = $RCMAIL->imap->mailbox_attributes($path))) {
+      $virtual = in_array('\\Noselect', $attrs);
     }
 
     $arrFolders[$currentFolder] = array(
@@ -1402,30 +1402,40 @@
  * @access private
  * @return string
  */
-function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $exceptions=array())
+function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $opts=array())
 {
+  global $RCMAIL;
+
   $out = '';
 
   foreach ($arrFolders as $key => $folder) {
-    if (empty($exceptions) || !in_array($folder['id'], $exceptions)) {
-      if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
-        $foldername = rcube_label($folder_class);
-      else {
-        $foldername = $folder['name'];
-
-        // shorten the folder name to a given length
-        if ($maxlength && $maxlength>1)
-          $foldername = abbreviate_string($foldername, $maxlength);
-      }
-
-      $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
-    }
-    else if ($nestLevel)
+    // skip exceptions (and its subfolders)
+    if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) {
       continue;
+    }
+
+    // skip folders in which it isn't possible to create subfolders
+    if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->imap->mailbox_attributes($folder['id']))
+        && in_array('\\Noinferiors', $attrs)
+    ) {
+      continue;
+    }
+
+    if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id'])))
+      $foldername = rcube_label($folder_class);
+    else {
+      $foldername = $folder['name'];
+
+      // shorten the folder name to a given length
+      if ($maxlength && $maxlength>1)
+        $foldername = abbreviate_string($foldername, $maxlength);
+    }
+
+    $select->add(str_repeat('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
 
     if (!empty($folder['folders']))
       $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength,
-        $select, $realnames, $nestLevel+1, $exceptions);
+        $select, $realnames, $nestLevel+1, $opts);
   }
 
   return $out;
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 43d3590..7508acd 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -3087,7 +3087,19 @@
      */
     function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false)
     {
-        // @TODO: caching
+        $cache_key = $root.':'.$name;
+        if (!empty($filter)) {
+            $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter));
+        }
+        $cache_key .= ':'.$rights;
+        $cache_key = 'mailboxes.list.'.md5($cache_key);
+
+        // get cached folder list
+        $a_mboxes = $this->get_cache($cache_key);
+        if (is_array($a_mboxes)) {
+            return $a_mboxes;
+        }
+
         // Give plugins a chance to provide a list of mailboxes
         $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list',
             array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST'));
@@ -3109,6 +3121,11 @@
             array_unshift($a_mboxes, 'INBOX');
         }
 
+        // cache folder attributes
+        if ($root == '' && $name == '*' && empty($filter)) {
+            $this->update_cache('mailboxes.attributes', $this->conn->data['LIST']);
+        }
+
         // filter folders list according to rights requirements
         if ($rights && $this->get_capability('ACL')) {
             $a_folders = $this->filter_rights($a_folders, $rights);
@@ -3118,6 +3135,9 @@
         if (!$skip_sort) {
             $a_mboxes = $this->_sort_mailbox_list($a_mboxes);
         }
+
+        // write mailboxlist to cache
+        $this->update_cache($cache_key, $a_mboxes);
 
         return $a_mboxes;
     }
@@ -3450,30 +3470,29 @@
 
 
     /**
-     * Gets folder options from LIST response, e.g. \Noselect, \Noinferiors
+     * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors
      *
      * @param string $mailbox Folder name
-     * @param bool   $force   Set to True if options should be refreshed
-     *                        Options are available after LIST command only
+     * @param bool   $force   Set to True if attributes should be refreshed
      *
      * @return array Options list
      */
-    function mailbox_options($mailbox, $force=false)
+    function mailbox_attributes($mailbox, $force=false)
     {
-        if ($mailbox == 'INBOX') {
-            return array();
+        // get attributes directly from LIST command
+        if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$mailbox])) {
+            $opts = $this->conn->data['LIST'][$mailbox];
+        }
+        // get cached folder attributes
+        else if (!$force) {
+            $opts = $this->get_cache('mailboxes.attributes');
+            $opts = $opts[$mailbox];
         }
 
-        if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mailbox])) {
-            if ($force) {
-                $this->conn->listMailboxes('', $mailbox);
-            }
-            else {
-                return array();
-            }
+        if (!is_array($opts)) {
+            $this->conn->listMailboxes('', $mailbox);
+            $opts = $this->conn->data['LIST'][$mailbox];
         }
-
-        $opts = $this->conn->data['LIST'][$mailbox];
 
         return is_array($opts) ? $opts : array();
     }
@@ -3556,17 +3575,17 @@
             }
         }
 
-        $options['name']      = $mailbox;
-        $options['options']   = $this->mailbox_options($mailbox, true);
-        $options['namespace'] = $this->mailbox_namespace($mailbox);
-        $options['rights']    = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array();
-        $options['special']   = in_array($mailbox, $this->default_folders);
+        $options['name']       = $mailbox;
+        $options['attributes'] = $this->mailbox_attributes($mailbox, true);
+        $options['namespace']  = $this->mailbox_namespace($mailbox);
+        $options['rights']     = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array();
+        $options['special']    = in_array($mailbox, $this->default_folders);
 
         // Set 'noselect' and 'norename' flags
-        if (is_array($options['options'])) {
-            foreach ($options['options'] as $opt) {
-                $opt = strtolower($opt);
-                if ($opt == '\noselect' || $opt == '\nonexistent') {
+        if (is_array($options['attributes'])) {
+            foreach ($options['attributes'] as $attrib) {
+                $attrib = strtolower($attrib);
+                if ($attrib == '\noselect' || $attrib == '\nonexistent') {
                     $options['noselect'] = true;
                 }
             }
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index 5c7a41c..f200e17 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -2258,13 +2258,11 @@
                     }
 
                     // Add to options array
-                    if (!empty($opts)) {
-                        if (empty($this->data['LIST'][$mailbox]))
-                            $this->data['LIST'][$mailbox] = $opts;
-                        else
-                            $this->data['LIST'][$mailbox] = array_unique(array_merge(
-                                $this->data['LIST'][$mailbox], $opts));
-                    }
+                    if (empty($this->data['LIST'][$mailbox]))
+                        $this->data['LIST'][$mailbox] = $opts;
+                    else if (!empty($opts))
+                        $this->data['LIST'][$mailbox] = array_unique(array_merge(
+                            $this->data['LIST'][$mailbox], $opts));
                 }
                 // * STATUS <mailbox> (<result>)
                 else if ($cmd == 'STATUS') {
diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc
index 36a4b28..e9566f7 100644
--- a/program/steps/settings/edit_folder.inc
+++ b/program/steps/settings/edit_folder.inc
@@ -119,7 +119,8 @@
             'realnames'   => false,
             'maxlength'   => 150,
             'unsubscribed' => true,
-            'exceptions' => array($mbox_imap),
+            'skip_noinferiors' => true,
+            'exceptions'  => array($mbox_imap),
         ));
 
         $form['props']['fieldsets']['location']['content']['path'] = array(
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 206f62c..77cbb55 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -283,8 +283,8 @@
         }
 
         if (!$protected) {
-            $opts = $IMAP->mailbox_options($folder['id']);
-            $noselect = in_array('\\Noselect', $opts);
+            $attrs = $IMAP->mailbox_attributes($folder['id']);
+            $noselect = in_array('\\Noselect', $attrs);
         }
 
         $disabled = (($protected && $subscribed) || $noselect);

--
Gitblit v1.9.1