Aleksander Machniak
2014-07-16 3cb61e7528c2a8544083bf14e02ea4b9387671fb
Collapsible (and iconized) folders tree in folder manager (#1489648)
16 files modified
1306 ■■■■ changed files
program/js/app.js 306 ●●●● patch | view | raw | blame | history
program/js/treelist.js 36 ●●●●● patch | view | raw | blame | history
program/steps/settings/edit_folder.inc 36 ●●●● patch | view | raw | blame | history
program/steps/settings/folders.inc 114 ●●●●● patch | view | raw | blame | history
program/steps/settings/func.inc 17 ●●●●● patch | view | raw | blame | history
program/steps/settings/save_folder.inc 10 ●●●●● patch | view | raw | blame | history
skins/classic/common.css 154 ●●●●● patch | view | raw | blame | history
skins/classic/mail.css 146 ●●●●● patch | view | raw | blame | history
skins/classic/settings.css 30 ●●●● patch | view | raw | blame | history
skins/classic/templates/folders.html 2 ●●● patch | view | raw | blame | history
skins/classic/templates/mail.html 2 ●●● patch | view | raw | blame | history
skins/larry/mail.css 218 ●●●●● patch | view | raw | blame | history
skins/larry/settings.css 13 ●●●● patch | view | raw | blame | history
skins/larry/styles.css 218 ●●●●● patch | view | raw | blame | history
skins/larry/templates/folders.html 2 ●●● patch | view | raw | blame | history
skins/larry/templates/mail.html 2 ●●● patch | view | raw | blame | history
program/js/app.js
@@ -544,7 +544,7 @@
    // select first input field in an edit form
    if (this.gui_objects.editform)
      $("input,select,textarea", this.gui_objects.editform)
        .not(':hidden').not(':disabled').first().select();
        .not(':hidden').not(':disabled').first().select().focus();
    // unset contentframe variable if preview_pane is enabled
    if (this.env.contentframe && !$('#' + this.env.contentframe).is(':visible'))
@@ -5793,39 +5793,52 @@
    this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
    this.subscription_list = new rcube_treelist_widget(this.gui_objects.subscriptionlist, {
        selectable: true
        selectable: true,
        id_prefix: 'rcmli',
        id_encode: this.html_identifier_encode,
        id_decode: this.html_identifier_decode
    });
    this.subscription_list
      .addEventListener('select', function(node) { ref.subscription_select(node.id); })
      .draggable({cancel: '#mailboxroot'})
      .addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
      .addEventListener('expand', function(node) { ref.folder_collapsed(node) })
      .draggable({cancel: 'li.mailbox.root'})
      .droppable({
        // @todo: find better way, accept callback is executed for every folder
        // on the list when dragging starts (and stops), this is slow, but
        // I didn't find a method to check droptarget on over event
        accept: function(node) {
          var source = ref.env.subscriptionrows[$(node).attr('id')],
            dest = ref.env.subscriptionrows[this.id],
            source_name = source[0],
            dest_name = dest[0];
          var source_folder = ref.folder_id2name($(node).attr('id')),
            dest_folder = ref.folder_id2name(this.id),
            source = ref.env.subscriptionrows[source_folder],
            dest = ref.env.subscriptionrows[dest_folder];
          return !source[2]
            && dest_name != source_name.replace(ref.last_sub_rx, '')
            && !dest_name.startsWith(source_name + ref.env.delimiter);
          return source && !source[2]
            && dest_folder != source_folder.replace(ref.last_sub_rx, '')
            && !dest_folder.startsWith(source_folder + ref.env.delimiter);
        },
        drop: function(e, ui) {
          ref.subscription_move_folder(ui.draggable.attr('id'), this.id);
          var source = ref.folder_id2name(ui.draggable.attr('id')),
            dest = ref.folder_id2name(this.id);
          ref.subscription_move_folder(source, dest);
        }
      });
  };
  this.folder_id2name = function(id)
  {
    return ref.html_identifier_decode(id.replace(/^rcmli/, ''));
  };
  this.subscription_select = function(id)
  {
    var folder;
    if (id && id != 'mailboxroot' && (folder = this.env.subscriptionrows[id])) {
      this.env.mailbox = folder[0];
      this.show_folder(folder[0]);
    if (id && id != '*' && (folder = this.env.subscriptionrows[id])) {
      this.env.mailbox = id;
      this.show_folder(id);
      this.enable_command('delete-folder', !folder[2]);
    }
    else {
@@ -5837,16 +5850,13 @@
  this.subscription_move_folder = function(from, to)
  {
    var source = this.env.subscriptionrows[from][0];
      dest = this.env.subscriptionrows[to][0];
    if (source && dest !== null && source != dest && dest != source.replace(this.last_sub_rx, '')) {
      var path = source.split(this.env.delimiter),
    if (from && to !== null && from != to && to != from.replace(this.last_sub_rx, '')) {
      var path = from.split(this.env.delimiter),
        basename = path.pop(),
        newname = dest === '' ? basename : dest + this.env.delimiter + basename;
        newname = to === '' || to === '*' ? basename : to + this.env.delimiter + basename;
      if (newname != source) {
        this.http_post('rename-folder', {_folder_oldname: source, _folder_newname: newname},
      if (newname != from) {
        this.http_post('rename-folder', {_folder_oldname: from, _folder_newname: newname},
          this.set_busy(true, 'foldermoving'));
      }
    }
@@ -5861,50 +5871,51 @@
  // delete a specific mailbox with all its messages
  this.delete_folder = function(name)
  {
    var id = this.get_folder_row_id(name ? name : this.env.mailbox),
      folder = this.env.subscriptionrows[id][0];
    if (!name)
      name = this.env.mailbox;
    if (folder && confirm(this.get_label('deletefolderconfirm'))) {
      this.http_post('delete-folder', {_mbox: folder}, this.set_busy(true, 'folderdeleting'));
    if (name && confirm(this.get_label('deletefolderconfirm'))) {
      this.http_post('delete-folder', {_mbox: name}, this.set_busy(true, 'folderdeleting'));
    }
  };
  // Add folder row to the table and initialize it
  this.add_folder_row = function (name, display_name, is_protected, subscribed, skip_init, class_name)
  this.add_folder_row = function (id, name, display_name, is_protected, subscribed, class_name, refrow, subfolders)
  {
    if (!this.gui_objects.subscriptionlist)
      return false;
    var row, n, tmp, tmp_name, rowid, collator,
    var row, n, tmp, tmp_name, rowid, collator, pos, p, parent = '',
      folders = [], list = [], slist = [],
      list_element = $(this.gui_objects.subscriptionlist),
      refrow = $('li', list_element).get(1),
      id = 'rcmli'+((new Date).getTime());
      list_element = $(this.gui_objects.subscriptionlist);
      row = refrow ? refrow : $($('li', list_element).get(1)).clone(true);
    if (!refrow) {
    if (!row.length) {
      // Refresh page if we don't have a table row to clone
      this.goto_url('folders');
      return false;
    }
    // clone a table row if there are existing rows
    row = $(refrow).clone(true);
    // set ID, reset css class
    row.attr({id: id, 'class': class_name});
    row.attr({id: 'rcmli' + this.html_identifier_encode(id), 'class': class_name});
    if (!refrow || !refrow.length) {
      // remove old subfolders and toggle
      $('ul,div.treetoggle', row).remove();
    }
    // set folder name
    $('.name', row).html(display_name);
    $('a:first', row).text(display_name);
    // update subscription checkbox
    $('input[name="_subscribed[]"]', row).val(name)
    $('input[name="_subscribed[]"]:first', row).val(id)
      .prop({checked: subscribed ? true : false, disabled: is_protected ? true : false});
    // add to folder/row-ID map
    this.env.subscriptionrows[id] = [name, display_name, false];
    // copy folders data to an array for sorting
    $.each(this.env.subscriptionrows, function(k, v) { folders.push(v); });
    $.each(this.env.subscriptionrows, function(k, v) { v[3] = k; folders.push(v); });
    try {
      // use collator if supported (FF29, IE11, Opera15, Chrome24)
@@ -5916,64 +5927,106 @@
    folders.sort(function(a, b) {
      var i, f1, f2,
        path1 = a[0].split(ref.env.delimiter),
        path2 = b[0].split(ref.env.delimiter);
        path2 = b[0].split(ref.env.delimiter),
        len = path1.length;
      for (i=0; i<path1.length; i++) {
      for (i=0; i<len; i++) {
        f1 = path1[i];
        f2 = path2[i];
        if (f1 !== f2) {
          if (f2 === undefined)
            return 1;
          if (collator)
            return collator.compare(f1, f2);
          else
            return f1 < f2 ? -1 : 1;
        }
        else if (i == len-1) {
          return -1
        }
      }
    });
    for (n in folders) {
      p = folders[n][3];
      // protected folder
      if (folders[n][2]) {
        tmp_name = folders[n][0] + this.env.delimiter;
        tmp_name = p + this.env.delimiter;
        // prefix namespace cannot have subfolders (#1488349)
        if (tmp_name == this.env.prefix_ns)
          continue;
        slist.push(folders[n][0]);
        slist.push(p);
        tmp = tmp_name;
      }
      // protected folder's child
      else if (tmp && folders[n][0].startsWith(tmp))
        slist.push(folders[n][0]);
      else if (tmp && p.startsWith(tmp))
        slist.push(p);
      // other
      else {
        list.push(folders[n][0]);
        list.push(p);
        tmp = null;
      }
    }
    // check if subfolder of a protected folder
    for (n=0; n<slist.length; n++) {
      if (name.startsWith(slist[n] + this.env.delimiter))
        rowid = this.get_folder_row_id(slist[n]);
      if (id.startsWith(slist[n] + this.env.delimiter))
        rowid = slist[n];
    }
    // find folder position after sorting
    for (n=0; !rowid && n<list.length; n++) {
      if (n && list[n] == name)
        rowid = this.get_folder_row_id(list[n-1]);
      if (n && list[n] == id)
        rowid = list[n-1];
    }
    // add row to the table
    if (rowid)
      $('#' + rowid).after(row);
    else
    if (rowid && (n = this.subscription_list.get_item(rowid, true))) {
      // find parent folder
      if (pos = id.lastIndexOf(this.env.delimiter)) {
        parent = id.substring(0, pos);
        parent = this.subscription_list.get_item(parent, true);
        // add required tree elements to the parent if not already there
        if (!$('div.treetoggle', parent).length) {
          $('<div>&nbsp;</div>').addClass('treetoggle collapsed').appendTo(parent);
        }
        if (!$('ul', parent).length) {
          $('<ul>').css('display', 'none').appendTo(parent);
        }
      }
      if (parent && n == parent) {
        $('ul:first', parent).append(row);
      }
      else {
        while (p = $(n).parent().parent().get(0)) {
          if (parent && p == parent)
            break;
          if (!$(p).is('li.mailbox'))
            break;
          n = p;
        }
        $(n).after(row);
      }
    }
    else {
      list_element.append(row);
    }
    // add subfolders
    $.extend(this.env.subscriptionrows, subfolders || {});
    // update list widget
    this.subscription_list.select();
    this.subscription_list.reset(true);
    this.subscription_select();
    if (!skip_init)
      this.init_subscription_list();
    // expand parent
    if (parent) {
      this.subscription_list.expand(this.folder_id2name(parent.id));
    }
    row = row.get(0);
    if (row.scrollIntoView)
@@ -5983,113 +6036,71 @@
  };
  // replace an existing table row with a new folder line (with subfolders)
  this.replace_folder_row = function(oldfolder, newfolder, display_name, is_protected, class_name)
  this.replace_folder_row = function(oldid, id, name, display_name, is_protected, class_name)
  {
    if (!this.gui_objects.subscriptionlist) {
      if (this.is_framed)
        return parent.rcmail.replace_folder_row(oldfolder, newfolder, display_name, is_protected, class_name);
        return parent.rcmail.replace_folder_row(oldid, id, name, display_name, is_protected, class_name);
      return false;
    }
    var i, n, len, name, dispname, oldrow, tmprow, row, level,
      folders = this.env.subscriptionrows,
      id = this.get_folder_row_id(oldfolder),
      prefix_len = oldfolder.length,
      subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'),
      // find subfolders of renamed folder
      list = this.get_subfolders(oldfolder);
    var subfolders = {},
      row = this.subscription_list.get_item(oldid, true),
      parent = $(row).parent(),
      old_folder = this.env.subscriptionrows[oldid],
      prefix_len_id = oldid.length,
      prefix_len_name = old_folder[0].length,
      subscribed = $('input[name="_subscribed[]"]:first', row).prop('checked');
    // no renaming, only update class_name
    if (oldfolder == newfolder) {
      $('#'+id).attr('class', class_name || '');
    if (oldid == id) {
      $(row).attr('class', class_name || '');
      return;
    }
    // replace an existing table row
    this._remove_folder_row(id);
    row = $(this.add_folder_row(newfolder, display_name, is_protected, subscribed, true, class_name));
    // update subfolders
    $('li', row).each(function() {
      var fname = ref.folder_id2name(this.id),
        folder = ref.env.subscriptionrows[fname],
        newid = id + fname.slice(prefix_len_id);
    // detect tree depth change
    if (len = list.length) {
      level = (oldfolder.split(this.env.delimiter)).length - (newfolder.split(this.env.delimiter)).length;
      this.id = 'rcmli' + ref.html_identifier_encode(newid);
      $('input[name="_subscribed[]"]:first', this).val(newid);
      folder[0] = name + folder[0].slice(prefix_len_name);
      subfolders[newid] = folder;
      delete ref.env.subscriptionrows[fname];
    });
    // get row off the list
    row = $(row).detach();
    delete this.env.subscriptionrows[oldid];
    // remove parent list/toggle elements if not needed
    if (parent.get(0) != this.gui_objects.subscriptionlist && !$('li', parent).length) {
      $('ul,div.treetoggle', parent.parent()).remove();
    }
    // move subfolders to the new branch
    for (n=0; n<len; n++) {
      id = list[n];
      name = this.env.subscriptionrows[id][0];
      dispname = this.env.subscriptionrows[id][1];
      oldrow = $('#'+id);
      tmprow = oldrow.clone(true);
      oldrow.remove();
      row.after(tmprow);
      row = tmprow;
      // update folder index
      name = newfolder + name.slice(prefix_len);
      $('input[name="_subscribed[]"]', row).val(name);
      this.env.subscriptionrows[id][0] = name;
      // update the name if level is changed
      if (level != 0) {
        if (level > 0) {
          for (i=level; i>0; i--)
            dispname = dispname.replace(/^&nbsp;&nbsp;&nbsp;&nbsp;/, '');
        }
        else {
          for (i=level; i<0; i++)
            dispname = '&nbsp;&nbsp;&nbsp;&nbsp;' + dispname;
        }
        $('.name', row).html(dispname);
        this.env.subscriptionrows[id][1] = dispname;
      }
    }
    // update list widget
    this.init_subscription_list();
    // move the existing table row
    this.add_folder_row(id, name, display_name, is_protected, subscribed, class_name, row, subfolders);
  };
  // remove the table row of a specific mailbox from the table
  this.remove_folder_row = function(folder, subs)
  this.remove_folder_row = function(folder)
  {
    var n, len, list = [], id = this.get_folder_row_id(folder);
    var list = [], row = this.subscription_list.get_item(folder, true);
    // get subfolders if any
    if (subs)
      list = this.get_subfolders(folder);
    $('li', row).each(function() { list.push(ref.folder_id2name(this.id)); });
    // remove old row
    this._remove_folder_row(id);
    // remove folder row (and subfolders)
    this.subscription_list.remove(folder);
    // remove subfolders
    for (n=0, len=list.length; n<len; n++)
      this._remove_folder_row(list[n]);
  };
  this._remove_folder_row = function(id)
  {
    this.subscription_list.remove(id.replace(/^rcmli/, ''));
    $('#' + id).remove();
    delete this.env.subscriptionrows[id];
  };
  this.get_subfolders = function(folder)
  {
    var name, list = [],
      prefix = folder + this.env.delimiter,
      row = $('#'+this.get_folder_row_id(folder)).get(0);
    while (row = row.nextSibling) {
      if (row.id) {
        name = this.env.subscriptionrows[row.id][0];
        if (name && name.startsWith(prefix)) {
          list.push(row.id);
        }
        else
          break;
      }
    }
    return list;
    // update local list variable
    list.push(folder);
    $.each(list, function(i, v) { delete ref.env.subscriptionrows[v]; });
  };
  this.subscribe = function(folder)
@@ -6106,15 +6117,6 @@
      var lock = this.display_message(this.get_label('folderunsubscribing'), 'loading');
      this.http_post('unsubscribe', {_mbox: folder}, lock);
    }
  };
  // helper method to find a specific mailbox row ID
  this.get_folder_row_id = function(folder)
  {
    var id, folders = this.env.subscriptionrows;
    for (id in folders)
      if (folders[id] && folders[id][0] == folder)
        return id;
  };
  // when user select a folder in manager
@@ -6140,9 +6142,9 @@
  // disables subscription checkbox (for protected folder)
  this.disable_subscription = function(folder)
  {
    var id = this.get_folder_row_id(folder);
    if (id)
      $('input[name="_subscribed[]"]', $('#'+id)).prop('disabled', true);
    var row = this.subscription_list.get_item(folder, true);
    if (row)
      $('input[name="_subscribed[]"]:first', row).prop('disabled', true);
  };
  this.folder_size = function(folder)
program/js/treelist.js
@@ -68,6 +68,8 @@
    tree_state,
    ui_droppable,
    ui_draggable,
    draggable_opts,
    droppable_opts,
    list_id = (container.attr('id') || p.id_prefix || '0'),
    me = this;
@@ -470,7 +472,7 @@
  /**
   *
   */
  function reset()
  function reset(keep_content)
  {
    select('');
@@ -478,7 +480,22 @@
    indexbyid = {};
    drag_active = false;
    container.html('');
    if (keep_content) {
      if (draggable_opts) {
        draggable('destroy');
        draggable(draggable_opts);
      }
      if (droppable_opts) {
        droppable('destroy');
        droppable(droppable_opts);
      }
      update_data();
    }
    else {
      container.html('');
    }
    reset_search();
  }
@@ -1043,6 +1060,13 @@
  {
    if (!opts) opts = {};
    if ($.type(opts) == 'string') {
      $('li:not(.virtual)', container).droppable(opts);
      return this;
    }
    droppable_opts = opts;
    var my_opts = $.extend({
        greedy: true,
        tolerance: 'pointer',
@@ -1084,8 +1108,16 @@
  {
    if (!opts) opts = {};
    if ($.type(opts) == 'string') {
      $('li:not(.virtual)', container).draggable(opts);
      return this;
    }
    draggable_opts = opts;
    var my_opts = $.extend({
        appendTo: 'body',
        revert: 'invalid',
        iframeFix: true,
        addClasses: false,
        cursorAt: {left: -20, top: 5},
program/steps/settings/edit_folder.inc
@@ -38,22 +38,20 @@
    $storage = $RCMAIL->get_storage();
    // edited folder name (empty in create-folder mode)
    $mbox      = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true);
    $mbox_imap = rcube_charset::convert($mbox, RCUBE_CHARSET, 'UTF7-IMAP');
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GPC, true);
    // predefined path for new folder
    $parent      = rcube_utils::get_input_value('_path', rcube_utils::INPUT_GPC, true);
    $parent_imap = rcube_charset::convert($parent, RCUBE_CHARSET, 'UTF7-IMAP');
    $parent = rcube_utils::get_input_value('_path', rcube_utils::INPUT_GPC, true);
    $threading_supported = $storage->get_capability('THREAD');
    $delimiter = $storage->get_hierarchy_delimiter();
    // Get mailbox parameters
    if (strlen($mbox)) {
        $options   = rcmail_folder_options($mbox_imap);
        $options   = rcmail_folder_options($mbox);
        $namespace = $storage->get_namespace();
        $path   = explode($delimiter, $mbox_imap);
        $path   = explode($delimiter, $mbox);
        $folder = array_pop($path);
        $path   = implode($delimiter, $path);
        $folder = rcube_charset::convert($folder, 'UTF7-IMAP');
@@ -62,7 +60,7 @@
    }
    else {
        $options = array();
        $path    = $parent_imap;
        $path    = $parent;
        // allow creating subfolders of INBOX folder
        if ($path == 'INBOX') {
@@ -88,7 +86,7 @@
    // Location (name)
    if ($options['protected']) {
        $foldername = str_replace($delimiter, ' &raquo; ', rcube::Q($RCMAIL->localize_folderpath($mbox_imap)));
        $foldername = str_replace($delimiter, ' &raquo; ', rcube::Q($RCMAIL->localize_folderpath($mbox)));
    }
    else if ($options['norename']) {
        $foldername = rcube::Q($folder);
@@ -101,7 +99,7 @@
        $foldername = $foldername->show($folder);
        if ($options['special']) {
            $foldername .= '&nbsp;(' . rcube::Q($RCMAIL->localize_foldername($mbox_imap)) .')';
            $foldername .= '&nbsp;(' . rcube::Q($RCMAIL->localize_foldername($mbox)) .')';
        }
    }
@@ -122,7 +120,7 @@
    }
    else {
        $selected = isset($_POST['_parent']) ? $_POST['_parent'] : $path_id;
        $exceptions = array($mbox_imap);
        $exceptions = array($mbox);
        // Exclude 'prefix' namespace from parent folders list (#1488349)
        // If INBOX. namespace exists, folders created as INBOX subfolders
@@ -154,7 +152,7 @@
    );
    // Settings: threading
    if ($threading_supported && ($mbox_imap == 'INBOX' || (!$options['noselect'] && !$options['is_root']))) {
    if ($threading_supported && ($mbox == 'INBOX' || (!$options['noselect'] && !$options['is_root']))) {
        $select = new html_select(array('name' => '_viewmode', 'id' => '_viewmode'));
        $select->add($RCMAIL->gettext('list'), 0);
        $select->add($RCMAIL->gettext('threads'), 1);
@@ -162,11 +160,11 @@
        if (isset($_POST['_viewmode'])) {
            $value = (int) $_POST['_viewmode'];
        }
        else if (strlen($mbox_imap)) {
        else if (strlen($mbox)) {
            $a_threaded   = $RCMAIL->config->get('message_threading', array());
            $default_mode = $RCMAIL->config->get('default_list_mode', 'list');
            $value = (int) (isset($a_threaded[$mbox_imap]) ? $a_threaded[$mbox_imap] : $default_mode == 'threads');
            $value = (int) (isset($a_threaded[$mbox]) ? $a_threaded[$mbox] : $default_mode == 'threads');
        }
        $form['props']['fieldsets']['settings']['content']['viewmode'] = array(
@@ -213,14 +211,14 @@
            'content' => array()
        );
        if ((!$options['noselect'] && !$options['is_root']) || $mbox_imap == 'INBOX') {
            $msgcount = $storage->count($mbox_imap, 'ALL', true, false);
        if ((!$options['noselect'] && !$options['is_root']) || $mbox == 'INBOX') {
            $msgcount = $storage->count($mbox, 'ALL', true, false);
            // Size
            if ($msgcount) {
                // create link with folder-size command
                $onclick = sprintf("return %s.command('folder-size', '%s', this)",
                    rcmail_output::JS_OBJECT_NAME, rcube::JQ($mbox_imap));
                    rcmail_output::JS_OBJECT_NAME, rcube::JQ($mbox));
                $size = html::a(array('href' => '#', 'onclick' => $onclick,
                    'id' => 'folder-size'), $RCMAIL->gettext('getfoldersize'));
            }
@@ -250,7 +248,7 @@
    // Allow plugins to modify folder form content
    $plugin = $RCMAIL->plugins->exec_hook('folder_form',
        array('form' => $form, 'options' => $options,
            'name' => $mbox_imap, 'parent_name' => $parent_imap));
            'name' => $mbox, 'parent_name' => $parent));
    $form = $plugin['form'];
@@ -290,8 +288,8 @@
    $RCMAIL->output->set_env('messagecount', (int) $msgcount);
    if ($mbox_imap !== null && empty($_POST)) {
        $RCMAIL->output->command('parent.set_quota', $RCMAIL->quota_content(null, $mbox_imap));
    if ($mbox !== null && empty($_POST)) {
        $RCMAIL->output->command('parent.set_quota', $RCMAIL->quota_content(null, $mbox));
    }
    return $out;
program/steps/settings/folders.inc
@@ -20,14 +20,12 @@
 +-----------------------------------------------------------------------+
*/
// WARNING: folder names in UI are encoded with RCUBE_CHARSET
// init IMAP connection
$STORAGE = $RCMAIL->get_storage();
// subscribe mailbox
if ($RCMAIL->action == 'subscribe') {
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true, 'UTF7-IMAP');
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    if (strlen($mbox)) {
        $result = $STORAGE->subscribe(array($mbox));
@@ -58,7 +56,8 @@
}
// unsubscribe mailbox
else if ($RCMAIL->action == 'unsubscribe') {
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true, 'UTF7-IMAP');
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    if (strlen($mbox)) {
        $result = $STORAGE->unsubscribe(array($mbox));
        if ($result)
@@ -69,8 +68,7 @@
}
// delete an existing mailbox
else if ($RCMAIL->action == 'delete-folder') {
    $mbox_utf8 = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    $mbox      = rcube_charset::convert($mbox_utf8, RCUBE_CHARSET, 'UTF7-IMAP');
    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    if (strlen($mbox)) {
        $plugin = $RCMAIL->plugins->exec_hook('folder_delete', array('name' => $mbox));
@@ -90,7 +88,7 @@
    if ($OUTPUT->ajax_call && $deleted) {
        // Remove folder and subfolders rows
        $OUTPUT->command('remove_folder_row', $mbox_utf8, true);
        $OUTPUT->command('remove_folder_row', $mbox);
        $OUTPUT->show_message('folderdeleted', 'confirmation');
        // Clear content frame
        $OUTPUT->command('subscription_select');
@@ -102,13 +100,10 @@
}
// rename an existing mailbox
else if ($RCMAIL->action == 'rename-folder') {
    $name_utf8    = trim(rcube_utils::get_input_value('_folder_newname', rcube_utils::INPUT_POST, true));
    $oldname_utf8 = rcube_utils::get_input_value('_folder_oldname', rcube_utils::INPUT_POST, true);
    $name    = trim(rcube_utils::get_input_value('_folder_newname', rcube_utils::INPUT_POST, true));
    $oldname = rcube_utils::get_input_value('_folder_oldname', rcube_utils::INPUT_POST, true);
    if (strlen($name_utf8) && strlen($oldname_utf8)) {
        $name    = rcube_charset::convert($name_utf8, RCUBE_CHARSET, 'UTF7-IMAP');
        $oldname = rcube_charset::convert($oldname_utf8, RCUBE_CHARSET, 'UTF7-IMAP');
    if (strlen($name) && strlen($oldname)) {
        $rename = rcmail_rename_folder($oldname, $name);
    }
@@ -121,8 +116,7 @@
}
// clear mailbox
else if ($RCMAIL->action == 'purge') {
    $mbox_utf8    = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    $mbox         = rcube_charset::convert($mbox_utf8, RCUBE_CHARSET, 'UTF7-IMAP');
    $mbox         = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
    $delimiter    = $STORAGE->get_hierarchy_delimiter();
    $trash_mbox   = $RCMAIL->config->get('trash_mbox');
    $trash_regexp = '/^' . preg_quote($trash . $delimiter, '/') . '/';
@@ -150,7 +144,7 @@
            $OUTPUT->show_message('messagemoved', 'confirmation');
        }
        $_SESSION['unseen_count'][$mbox] = 0;
        $OUTPUT->command('show_folder', $mbox_utf8, null, true);
        $OUTPUT->command('show_folder', $mbox, null, true);
    }
    else {
        $RCMAIL->display_server_error('errorsaving');
@@ -270,6 +264,7 @@
    $js_folders = array();
    $folders    = array();
    $collapsed  = $RCMAIL->config->get('collapsed_folders');
    // create list of available folders
    foreach ($list_folders as $i => $folder) {
@@ -278,11 +273,10 @@
        $subscribed = $sub_key !== false;
        $protected  = $protect_default && isset($special_folders[$folder['id']]);
        $noselect   = false;
        $classes    = array('listitem');
        $classes    = array();
        $folder_utf8    = rcube_charset::convert($folder['id'], 'UTF7-IMAP');
        $display_folder = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $folder['level'])
            . rcube::Q($protected ? $RCMAIL->localize_foldername($folder['id']) : $folder['name']);
        $display_folder = rcube::Q($protected ? $RCMAIL->localize_foldername($folder['id']) : $folder['name']);
        if ($folder['virtual']) {
            $classes[] = 'virtual';
@@ -338,45 +332,85 @@
            }
        }
        $row_id = 'rcmli' . $idx;
        $folders[$row_id] = array(
        $is_collapsed = strpos($collapsed, '&'.rawurlencode($folder['id']).'&') !== false;
        $folder_id    = rcube_utils::html_identifier($folder['id'], true);
        if ($folder_class = $RCMAIL->folder_classname($folder['id'])) {
            $classes[] = $folder_class;
        }
        $folders[$folder['id']] = array(
            'idx'         => $folder_id,
            'folder_imap' => $folder['id'],
            'folder'      => $folder_utf8,
            'display'     => $display_folder,
            'class'       => join(' ', $classes),
            'folder_imap' => $folder['id'],
            'subscribed'  => $subscribed,
            'protected'   => $protected || $folder['virtual'],
            'content'     => html::a(array('class' => 'name', 'href' => '#_' . $row_id), $display_folder)
                . $checkbox_subscribe->show(($subscribed ? $folder_utf8 : ''),
                    array('value' => $folder_utf8, 'disabled' => $disabled ? 'disabled' : ''))
            'class'       => join(' ', $classes),
            'subscribed'  => $subscribed,
            'level'       => $folder['level'],
            'collapsed'   => $is_collapsed,
            'content'     => html::a(array('href' => '#'), $display_folder)
                . $checkbox_subscribe->show(($subscribed ? $folder['id'] : ''),
                    array('value' => $folder['id'], 'disabled' => $disabled ? 'disabled' : ''))
        );
    }
    $plugin = $RCMAIL->plugins->exec_hook('folders_list', array('list' => $folders));
    // add drop-target representing 'root'
    $roots = array(
        'mailboxroot' => array(
            'folder'    => '',
            'display'   => '',
            'protected' => true,
            'class'     => 'root',
            'content'   => html::span('name', '&nbsp;')
        )
    $root = array(
        'idx'         => rcube_utils::html_identifier('*', true),
        'folder_imap' => '*',
        'folder'      => '',
        'display'     => '',
        'protected'   => true,
        'class'       => 'root',
        'content'     => '<span>&nbsp;</span>',
    );
    $folders = array_merge($roots, $plugin['list']);
    while (list($key, $data) = each($folders)) {
        $js_folders[$key] = array($data['folder'], $data['display'], $data['protected']);
        $folders[$key]    = html::tag('li', array('id'  => $key, 'class' => $data['class']), $data['content']);
    $folders        = array();
    $plugin['list'] = array_values($plugin['list']);
    array_unshift($plugin['list'], $root);
    for ($i = 0, $length = count($plugin['list'])-1; $i<$length; $i++) {
        $folders[] = rcmail_folder_tree_element($plugin['list'], $i, $js_folders);
    }
    $OUTPUT->add_gui_object('subscriptionlist', $attrib['id']);
    $OUTPUT->set_env('subscriptionrows', $js_folders);
    $OUTPUT->set_env('defaultfolders', array_keys($special_folders));
    $OUTPUT->set_env('collapsed_folders', $collapsed);
    $OUTPUT->set_env('delimiter', $delimiter);
    return $form_start . html::tag('ul', $attrib, implode("\n", $folders)) . $form_end;
    return $form_start . html::tag('ul', $attrib, implode('', $folders), html::$common_attrib) . $form_end;
}
function rcmail_folder_tree_element($folders, &$key, &$js_folders)
{
    $data = $folders[$key];
    $idx  = 'rcmli' . $data['idx'];
    $js_folders[$data['folder_imap']] = array($data['folder'], $data['display'], $data['protected']);
    $content          = $data['content'];
    $attribs          = array(
        'id'    => $idx,
        'class' => trim($data['class'] . ' mailbox')
    );
    $children = array();
    while ($folders[$key+1] && $folders[$key+1]['level'] > $data['level']) {
        $key++;
        $children[] = rcmail_folder_tree_element($folders, $key, $js_folders);
    }
    if (!empty($children)) {
        $content .= html::div('treetoggle ' . ($data['collapsed'] ? 'collapsed' : 'expanded'), '&nbsp;')
            . html::tag('ul', array('style' => ($data['collapsed'] ? "display:none" : null)),
                implode("\n", $children));
    }
    return html::tag('li', $attribs, $content);
}
function rcmail_folder_frame($attrib)
program/steps/settings/func.inc
@@ -1302,23 +1302,20 @@
    $protect_folders = $RCMAIL->config->get('protect_default_folders');
    $storage         = $RCMAIL->get_storage();
    $delimiter       = $storage->get_hierarchy_delimiter();
    $name_utf8       = rcube_charset::convert($name, 'UTF7-IMAP');
    $protected       = $protect_folders && $storage->is_special_folder($name);
    $name_utf8    = rcube_charset::convert($name, 'UTF7-IMAP');
    $protected    = $protect_folders && $storage->is_special_folder($name);
    $foldersplit  = explode($delimiter, $storage->mod_folder($name));
    $level        = count($foldersplit) - 1;
    $display_name = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level)
        . rcube::Q($protected ? $RCMAIL->localize_foldername($name) : rcube_charset::convert($foldersplit[$level], 'UTF7-IMAP'));
    $class_name = trim($class_name . ' listitem');
    $display_name = $protected ? $RCMAIL->localize_foldername($name) : rcube_charset::convert($foldersplit[$level], 'UTF7-IMAP');
    $class_name   = trim($class_name . ' mailbox');
    if ($oldname === null) {
        $OUTPUT->command('add_folder_row', $name_utf8, $display_name, $protected, $subscribe,
            false, $class_name);
        $OUTPUT->command('add_folder_row', $name, $name_utf8, $display_name, $protected, $subscribe,
            $class_name);
    }
    else {
        $OUTPUT->command('replace_folder_row', rcube_charset::convert($oldname, 'UTF7-IMAP'),
            $name_utf8, $display_name, $protected, $class_name);
        $OUTPUT->command('replace_folder_row', $oldname, $name, $name_utf8, $display_name, $protected, $class_name);
    }
}
program/steps/settings/save_folder.inc
@@ -24,12 +24,10 @@
// init IMAP connection
$STORAGE = $RCMAIL->get_storage();
$name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true));
$old  = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
$path = rcube_utils::get_input_value('_parent', rcube_utils::INPUT_POST, true);
$name      = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true));
$path      = rcube_utils::get_input_value('_parent', rcube_utils::INPUT_POST, true);
$old_imap  = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
$name_imap = rcube_charset::convert($name, RCUBE_CHARSET, 'UTF7-IMAP');
$old_imap  = rcube_charset::convert($old, RCUBE_CHARSET, 'UTF7-IMAP');
// $path is in UTF7-IMAP already
$delimiter = $STORAGE->get_hierarchy_delimiter();
@@ -96,7 +94,7 @@
}
// create a new mailbox
if (!$error && !strlen($old)) {
if (!$error && !strlen($old_imap)) {
    $folder['subscribe'] = true;
    $plugin = $RCMAIL->plugins->exec_hook('folder_create', array('record' => $folder));
skins/classic/common.css
@@ -290,9 +290,9 @@
body > #message div.voice
{
    position: absolute;
    top: -1000px;
    clip: rect(0 0 0 0);
  position: absolute;
  top: -1000px;
  clip: rect(0 0 0 0);
}
body > #message a
@@ -766,6 +766,154 @@
  background-color: #FFFFA6;
}
/***** folders list *****/
.folderlist li ul li:last-child
{
  border-bottom: 0 none;
}
.folderlist li.inbox a
{
  background-position: 5px -18px;
}
.folderlist li.drafts a
{
  background-position: 5px -35px;
}
.folderlist li.sent a
{
  background-position: 5px -54px;
}
.folderlist li.junk a
{
  background-position: 5px -73px;
}
.folderlist li.trash a
{
  background-position: 5px -180px;
}
.folderlist li.trash.empty a
{
  background-position: 5px -90px;
}
.folderlist li a
{
  cursor: default;
  display: block;
  position: relative;
  padding-left: 25px;
  padding-top: 2px;
  padding-bottom: 2px;
  text-decoration: none;
  height: 15px;
  background: url(images/icons/folders.png) 5px 0 no-repeat;
}
.folderlist li.virtual > a
{
  color: #666;
}
.folderlist li.selected,
.folderlist li.droptarget li.selected
{
  background-color: #929292;
}
.folderlist li.selected > a,
.folderlist li.droptarget li.selected a
{
  color: #FFF;
  font-weight: bold;
}
.folderlist li.droptarget
{
  background-color: #FFFFA6;
}
/* styles for nested folders */
.folderlist ul {
  list-style: none;
  padding: 0;
  margin: 0;
  border-top: 1px solid #EBEBEB;
  background-color: #FFF;
  font-weight: normal;
}
.folderlist li.mailbox ul li a {
  padding-left: 40px;  /* 24 + 1 x 16 */
  background-position: 20px 0;  /* 4 + 1 x 16 */
}
.folderlist li.mailbox ul li div.treetoggle {
  left: 23px !important;
}
.folderlist li.mailbox ul ul li.mailbox a {
  padding-left: 56px;  /* 2x */
  background-position: 36px 0;
}
.folderlist li.mailbox ul ul li div.treetoggle {
  left: 39px !important;
}
.folderlist li.mailbox ul ul ul li.mailbox a {
  padding-left: 72px;  /* 3x */
  background-position: 52px 0;
}
.folderlist li.mailbox ul ul ul li div.treetoggle {
  left: 55px !important;
}
.folderlist li.mailbox ul ul ul ul li.mailbox a {
  padding-left: 88px;  /* 4x */
  background-position: 68px 0;
}
.folderlist li.mailbox ul ul ul ul li div.treetoggle {
  left: 71px !important;
}
/* indent folders on levels > 4 */
.folderlist li.mailbox ul ul ul ul ul li {
  padding-left: 16px;
}
.folderlist li.mailbox ul ul ul ul ul li div.treetoggle {
  left: 87px !important;
}
.folderlist li.mailbox ul li.drafts a
{
  background-position: 21px -37px;
}
.folderlist li.mailbox ul li.sent a
{
  background-position: 21px -54px;
}
.folderlist li.mailbox ul li.junk a
{
  background-position: 21px -73px;
}
.folderlist li.mailbox ul li.trash a
{
  background-position: 21px -180px;
}
.folderlist li.mailbox ul li.trash.empty a
{
  background-position: 21px -90px;
}
/***** mac-style quicksearch field *****/
skins/classic/mail.css
@@ -409,160 +409,14 @@
  background-color: #FFF;
}
#mailboxlist li ul li:last-child
{
  border-bottom: 0 none;
}
#mailboxlist li.inbox a
{
  background-position: 5px -18px;
}
#mailboxlist li.drafts a
{
  background-position: 5px -37px;
}
#mailboxlist li.sent a
{
  background-position: 5px -54px;
}
#mailboxlist li.junk a
{
  background-position: 5px -73px;
}
#mailboxlist li.trash a
{
  background-position: 5px -180px;
}
#mailboxlist li.trash.empty a
{
  background-position: 5px -90px;
}
#mailboxlist li a
{
  cursor: default;
  display: block;
  position: relative;
  padding-left: 25px;
  padding-top: 2px;
  padding-bottom: 2px;
  text-decoration: none;
  height: 15px;
  background: url(images/icons/folders.png) 5px 0 no-repeat;
}
#mailboxlist li.unread
{
  font-weight: bold;
}
#mailboxlist li.virtual > a
{
  color: #666;
}
#mailboxlist li.recent > a
{
  color: #0066FF;
}
#mailboxlist li.selected,
#mailboxlist li.droptarget li.selected
{
  background-color: #929292;
}
#mailboxlist li.selected > a,
#mailboxlist li.droptarget li.selected a
{
  color: #FFF;
  font-weight: bold;
}
#mailboxlist li.droptarget
{
  background-color: #FFFFA6;
}
/* styles for nested folders */
#mailboxlist ul {
  list-style: none;
  padding: 0;
  margin: 0;
  border-top: 1px solid #EBEBEB;
  background-color: #FFF;
  font-weight: normal;
}
#mailboxlist li.mailbox ul li a {
  padding-left: 40px;  /* 24 + 1 x 16 */
  background-position: 20px 0;  /* 4 + 1 x 16 */
}
#mailboxlist li.mailbox ul li div.treetoggle {
  left: 23px !important;
}
#mailboxlist li.mailbox ul ul li.mailbox a {
  padding-left: 56px;  /* 2x */
  background-position: 36px 0;
}
#mailboxlist li.mailbox ul ul li div.treetoggle {
  left: 39px !important;
}
#mailboxlist li.mailbox ul ul ul li.mailbox a {
  padding-left: 72px;  /* 3x */
  background-position: 52px 0;
}
#mailboxlist li.mailbox ul ul ul li div.treetoggle {
  left: 55px !important;
}
#mailboxlist li.mailbox ul ul ul ul li.mailbox a {
  padding-left: 88px;  /* 4x */
  background-position: 68px 0;
}
#mailboxlist li.mailbox ul ul ul ul li div.treetoggle {
  left: 71px !important;
}
/* indent folders on levels > 4 */
#mailboxlist li.mailbox ul ul ul ul ul li {
  padding-left: 16px;
}
#mailboxlist li.mailbox ul ul ul ul ul li div.treetoggle {
  left: 87px !important;
}
#mailboxlist li.mailbox ul li.drafts a
{
  background-position: 21px -37px;
}
#mailboxlist li.mailbox ul li.sent a
{
  background-position: 21px -54px;
}
#mailboxlist li.mailbox ul li.junk a
{
  background-position: 21px -73px;
}
#mailboxlist li.mailbox ul li.trash a
{
  background-position: 21px -180px;
}
#mailboxlist li.mailbox ul li.trash.empty a
{
  background-position: 21px -90px;
}
#listcontrols
skins/classic/settings.css
@@ -23,12 +23,6 @@
  font-style: italic;
}
#subscription-table li.selected a
{
  color: #FFF;
  background-color: #CC3333;
}
#subscription-table li.root
{
  font-size: 5%;
@@ -37,28 +31,16 @@
  padding: 2px;
}
#subscription-table li a.name
{
  overflow: hidden;
  text-overflow: ellipsis;
  width: 100%;
  display: block;
  float: left;
  padding: 0 0 0 5px;
  height: 24px;
  line-height: 24px;
}
#subscription-table li input
{
#subscription-table li input {
  position: absolute;
  right: 0;
  top: 2px;
}
html.chrome #subscription-table li input,
html.opera #subscription-table li input
{
  margin-top: 6px;
#subscription-table li a {
  padding-right: 20px;
  overflow: hidden;
  text-overflow: ellipsis;
}
#folder-box,
skins/classic/templates/folders.html
@@ -22,7 +22,7 @@
<div id="folderlist-title" class="boxtitle"><span class="rightalign"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></div>
<div id="folderlist-content" class="boxlistcontent">
    <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table"
        summary="Folder subscription table" class="treelist" />
        summary="Folder subscription table" class="treelist folderlist" />
</div>
<div id="folderlist-footer" class="boxfooter">
    <roundcube:button command="create-folder" type="link" title="createfolder" class="buttonPas addgroup" classAct="button addgroup" content=" " />
skins/classic/templates/mail.html
@@ -24,7 +24,7 @@
<div id="mailboxlist-container">
<div id="mailboxlist-title" class="boxtitle"><roundcube:label name="mailboxlist" /></div>
<div id="mailboxlist-content"  class="boxlistcontent">
<roundcube:object name="mailboxlist" id="mailboxlist" class="treelist" folder_filter="mail" />
  <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist folderlist" folder_filter="mail" />
</div>
<div id="mailboxlist-footer" class="boxfooter">
  <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " />
skins/larry/mail.css
@@ -137,238 +137,24 @@
    background-position: -26px -497px;
}
#mailboxlist li.mailbox {
    position: relative;
    background-repeat: no-repeat;
    background-position: 6px 2px;
}
#mailboxlist > li:first-child {
    border-radius: 4px 4px 0 0;
    border-top: 0;
}
#mailboxlist li.mailbox a {
    padding-left: 36px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background-image: url(images/listicons.png);
    background-repeat: no-repeat;
    background-position: 6px 3px;
