- 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)
release-0.6
alecpl 13 years ago
parent 3253b296c2
commit 254d5ef32b

@ -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

@ -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)
{

@ -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');

@ -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'));

@ -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(

@ -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');
}
}

Loading…
Cancel
Save