alecpl
2011-11-08 aa07b2290640061a65c9f90d5f30cfc5d4ada195
- 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


6 files modified
139 ■■■■■ changed files
CHANGELOG 1 ●●●● patch | view | raw | blame | history
program/include/main.inc 50 ●●●●● patch | view | raw | blame | history
program/include/rcube_imap.php 69 ●●●●● patch | view | raw | blame | history
program/include/rcube_imap_generic.php 12 ●●●●● patch | view | raw | blame | history
program/steps/settings/edit_folder.inc 3 ●●●● patch | view | raw | blame | history
program/steps/settings/folders.inc 4 ●●●● patch | view | raw | blame | history
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)
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(' ', $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(' ', $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;
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;
                }
            }
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') {
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(
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);