summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoralecpl <alec@alec.pl>2011-05-20 10:38:44 +0000
committeralecpl <alec@alec.pl>2011-05-20 10:38:44 +0000
commit254d5ef32b7ec45a48abd43f19c84168dabe13d1 (patch)
tree4b8551073ec4ac519856f9f04049d3c5b1d7dd40
parent3253b296c21c54df228de39ff3e4775974df81d5 (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--CHANGELOG1
-rw-r--r--program/js/app.js261
-rw-r--r--program/steps/settings/edit_folder.inc6
-rw-r--r--program/steps/settings/folders.inc44
-rw-r--r--program/steps/settings/func.inc23
-rw-r--r--program/steps/settings/save_folder.inc8
6 files changed, 201 insertions, 142 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 53515f6d0..4958bf8ef 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -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(/^&nbsp;&nbsp;&nbsp;&nbsp;/, '');
+ }
+ else {
+ for (i=level; i<0; i++)
+ dispname = '&nbsp;&nbsp;&nbsp;&nbsp;' + 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('&nbsp;&nbsp;&nbsp;&nbsp;', $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('&nbsp;&nbsp;&nbsp;&nbsp;', $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('&nbsp;&nbsp;&nbsp;&nbsp;', $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');
}
}