diff options
author | alecpl <alec@alec.pl> | 2011-05-20 10:38:44 +0000 |
---|---|---|
committer | alecpl <alec@alec.pl> | 2011-05-20 10:38:44 +0000 |
commit | 254d5ef32b7ec45a48abd43f19c84168dabe13d1 (patch) | |
tree | 4b8551073ec4ac519856f9f04049d3c5b1d7dd40 | |
parent | 3253b296c21c54df228de39ff3e4775974df81d5 (diff) |
- Improve performence of folder manager operations by moving subscriptions table operations (like adding/updateing/moving folders) into client-side - no need to invoke LIST, do sorting in browser
- This change should also handle better situations when working with replicated IMAP backend (e.g.Cyrus Murder)
-rw-r--r-- | CHANGELOG | 1 | ||||
-rw-r--r-- | program/js/app.js | 261 | ||||
-rw-r--r-- | program/steps/settings/edit_folder.inc | 6 | ||||
-rw-r--r-- | program/steps/settings/folders.inc | 44 | ||||
-rw-r--r-- | program/steps/settings/func.inc | 23 | ||||
-rw-r--r-- | program/steps/settings/save_folder.inc | 8 |
6 files changed, 201 insertions, 142 deletions
@@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Improve performence of folder manager operations - Fix default_port option handling in Installer when config.inc.php file exists (#1487925) - Removed option focus_on_new_message, added newmail_notifier plugin - Added general rcube_cache class with Memcache and APC support diff --git a/program/js/app.js b/program/js/app.js index a114c80d6..ea54b0b78 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -4016,7 +4016,6 @@ function rcube_webmail() this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:li[0], newid:prop.newid }); }; - this.init_edit_field = function(col, elem) { if (!elem) @@ -4179,21 +4178,6 @@ function rcube_webmail() /********* user settings methods *********/ /*********************************************************/ - this.init_subscription_list = function() - { - var p = this; - this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, - {multiselect:false, draggable:true, keyboard:false, toggleselect:true}); - this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); }); - this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); - this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); }); - this.subscription_list.row_init = function (row) { - row.obj.onmouseover = function() { p.focus_subscription(row.id); }; - row.obj.onmouseout = function() { p.unfocus_subscription(row.id); }; - }; - this.subscription_list.init(); - }; - // preferences section select and load options frame this.section_select = function(list) { @@ -4258,6 +4242,26 @@ function rcube_webmail() return true; }; + + /*********************************************************/ + /********* folder manager methods *********/ + /*********************************************************/ + + this.init_subscription_list = function() + { + var p = this; + this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist, + {multiselect:false, draggable:true, keyboard:false, toggleselect:true}); + this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); }); + this.subscription_list.addEventListener('dragstart', function(o){ p.drag_active = true; }); + this.subscription_list.addEventListener('dragend', function(o){ p.subscription_move_folder(o); }); + this.subscription_list.row_init = function (row) { + row.obj.onmouseover = function() { p.focus_subscription(row.id); }; + row.obj.onmouseout = function() { p.unfocus_subscription(row.id); }; + }; + this.subscription_list.init(); + }; + this.focus_subscription = function(id) { var row, folder, @@ -4347,91 +4351,183 @@ function rcube_webmail() } }; - // add a new folder to the subscription list by cloning a folder row - this.add_folder_row = function(name, display_name, replace, before) + // Add folder row to the table and initialize it + this.add_folder_row = function (name, display_name, protected, subscribed, skip_init) { if (!this.gui_objects.subscriptionlist) return false; - // find not protected folder - var refid; - for (var rid in this.env.subscriptionrows) { - if (this.env.subscriptionrows[rid]!=null && !this.env.subscriptionrows[rid][2]) { - refid = rid; - break; - } - } - - var refrow, form, + var row, n, i, tmp, folders, len, list = [], slist = [], tbody = this.gui_objects.subscriptionlist.tBodies[0], - id = 'rcmrow'+(tbody.childNodes.length+1), - selection = this.subscription_list.get_single_selection(); - - if (replace && replace.id) { - id = replace.id; - refid = replace.id; - } + refrow = $('tr', tbody).get(0), + id = 'rcmrow'+((new Date).getTime()); - if (!id || !refid || !(refrow = document.getElementById(refid))) { + if (!refrow) { // 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 - var row = this.clone_table_row(refrow); - row.id = id; + row = $(refrow).clone(true); + row.attr('id', id); - if (before && (before = this.get_folder_row_id(before))) - tbody.insertBefore(row, document.getElementById(before)); - else - tbody.appendChild(row); + // set folder name + row.find('td:first').html(display_name); - if (replace) - tbody.removeChild(replace); + // update subscription checkbox + $('input[name="_subscribed[]"]', row).val(name) + .prop({checked: subscribed ? true : false, disabled: protected ? true : false}); // add to folder/row-ID map - this.env.subscriptionrows[row.id] = [name, display_name, 0]; + this.env.subscriptionrows[id] = [name, display_name, 0]; + + // sort folders, to find a place where to insert the row + folders = this.env.subscriptionrows; + for (n in folders) { + // protected folder + if (folders[n][2]) { + slist.push(folders[n][0]); + tmp = folders[n][0]+this.env.delimiter; + } + // protected folder's child + else if (tmp && folders[n][0].indexOf(tmp) == 0) + slist.push(folders[n][0]); + // other + else { + list.push(folders[n][0]); + tmp = null; + } + } + list.sort(); + // make sure protected folders (and their subs) are on top + list = slist.concat(list); - // set folder name - row.cells[0].innerHTML = display_name; + // find folder position after sorting + for (n=0, len=list.length; n<len; n++) { + if (list[n] == name) + break; + } - if (!replace) { - // set messages count to zero - row.cells[1].innerHTML = '*'; + // add row to the table + if (n && n < len) + $('#'+this.get_folder_row_id(list[n-1])).after(row); + else + row.appendTo(tbody); - // update subscription checkbox - $('input[name="_subscribed[]"]', row).val(name).prop('checked', true); - } + // update list widget + this.subscription_list.clear_selection(); + if (!skip_init) + this.init_subscription_list(); - this.init_subscription_list(); - if (selection && document.getElementById('rcmrow'+selection)) - this.subscription_list.select_row(selection); + row = row.get(0); + if (row.scrollIntoView) + row.scrollIntoView(); - if (document.getElementById(id).scrollIntoView) - document.getElementById(id).scrollIntoView(); + return row; }; - // replace an existing table row with a new folder line - this.replace_folder_row = function(oldfolder, newfolder, display_name, before) + // replace an existing table row with a new folder line (with subfolders) + this.replace_folder_row = function(oldfolder, newfolder, display_name, protected) { - var id = this.get_folder_row_id(oldfolder), - row = document.getElementById(id); + if (!this.gui_objects.subscriptionlist) + return false; - // replace an existing table row (if found) - this.add_folder_row(newfolder, display_name, row, before); + var i, n, len, name, dispname, oldrow, tmprow, row, level, + tbody = this.gui_objects.subscriptionlist.tBodies[0], + folders = this.env.subscriptionrows, + id = this.get_folder_row_id(oldfolder), + regex = new RegExp('^'+RegExp.escape(oldfolder)), + subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'), + // find subfolders of renamed folder + list = this.get_subfolders(oldfolder); + + // replace an existing table row + this._remove_folder_row(id); + row = $(this.add_folder_row(newfolder, display_name, protected, subscribed, true)); + + // detect tree depth change + if (len = list.length) { + level = (oldfolder.split(this.env.delimiter)).length - (newfolder.split(this.env.delimiter)).length; + } + + // 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 = name.replace(regex, newfolder); + $('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(/^ /, ''); + } + else { + for (i=level; i<0; i++) + dispname = ' ' + dispname; + } + row.find('td:first').html(dispname); + this.env.subscriptionrows[id][1] = dispname; + } + } + + // update list widget + this.init_subscription_list(); }; // remove the table row of a specific mailbox from the table - // (the row will not be removed, just hidden) - this.remove_folder_row = function(folder) + this.remove_folder_row = function(folder, subs) { - var row, id = this.get_folder_row_id(folder); + var n, len, list = [], id = this.get_folder_row_id(folder); + + // get subfolders if any + if (subs) + list = this.get_subfolders(folder); + + // remove old row + this._remove_folder_row(id); - if (id && (row = document.getElementById(id))) - row.style.display = 'none'; + // 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_row(id.replace(/^rcmrow/, '')); + $('#'+id).remove(); + delete this.env.subscriptionrows[id]; + } + + this.get_subfolders = function(folder) + { + var name, list = [], + regex = new RegExp('^'+RegExp.escape(folder)+RegExp.escape(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 (regex.test(name)) { + list.push(row.id); + } + else + break; + } + } + + return list; + } + this.subscribe = function(folder) { if (folder) { @@ -4451,35 +4547,14 @@ function rcube_webmail() // helper method to find a specific mailbox row ID this.get_folder_row_id = function(folder) { - for (var id in this.env.subscriptionrows) - if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder) + var id, folders = this.env.subscriptionrows; + for (id in folders) + if (folders[id] && folders[id][0] == folder) break; return id; }; - // duplicate a specific table row - this.clone_table_row = function(row) - { - var cell, td, - new_row = document.createElement('tr'); - - for (var n=0; n<row.cells.length; n++) { - cell = row.cells[n]; - td = document.createElement('td'); - - if (cell.className) - td.className = cell.className; - if (cell.align) - td.setAttribute('align', cell.align); - - td.innerHTML = cell.innerHTML; - new_row.appendChild(td); - } - - return new_row; - }; - // when user select a folder in manager this.show_folder = function(folder, path, force) { diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc index bc4310310..0bc7ab657 100644 --- a/program/steps/settings/edit_folder.inc +++ b/program/steps/settings/edit_folder.inc @@ -24,7 +24,7 @@ // init IMAP connection $RCMAIL->imap_connect(); -function rcube_folder_form($attrib) +function rcmail_folder_form($attrib) { global $RCMAIL; @@ -41,7 +41,7 @@ function rcube_folder_form($attrib) // Get mailbox parameters if (strlen($mbox)) { - $options = rcube_folder_options($mbox_imap); + $options = rcmail_folder_options($mbox_imap); $namespace = $RCMAIL->imap->get_namespace(); $path = explode($delimiter, $mbox_imap); @@ -319,7 +319,7 @@ function rcmail_localize_folderpath($path) // register UI objects $OUTPUT->add_handlers(array( - 'folderdetails' => 'rcube_folder_form', + 'folderdetails' => 'rcmail_folder_form', )); $OUTPUT->add_label('nonamewarning'); diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index bc95c7506..a9068099b 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -76,24 +76,12 @@ else if ($RCMAIL->action == 'delete-folder') $mbox_utf8 = get_input_value('_mbox', RCUBE_INPUT_POST, true); $mbox = rcube_charset_convert($mbox_utf8, RCMAIL_CHARSET, 'UTF7-IMAP'); - // get folder's children or all folders if the name contains special characters - $delimiter = $IMAP->get_hierarchy_delimiter(); - if ((strpos($mbox, '%') === false) && (strpos($mbox, '*') === false)) - $a_mboxes = $IMAP->list_unsubscribed('', $mbox.$delimiter.'*'); - else - $a_mboxes = $IMAP->list_unsubscribed(); - if (strlen($mbox)) $deleted = $IMAP->delete_mailbox($mbox); if ($OUTPUT->ajax_call && $deleted) { // Remove folder and subfolders rows - $OUTPUT->command('remove_folder_row', $mbox_utf8); - foreach ($a_mboxes as $folder) { - if (preg_match('/^'. preg_quote($mbox.$delimiter, '/') .'/', $folder)) { - $OUTPUT->command('remove_folder_row', rcube_charset_convert($folder, 'UTF7-IMAP')); - } - } + $OUTPUT->command('remove_folder_row', $mbox_utf8, true); $OUTPUT->show_message('folderdeleted', 'confirmation'); // Clear content frame $OUTPUT->command('subscription_select'); @@ -118,34 +106,7 @@ else if ($RCMAIL->action == 'rename-folder') } if ($rename && $OUTPUT->ajax_call) { - $folderlist = $IMAP->list_unsubscribed(); - $delimiter = $IMAP->get_hierarchy_delimiter(); - - $regexp = '/^' . preg_quote($name . $delimiter, '/') . '/'; - - // subfolders - for ($x=sizeof($folderlist)-1; $x>=0; $x--) { - if (preg_match($regexp, $folderlist[$x])) { - $oldfolder = $oldname . $delimiter . preg_replace($regexp, '', $folderlist[$x]); - $foldersplit = explode($delimiter, $IMAP->mod_mailbox($folderlist[$x])); - $level = count($foldersplit) - 1; - $display_rename = str_repeat(' ', $level) - . rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'); - $before = isset($folderlist[$x+1]) ? rcube_charset_convert($folderlist[$x+1], 'UTF7-IMAP') : false; - - $OUTPUT->command('replace_folder_row', rcube_charset_convert($oldfolder, 'UTF7-IMAP'), - rcube_charset_convert($folderlist[$x], 'UTF7-IMAP'), $display_rename, $before); - } - } - - $index = array_search($name, $folderlist); - $name = $IMAP->mod_mailbox($name); - $foldersplit = explode($delimiter, $name); - $level = count($foldersplit) - 1; - $display_rename = str_repeat(' ', $level) . rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'); - $before = $index !== false && isset($folderlist[$index+1]) ? rcube_charset_convert($folderlist[$index+1], 'UTF7-IMAP') : false; - - $OUTPUT->command('replace_folder_row', $oldname_utf8, $name_utf8, $display_rename, $before); + rcmail_update_folder_row($name, $oldname); } else if (!$rename) { rcmail_display_server_error('errorsaving'); @@ -375,6 +336,7 @@ function rcmail_rename_folder($oldname, $newname) return false; } + $OUTPUT->set_pagetitle(rcube_label('folders')); $OUTPUT->include_script('list.js'); $OUTPUT->set_env('quota', $IMAP->get_capability('QUOTA')); diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index b204d9bc9..a44d6c8ff 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -747,7 +747,7 @@ function rcmail_get_skins() } -function rcube_folder_options($mailbox) +function rcmail_folder_options($mailbox) { global $RCMAIL; @@ -785,6 +785,27 @@ function rcube_folder_options($mailbox) return $options; } +// Updates (or creates) folder row in the subscriptions table +function rcmail_update_folder_row($name, $oldname=null) +{ + global $IMAP, $CONFIG, $OUTPUT; + + $delimiter = $IMAP->get_hierarchy_delimiter(); + $name_utf8 = rcube_charset_convert($name, 'UTF7-IMAP'); + $protected = ($CONFIG['protect_default_folders'] == true && in_array($name, $CONFIG['default_imap_folders'])); + + $foldersplit = explode($delimiter, $IMAP->mod_mailbox($name)); + $level = count($foldersplit) - 1; + $display_name = str_repeat(' ', $level) + . Q($protected ? rcmail_localize_foldername($name) : rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP')); + + if ($oldname === null) + $OUTPUT->command('add_folder_row', $name_utf8, $display_name, $protected, true); + else + $OUTPUT->command('replace_folder_row', rcube_charset_convert($oldname, 'UTF7-IMAP'), + $name_utf8, $display_name, $protected); +} + // register UI objects $OUTPUT->add_handlers(array( diff --git a/program/steps/settings/save_folder.inc b/program/steps/settings/save_folder.inc index c1120961b..a4e752c90 100644 --- a/program/steps/settings/save_folder.inc +++ b/program/steps/settings/save_folder.inc @@ -34,7 +34,7 @@ $old_imap = rcube_charset_convert($old, RCMAIL_CHARSET, 'UTF7-IMAP'); // $path is in UTF7-IMAP already $delimiter = $IMAP->get_hierarchy_delimiter(); -$options = strlen($old_imap) ? rcube_folder_options($old_imap) : array(); +$options = strlen($old_imap) ? rcmail_folder_options($old_imap) : array(); // Folder name checks if ($options['protected'] || $options['norename']) { @@ -105,9 +105,9 @@ if (!$error && !strlen($old)) { $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); } - + + rcmail_update_folder_row($folder['name']); $OUTPUT->show_message('foldercreated', 'confirmation'); - $OUTPUT->command('reload', 250); $OUTPUT->send('iframe'); } else { @@ -163,7 +163,7 @@ else if (!$error) { $OUTPUT->show_message('folderupdated', 'confirmation'); if ($rename) { - $OUTPUT->command('reload', 250); + rcmail_update_folder_row($folder['name'], $folder['oldname']); $OUTPUT->send('iframe'); } } |