html.mozilla #mailboxlist > li:first-child {
    border-radius: 4px 4px 0 0;
}
#mailboxlist li.mailbox.unread > a {
    padding-right: 36px;
}
#mailboxlist li.mailbox > a:focus,
#mailboxlist li.mailbox.selected > a {
    background-position: 6px -21px;
}
#mailboxlist li.mailbox.inbox > a {
    background-position: 6px -189px;
}
#mailboxlist li.mailbox.inbox > a:focus,
#mailboxlist li.mailbox.inbox.selected > a {
    background-position: 6px -213px;
}
#mailboxlist li.mailbox.drafts > a {
    background-position: 6px -238px;
}
#mailboxlist li.mailbox.drafts > a:focus,
#mailboxlist li.mailbox.drafts.selected > a {
    background-position: 6px -262px;
}
#mailboxlist li.mailbox.sent > a {
    background-position: 6px -286px;
}
#mailboxlist li.mailbox.sent > a:focus,
#mailboxlist li.mailbox.sent.selected > a {
    background-position: 6px -310px;
}
#mailboxlist li.mailbox.junk > a {
    background-position: 6px -334px;
}
#mailboxlist li.mailbox.junk > a:focus,
#mailboxlist li.mailbox.junk.selected > a {
    background-position: 6px -358px;
}
#mailboxlist li.mailbox.trash > a {
    background-position: 6px -382px;
}
#mailboxlist li.mailbox.trash > a:focus,
#mailboxlist li.mailbox.trash.selected > a {
    background-position: 6px -406px;
}
#mailboxlist li.mailbox.trash.empty > a {
    background-position: 6px -1924px;
}
#mailboxlist li.mailbox.trash.empty > a:focus,
#mailboxlist li.mailbox.trash.empty.selected > a {
    background-position: 6px -1948px;
}
#mailboxlist li.mailbox.archive > a {
    background-position: 6px -1699px;
}
#mailboxlist li.mailbox.archive > a:focus,
#mailboxlist li.mailbox.archive.selected > a {
    background-position: 6px -1723px;
}
#mailboxlist li.mailbox ul li.drafts > a {
    background-position: 23px -238px;
}
#mailboxlist li.mailbox ul li.drafts > a:focus,
#mailboxlist li.mailbox ul li.drafts.selected > a {
    background-position: 23px -262px;
}
#mailboxlist li.mailbox ul li.sent > a {
    background-position: 23px -286px;
}
#mailboxlist li.mailbox ul li.sent > a:focus,
#mailboxlist li.mailbox ul li.sent.selected > a {
    background-position: 23px -310px;
}
#mailboxlist li.mailbox ul li.junk > a {
    background-position: 23px -334px;
}
#mailboxlist li.mailbox ul li.junk > a:focus,
#mailboxlist li.mailbox ul li.junk.selected > a {
    background-position: 23px -358px;
}
#mailboxlist li.mailbox ul li.trash > a {
    background-position: 23px -382px;
}
#mailboxlist li.mailbox ul li.trash > a:focus,
#mailboxlist li.mailbox ul li.trash.selected > a {
    background-position: 23px -406px;
}
#mailboxlist li.mailbox ul li.trash.empty > a {
    background-position: 23px -1924px;
}
#mailboxlist li.mailbox ul li.trash.empty > a:focus,
#mailboxlist li.mailbox ul li.trash.empty.selected > a {
    background-position: 23px -1948px;
}
#mailboxlist li.mailbox ul li.archive > a {
    background-position: 23px -1699px;
}
#mailboxlist li.mailbox ul li.archive > a:focus,
#mailboxlist li.mailbox ul li.archive.selected > a {
    background-position: 23px -1723px;
}
#mailboxlist li.unread {
    font-weight: bold;
}
#mailboxlist li.virtual > a {
    color: #aaa;
}
#mailboxlist li.recent > a {
    color: #017cb4;
}
#mailboxlist li.mailbox div.treetoggle {
    top: 13px;
    left: 19px;
}
#mailboxlist li.mailbox ul li:last-child {
    border-bottom: 0;
}
/* nested mailboxes */
#mailboxlist li.mailbox ul {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid #bbd3da;
}
#mailboxlist li.mailbox ul li a {
    padding-left: 52px;  /* 36 + 1 x 16 */
    background-position: 22px -95px;  /* 6 + 1 x 16 */
}
#mailboxlist li.mailbox ul li > a:focus,
#mailboxlist li.mailbox ul li.selected > a {
    background-position: 22px -119px;
}
#mailboxlist li.mailbox ul li div.treetoggle {
    left: 33px;
    top: 14px;
}
#mailboxlist li.mailbox ul ul li.mailbox a {
    padding-left: 68px;  /* 2x */
    background-position: 38px -95px;
}
#mailboxlist li.mailbox ul ul li > a:focus,
#mailboxlist li.mailbox ul ul li.selected > a {
    background-position: 38px -119px;
}
#mailboxlist li.mailbox ul ul li div.treetoggle {
    left: 48px;
}
#mailboxlist li.mailbox ul ul ul li.mailbox a {
    padding-left: 84px;  /* 3x */
    background-position: 54px -95px;
}
#mailboxlist li.mailbox ul ul ul li > a:focus,
#mailboxlist li.mailbox ul ul ul li.selected > a {
    background-position: 54px -119px;
}
#mailboxlist li.mailbox ul ul ul li div.treetoggle {
    left: 64px;
}
#mailboxlist li.mailbox ul ul ul ul li.mailbox a {
    padding-left: 100px;  /* 4x */
    background-position: 70px -95px;
}
#mailboxlist li.mailbox ul ul ul ul li > a:focus,
#mailboxlist li.mailbox ul ul ul ul li.selected > a {
    background-position: 70px -119px;
}
#mailboxlist li.mailbox ul ul ul ul li div.treetoggle {
    left: 80px;
}
/* indent folders on levels > 4 */
#mailboxlist li.mailbox ul ul ul ul ul li {
    padding-left: 16px;
}
#mailboxlist li.mailbox ul ul ul ul ul li div.treetoggle {
    left: 96px;
}
#mailboxlist li.mailbox .unreadcount {
skins/larry/settings.css
@@ -251,21 +251,14 @@
    padding: 2px;
}
#subscription-table li a.name {
    overflow: hidden;
    text-overflow: ellipsis;
    width: 100%;
    float: left;
}
#subscription-table li input {
    position: absolute;
    right: 0;
    top: 4px;
}
html.chrome #subscription-table li input,
html.opera #subscription-table li input {
    margin-top: 6px;
#subscription-table li a {
    padding-right: 20px;
}
.skinselection {
skins/larry/styles.css
@@ -2606,6 +2606,224 @@
    background-position: 4px -2100px;
}
/*** folders list ***/
.folderlist li.mailbox a {
    padding-left: 36px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    background-image: url(images/listicons.png);
    background-repeat: no-repeat;
    background-position: 6px 3px;
}
.folderlist li.mailbox.unread > a {
    padding-right: 36px;
}
.folderlist li.mailbox > a:focus,
.folderlist li.mailbox.selected > a {
    background-position: 6px -21px;
}
.folderlist li.mailbox.inbox > a {
    background-position: 6px -189px;
}
.folderlist li.mailbox.inbox > a:focus,
.folderlist li.mailbox.inbox.selected > a {
    background-position: 6px -213px;
}
.folderlist li.mailbox.drafts > a {
    background-position: 6px -238px;
}
.folderlist li.mailbox.drafts > a:focus,
.folderlist li.mailbox.drafts.selected > a {
    background-position: 6px -262px;
}
.folderlist li.mailbox.sent > a {
    background-position: 6px -286px;
}
.folderlist li.mailbox.sent > a:focus,
.folderlist li.mailbox.sent.selected > a {
    background-position: 6px -310px;
}
.folderlist li.mailbox.junk > a {
    background-position: 6px -334px;
}
.folderlist li.mailbox.junk > a:focus,
.folderlist li.mailbox.junk.selected > a {
    background-position: 6px -358px;
}
.folderlist li.mailbox.trash > a {
    background-position: 6px -382px;
}
.folderlist li.mailbox.trash > a:focus,
.folderlist li.mailbox.trash.selected > a {
    background-position: 6px -406px;
}
.folderlist li.mailbox.trash.empty > a {
    background-position: 6px -1924px;
}
.folderlist li.mailbox.trash.empty > a:focus,
.folderlist li.mailbox.trash.empty.selected > a {
    background-position: 6px -1948px;
}
.folderlist li.mailbox.archive > a {
    background-position: 6px -1699px;
}
.folderlist li.mailbox.archive > a:focus,
.folderlist li.mailbox.archive.selected > a {
    background-position: 6px -1723px;
}
.folderlist li.mailbox ul li.drafts > a {
    background-position: 23px -238px;
}
.folderlist li.mailbox ul li.drafts > a:focus,
.folderlist li.mailbox ul li.drafts.selected > a {
    background-position: 23px -262px;
}
.folderlist li.mailbox ul li.sent > a {
    background-position: 23px -286px;
}
.folderlist li.mailbox ul li.sent > a:focus,
.folderlist li.mailbox ul li.sent.selected > a {
    background-position: 23px -310px;
}
.folderlist li.mailbox ul li.junk > a {
    background-position: 23px -334px;
}
.folderlist li.mailbox ul li.junk > a:focus,
.folderlist li.mailbox ul li.junk.selected > a {
    background-position: 23px -358px;
}
.folderlist li.mailbox ul li.trash > a {
    background-position: 23px -382px;
}
.folderlist li.mailbox ul li.trash > a:focus,
.folderlist li.mailbox ul li.trash.selected > a {
    background-position: 23px -406px;
}
.folderlist li.mailbox ul li.trash.empty > a {
    background-position: 23px -1924px;
}
.folderlist li.mailbox ul li.trash.empty > a:focus,
.folderlist li.mailbox ul li.trash.empty.selected > a {
    background-position: 23px -1948px;
}
.folderlist li.mailbox ul li.archive > a {
    background-position: 23px -1699px;
}
.folderlist li.mailbox ul li.archive > a:focus,
.folderlist li.mailbox ul li.archive.selected > a {
    background-position: 23px -1723px;
}
.folderlist li.virtual > a {
    color: #aaa;
}
.folderlist li.mailbox div.treetoggle {
    top: 13px;
    left: 19px;
}
.folderlist li.mailbox ul li:last-child {
    border-bottom: 0;
}
/* nested mailboxes */
.folderlist li.mailbox ul {
    list-style: none;
    margin: 0;
    padding: 0;
    border-top: 1px solid #bbd3da;
}
.folderlist li.mailbox ul li a {
    padding-left: 52px;  /* 36 + 1 x 16 */
    background-position: 22px -95px;  /* 6 + 1 x 16 */
}
.folderlist li.mailbox ul li > a:focus,
.folderlist li.mailbox ul li.selected > a {
    background-position: 22px -119px;
}
.folderlist li.mailbox ul li div.treetoggle {
    left: 33px;
    top: 14px;
}
.folderlist li.mailbox ul ul li.mailbox a {
    padding-left: 68px;  /* 2x */
    background-position: 38px -95px;
}
.folderlist li.mailbox ul ul li > a:focus,
.folderlist li.mailbox ul ul li.selected > a {
    background-position: 38px -119px;
}
.folderlist li.mailbox ul ul li div.treetoggle {
    left: 48px;
}
.folderlist li.mailbox ul ul ul li.mailbox a {
    padding-left: 84px;  /* 3x */
    background-position: 54px -95px;
}
.folderlist li.mailbox ul ul ul li > a:focus,
.folderlist li.mailbox ul ul ul li.selected > a {
    background-position: 54px -119px;
}
.folderlist li.mailbox ul ul ul li div.treetoggle {
    left: 64px;
}
.folderlist li.mailbox ul ul ul ul li.mailbox a {
    padding-left: 100px;  /* 4x */
    background-position: 70px -95px;
}
.folderlist li.mailbox ul ul ul ul li > a:focus,
.folderlist li.mailbox ul ul ul ul li.selected > a {
    background-position: 70px -119px;
}
.folderlist li.mailbox ul ul ul ul li div.treetoggle {
    left: 80px;
}
/* indent folders on levels > 4 */
.folderlist li.mailbox ul ul ul ul ul li {
    padding-left: 16px;
}
.folderlist li.mailbox ul ul ul ul ul li div.treetoggle {
    left: 96px;
}
/*** attachment list ***/
.attachmentslist {
skins/larry/templates/folders.html
@@ -19,7 +19,7 @@
<div id="folderslist" class="uibox listbox">
<h2 id="folderslist-header" class="boxtitle"><span style="float:right"><roundcube:label name="subscribed" /></span><roundcube:label name="folders" /></h2>
<div id="folderslist-content" class="scroller withfooter">
    <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="listing" />
    <roundcube:object name="foldersubscription" form="subscriptionform" id="subscription-table" class="treelist listing folderlist" />
</div>
<div id="folderslist-footer" class="boxfooter">
    <roundcube:button command="create-folder" type="link" title="createfolder" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="createfolder" /><roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="return UI.toggle_popup('mailboxmenu',event)" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="mailboxoptionsmenu" />
skins/larry/templates/mail.html
@@ -70,7 +70,7 @@
<div id="mailboxcontainer" class="uibox listbox" role="navigation" aria-labelledby="aria-label-folderlist">
<h2 id="aria-label-folderlist" class="voice"><roundcube:label name="arialabelfolderlist" /></h2>
<div id="folderlist-content" class="scroller withfooter">
<roundcube:object name="mailboxlist" id="mailboxlist" class="treelist listing" folder_filter="mail" unreadwrap="%s" />
    <roundcube:object name="mailboxlist" id="mailboxlist" class="treelist listing folderlist" folder_filter="mail" unreadwrap="%s" />
</div>
<div id="folderlist-footer" class="boxfooter">
    <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="listbutton groupactions" onclick="UI.toggle_popup('mailboxmenu',event);return false" innerClass="inner" content="&#9881;" aria-haspopup="true" aria-expanded="false" aria-owns="mailboxoptionsmenu" />