Changeset 4793 in subversion


Ignore:
Timestamp:
May 20, 2011 6:38:44 AM (2 years ago)
Author:
alec
Message:
  • 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)
Location:
trunk/roundcubemail
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r4789 r4793  
    22=========================== 
    33 
     4- Improve performence of folder manager operations 
    45- Fix default_port option handling in Installer when config.inc.php file exists (#1487925) 
    56- Removed option focus_on_new_message, added newmail_notifier plugin 
  • trunk/roundcubemail/program/js/app.js

    r4788 r4793  
    40174017  }; 
    40184018 
    4019  
    40204019  this.init_edit_field = function(col, elem) 
    40214020  { 
     
    41804179  /*********************************************************/ 
    41814180 
     4181  // preferences section select and load options frame 
     4182  this.section_select = function(list) 
     4183  { 
     4184    var id = list.get_single_selection(); 
     4185 
     4186    if (id) { 
     4187      var add_url = '', target = window; 
     4188      this.set_busy(true); 
     4189 
     4190      if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { 
     4191        add_url = '&_framed=1'; 
     4192        target = window.frames[this.env.contentframe]; 
     4193      } 
     4194      this.location_href(this.env.comm_path+'&_action=edit-prefs&_section='+id+add_url, target); 
     4195    } 
     4196 
     4197    return true; 
     4198  }; 
     4199 
     4200  this.identity_select = function(list) 
     4201  { 
     4202    var id; 
     4203    if (id = list.get_single_selection()) 
     4204      this.load_identity(id, 'edit-identity'); 
     4205  }; 
     4206 
     4207  // load identity record 
     4208  this.load_identity = function(id, action) 
     4209  { 
     4210    if (action=='edit-identity' && (!id || id==this.env.iid)) 
     4211      return false; 
     4212 
     4213    var add_url = '', target = window; 
     4214 
     4215    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { 
     4216      add_url = '&_framed=1'; 
     4217      target = window.frames[this.env.contentframe]; 
     4218      document.getElementById(this.env.contentframe).style.visibility = 'inherit'; 
     4219    } 
     4220 
     4221    if (action && (id || action=='add-identity')) { 
     4222      this.set_busy(true); 
     4223      this.location_href(this.env.comm_path+'&_action='+action+'&_iid='+id+add_url, target); 
     4224    } 
     4225 
     4226    return true; 
     4227  }; 
     4228 
     4229  this.delete_identity = function(id) 
     4230  { 
     4231    // exit if no mailbox specified or if selection is empty 
     4232    var selection = this.identity_list.get_selection(); 
     4233    if (!(selection.length || this.env.iid)) 
     4234      return; 
     4235 
     4236    if (!id) 
     4237      id = this.env.iid ? this.env.iid : selection[0]; 
     4238 
     4239    // append token to request 
     4240    this.goto_url('delete-identity', '_iid='+id+'&_token='+this.env.request_token, true); 
     4241 
     4242    return true; 
     4243  }; 
     4244 
     4245 
     4246  /*********************************************************/ 
     4247  /*********        folder manager methods         *********/ 
     4248  /*********************************************************/ 
     4249 
    41824250  this.init_subscription_list = function() 
    41834251  { 
     
    41954263  }; 
    41964264 
    4197   // preferences section select and load options frame 
    4198   this.section_select = function(list) 
    4199   { 
    4200     var id = list.get_single_selection(); 
    4201  
    4202     if (id) { 
    4203       var add_url = '', target = window; 
    4204       this.set_busy(true); 
    4205  
    4206       if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { 
    4207         add_url = '&_framed=1'; 
    4208         target = window.frames[this.env.contentframe]; 
    4209       } 
    4210       this.location_href(this.env.comm_path+'&_action=edit-prefs&_section='+id+add_url, target); 
    4211     } 
    4212  
    4213     return true; 
    4214   }; 
    4215  
    4216   this.identity_select = function(list) 
    4217   { 
    4218     var id; 
    4219     if (id = list.get_single_selection()) 
    4220       this.load_identity(id, 'edit-identity'); 
    4221   }; 
    4222  
    4223   // load identity record 
    4224   this.load_identity = function(id, action) 
    4225   { 
    4226     if (action=='edit-identity' && (!id || id==this.env.iid)) 
    4227       return false; 
    4228  
    4229     var add_url = '', target = window; 
    4230  
    4231     if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) { 
    4232       add_url = '&_framed=1'; 
    4233       target = window.frames[this.env.contentframe]; 
    4234       document.getElementById(this.env.contentframe).style.visibility = 'inherit'; 
    4235     } 
    4236  
    4237     if (action && (id || action=='add-identity')) { 
    4238       this.set_busy(true); 
    4239       this.location_href(this.env.comm_path+'&_action='+action+'&_iid='+id+add_url, target); 
    4240     } 
    4241  
    4242     return true; 
    4243   }; 
    4244  
    4245   this.delete_identity = function(id) 
    4246   { 
    4247     // exit if no mailbox specified or if selection is empty 
    4248     var selection = this.identity_list.get_selection(); 
    4249     if (!(selection.length || this.env.iid)) 
    4250       return; 
    4251  
    4252     if (!id) 
    4253       id = this.env.iid ? this.env.iid : selection[0]; 
    4254  
    4255     // append token to request 
    4256     this.goto_url('delete-identity', '_iid='+id+'&_token='+this.env.request_token, true); 
    4257  
    4258     return true; 
    4259   }; 
    4260  
    42614265  this.focus_subscription = function(id) 
    42624266  { 
     
    43484352  }; 
    43494353 
    4350   // add a new folder to the subscription list by cloning a folder row 
    4351   this.add_folder_row = function(name, display_name, replace, before) 
     4354  // Add folder row to the table and initialize it 
     4355  this.add_folder_row = function (name, display_name, protected, subscribed, skip_init) 
    43524356  { 
    43534357    if (!this.gui_objects.subscriptionlist) 
    43544358      return false; 
    43554359 
    4356     // find not protected folder 
    4357     var refid; 
    4358     for (var rid in this.env.subscriptionrows) { 
    4359       if (this.env.subscriptionrows[rid]!=null && !this.env.subscriptionrows[rid][2]) { 
    4360         refid = rid; 
    4361         break; 
    4362       } 
    4363     } 
    4364  
    4365     var refrow, form, 
     4360    var row, n, i, tmp, folders, len, list = [], slist = [], 
    43664361      tbody = this.gui_objects.subscriptionlist.tBodies[0], 
    4367       id = 'rcmrow'+(tbody.childNodes.length+1), 
    4368       selection = this.subscription_list.get_single_selection(); 
    4369  
    4370     if (replace && replace.id) { 
    4371       id = replace.id; 
    4372       refid = replace.id; 
    4373     } 
    4374  
    4375     if (!id || !refid || !(refrow = document.getElementById(refid))) { 
     4362      refrow = $('tr', tbody).get(0), 
     4363      id = 'rcmrow'+((new Date).getTime()); 
     4364 
     4365    if (!refrow) { 
    43764366      // Refresh page if we don't have a table row to clone 
    43774367      this.goto_url('folders'); 
     
    43804370 
    43814371    // clone a table row if there are existing rows 
    4382     var row = this.clone_table_row(refrow); 
    4383     row.id = id; 
    4384  
    4385     if (before && (before = this.get_folder_row_id(before))) 
    4386       tbody.insertBefore(row, document.getElementById(before)); 
     4372    row    = $(refrow).clone(true); 
     4373    row.attr('id', id); 
     4374 
     4375    // set folder name 
     4376    row.find('td:first').html(display_name); 
     4377 
     4378    // update subscription checkbox 
     4379    $('input[name="_subscribed[]"]', row).val(name) 
     4380      .prop({checked: subscribed ? true : false, disabled: protected ? true : false}); 
     4381 
     4382    // add to folder/row-ID map 
     4383    this.env.subscriptionrows[id] = [name, display_name, 0]; 
     4384 
     4385    // sort folders, to find a place where to insert the row 
     4386    folders = this.env.subscriptionrows; 
     4387    for (n in folders) { 
     4388      // protected folder 
     4389      if (folders[n][2]) { 
     4390        slist.push(folders[n][0]); 
     4391        tmp = folders[n][0]+this.env.delimiter; 
     4392      } 
     4393      // protected folder's child 
     4394      else if (tmp && folders[n][0].indexOf(tmp) == 0) 
     4395        slist.push(folders[n][0]); 
     4396      // other 
     4397      else { 
     4398        list.push(folders[n][0]); 
     4399        tmp = null; 
     4400      } 
     4401    } 
     4402    list.sort(); 
     4403    // make sure protected folders (and their subs) are on top 
     4404    list = slist.concat(list); 
     4405 
     4406    // find folder position after sorting 
     4407    for (n=0, len=list.length; n<len; n++) { 
     4408      if (list[n] == name) 
     4409        break; 
     4410    } 
     4411 
     4412    // add row to the table 
     4413    if (n && n < len) 
     4414      $('#'+this.get_folder_row_id(list[n-1])).after(row); 
    43874415    else 
    4388       tbody.appendChild(row); 
    4389  
    4390     if (replace) 
    4391       tbody.removeChild(replace); 
    4392  
    4393     // add to folder/row-ID map 
    4394     this.env.subscriptionrows[row.id] = [name, display_name, 0]; 
    4395  
    4396     // set folder name 
    4397     row.cells[0].innerHTML = display_name; 
    4398  
    4399     if (!replace) { 
    4400       // set messages count to zero 
    4401       row.cells[1].innerHTML = '*'; 
    4402  
    4403       // update subscription checkbox 
    4404       $('input[name="_subscribed[]"]', row).val(name).prop('checked', true); 
    4405     } 
    4406  
     4416      row.appendTo(tbody); 
     4417 
     4418    // update list widget 
     4419    this.subscription_list.clear_selection(); 
     4420    if (!skip_init) 
     4421      this.init_subscription_list(); 
     4422 
     4423    row = row.get(0); 
     4424    if (row.scrollIntoView) 
     4425      row.scrollIntoView(); 
     4426 
     4427    return row; 
     4428  }; 
     4429 
     4430  // replace an existing table row with a new folder line (with subfolders) 
     4431  this.replace_folder_row = function(oldfolder, newfolder, display_name, protected) 
     4432  { 
     4433    if (!this.gui_objects.subscriptionlist) 
     4434      return false; 
     4435 
     4436    var i, n, len, name, dispname, oldrow, tmprow, row, level, 
     4437      tbody = this.gui_objects.subscriptionlist.tBodies[0], 
     4438      folders = this.env.subscriptionrows, 
     4439      id = this.get_folder_row_id(oldfolder), 
     4440      regex = new RegExp('^'+RegExp.escape(oldfolder)), 
     4441      subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'), 
     4442      // find subfolders of renamed folder 
     4443      list = this.get_subfolders(oldfolder); 
     4444 
     4445    // replace an existing table row 
     4446    this._remove_folder_row(id); 
     4447    row = $(this.add_folder_row(newfolder, display_name, protected, subscribed, true)); 
     4448 
     4449    // detect tree depth change 
     4450    if (len = list.length) { 
     4451      level = (oldfolder.split(this.env.delimiter)).length - (newfolder.split(this.env.delimiter)).length; 
     4452    } 
     4453 
     4454    // move subfolders to the new branch 
     4455    for (n=0; n<len; n++) { 
     4456      id = list[n]; 
     4457      name = this.env.subscriptionrows[id][0]; 
     4458      dispname = this.env.subscriptionrows[id][1]; 
     4459      oldrow = $('#'+id); 
     4460      tmprow = oldrow.clone(true); 
     4461      oldrow.remove(); 
     4462      row.after(tmprow); 
     4463      row = tmprow; 
     4464      // update folder index 
     4465      name = name.replace(regex, newfolder); 
     4466      $('input[name="_subscribed[]"]', row).val(name); 
     4467      this.env.subscriptionrows[id][0] = name; 
     4468      // update the name if level is changed 
     4469      if (level != 0) { 
     4470        if (level > 0) { 
     4471          for (i=level; i>0; i--) 
     4472            dispname = dispname.replace(/^&nbsp;&nbsp;&nbsp;&nbsp;/, ''); 
     4473        } 
     4474        else { 
     4475          for (i=level; i<0; i++) 
     4476            dispname = '&nbsp;&nbsp;&nbsp;&nbsp;' + dispname; 
     4477        } 
     4478        row.find('td:first').html(dispname); 
     4479        this.env.subscriptionrows[id][1] = dispname; 
     4480      } 
     4481    } 
     4482 
     4483    // update list widget 
    44074484    this.init_subscription_list(); 
    4408     if (selection && document.getElementById('rcmrow'+selection)) 
    4409       this.subscription_list.select_row(selection); 
    4410  
    4411     if (document.getElementById(id).scrollIntoView) 
    4412       document.getElementById(id).scrollIntoView(); 
    4413   }; 
    4414  
    4415   // replace an existing table row with a new folder line 
    4416   this.replace_folder_row = function(oldfolder, newfolder, display_name, before) 
    4417   { 
    4418     var id = this.get_folder_row_id(oldfolder), 
    4419       row = document.getElementById(id); 
    4420  
    4421     // replace an existing table row (if found) 
    4422     this.add_folder_row(newfolder, display_name, row, before); 
    44234485  }; 
    44244486 
    44254487  // remove the table row of a specific mailbox from the table 
    4426   // (the row will not be removed, just hidden) 
    4427   this.remove_folder_row = function(folder) 
    4428   { 
    4429     var row, id = this.get_folder_row_id(folder); 
    4430  
    4431     if (id && (row = document.getElementById(id))) 
    4432       row.style.display = 'none'; 
    4433   }; 
     4488  this.remove_folder_row = function(folder, subs) 
     4489  { 
     4490    var n, len, list = [], id = this.get_folder_row_id(folder); 
     4491 
     4492    // get subfolders if any 
     4493    if (subs) 
     4494      list = this.get_subfolders(folder); 
     4495 
     4496    // remove old row 
     4497    this._remove_folder_row(id); 
     4498 
     4499    // remove subfolders 
     4500    for (n=0, len=list.length; n<len; n++) 
     4501      this._remove_folder_row(list[n]); 
     4502  }; 
     4503 
     4504  this._remove_folder_row = function(id) 
     4505  { 
     4506    this.subscription_list.remove_row(id.replace(/^rcmrow/, '')); 
     4507    $('#'+id).remove(); 
     4508    delete this.env.subscriptionrows[id]; 
     4509  } 
     4510 
     4511  this.get_subfolders = function(folder) 
     4512  { 
     4513    var name, list = [], 
     4514      regex = new RegExp('^'+RegExp.escape(folder)+RegExp.escape(this.env.delimiter)), 
     4515      row = $('#'+this.get_folder_row_id(folder)).get(0); 
     4516 
     4517    while (row = row.nextSibling) { 
     4518      if (row.id) { 
     4519        name = this.env.subscriptionrows[row.id][0]; 
     4520        if (regex.test(name)) { 
     4521          list.push(row.id); 
     4522        } 
     4523        else 
     4524          break; 
     4525      } 
     4526    } 
     4527 
     4528    return list; 
     4529  } 
    44344530 
    44354531  this.subscribe = function(folder) 
     
    44524548  this.get_folder_row_id = function(folder) 
    44534549  { 
    4454     for (var id in this.env.subscriptionrows) 
    4455       if (this.env.subscriptionrows[id] && this.env.subscriptionrows[id][0] == folder) 
     4550    var id, folders = this.env.subscriptionrows; 
     4551    for (id in folders) 
     4552      if (folders[id] && folders[id][0] == folder) 
    44564553        break; 
    44574554 
    44584555    return id; 
    4459   }; 
    4460  
    4461   // duplicate a specific table row 
    4462   this.clone_table_row = function(row) 
    4463   { 
    4464     var cell, td, 
    4465       new_row = document.createElement('tr'); 
    4466  
    4467     for (var n=0; n<row.cells.length; n++) { 
    4468       cell = row.cells[n]; 
    4469       td = document.createElement('td'); 
    4470  
    4471       if (cell.className) 
    4472         td.className = cell.className; 
    4473       if (cell.align) 
    4474         td.setAttribute('align', cell.align); 
    4475  
    4476       td.innerHTML = cell.innerHTML; 
    4477       new_row.appendChild(td); 
    4478     } 
    4479  
    4480     return new_row; 
    44814556  }; 
    44824557 
  • trunk/roundcubemail/program/steps/settings/edit_folder.inc

    r4739 r4793  
    2525$RCMAIL->imap_connect(); 
    2626 
    27 function rcube_folder_form($attrib) 
     27function rcmail_folder_form($attrib) 
    2828{ 
    2929    global $RCMAIL; 
     
    4242    // Get mailbox parameters 
    4343    if (strlen($mbox)) { 
    44         $options   = rcube_folder_options($mbox_imap); 
     44        $options   = rcmail_folder_options($mbox_imap); 
    4545        $namespace = $RCMAIL->imap->get_namespace(); 
    4646 
     
    320320// register UI objects 
    321321$OUTPUT->add_handlers(array( 
    322     'folderdetails' => 'rcube_folder_form', 
     322    'folderdetails' => 'rcmail_folder_form', 
    323323)); 
    324324 
  • trunk/roundcubemail/program/steps/settings/folders.inc

    r4743 r4793  
    7777    $mbox      = rcube_charset_convert($mbox_utf8, RCMAIL_CHARSET, 'UTF7-IMAP'); 
    7878 
    79     // get folder's children or all folders if the name contains special characters 
    80     $delimiter = $IMAP->get_hierarchy_delimiter(); 
    81     if ((strpos($mbox, '%') === false) && (strpos($mbox, '*') === false)) 
    82         $a_mboxes  = $IMAP->list_unsubscribed('', $mbox.$delimiter.'*'); 
    83     else 
    84         $a_mboxes  = $IMAP->list_unsubscribed(); 
    85  
    8679    if (strlen($mbox)) 
    8780        $deleted = $IMAP->delete_mailbox($mbox); 
     
    8982    if ($OUTPUT->ajax_call && $deleted) { 
    9083        // Remove folder and subfolders rows 
    91         $OUTPUT->command('remove_folder_row', $mbox_utf8); 
    92         foreach ($a_mboxes as $folder) { 
    93             if (preg_match('/^'. preg_quote($mbox.$delimiter, '/') .'/', $folder)) { 
    94                 $OUTPUT->command('remove_folder_row', rcube_charset_convert($folder, 'UTF7-IMAP')); 
    95             } 
    96         } 
     84        $OUTPUT->command('remove_folder_row', $mbox_utf8, true); 
    9785        $OUTPUT->show_message('folderdeleted', 'confirmation'); 
    9886        // Clear content frame 
     
    119107 
    120108    if ($rename && $OUTPUT->ajax_call) { 
    121         $folderlist = $IMAP->list_unsubscribed(); 
    122         $delimiter  = $IMAP->get_hierarchy_delimiter(); 
    123  
    124         $regexp = '/^' . preg_quote($name . $delimiter, '/') . '/'; 
    125  
    126         // subfolders 
    127         for ($x=sizeof($folderlist)-1; $x>=0; $x--) { 
    128             if (preg_match($regexp, $folderlist[$x])) { 
    129                 $oldfolder   = $oldname . $delimiter . preg_replace($regexp, '', $folderlist[$x]); 
    130                 $foldersplit = explode($delimiter, $IMAP->mod_mailbox($folderlist[$x])); 
    131                 $level       = count($foldersplit) - 1; 
    132                 $display_rename = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level)  
    133                     . rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'); 
    134                 $before = isset($folderlist[$x+1]) ? rcube_charset_convert($folderlist[$x+1], 'UTF7-IMAP') : false; 
    135  
    136                 $OUTPUT->command('replace_folder_row', rcube_charset_convert($oldfolder, 'UTF7-IMAP'), 
    137                     rcube_charset_convert($folderlist[$x], 'UTF7-IMAP'), $display_rename, $before); 
    138             } 
    139         } 
    140  
    141         $index       = array_search($name, $folderlist); 
    142         $name        = $IMAP->mod_mailbox($name); 
    143         $foldersplit = explode($delimiter, $name); 
    144         $level       = count($foldersplit) - 1; 
    145         $display_rename = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level) . rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP'); 
    146         $before         = $index !== false && isset($folderlist[$index+1]) ? rcube_charset_convert($folderlist[$index+1], 'UTF7-IMAP') : false; 
    147  
    148         $OUTPUT->command('replace_folder_row', $oldname_utf8, $name_utf8, $display_rename, $before); 
     109        rcmail_update_folder_row($name, $oldname); 
    149110    } 
    150111    else if (!$rename) { 
     
    376337} 
    377338 
     339 
    378340$OUTPUT->set_pagetitle(rcube_label('folders')); 
    379341$OUTPUT->include_script('list.js'); 
  • trunk/roundcubemail/program/steps/settings/func.inc

    r4788 r4793  
    748748 
    749749 
    750 function rcube_folder_options($mailbox) 
     750function rcmail_folder_options($mailbox) 
    751751{ 
    752752    global $RCMAIL; 
     
    784784 
    785785    return $options;     
     786} 
     787 
     788// Updates (or creates) folder row in the subscriptions table 
     789function rcmail_update_folder_row($name, $oldname=null) 
     790{ 
     791    global $IMAP, $CONFIG, $OUTPUT; 
     792 
     793    $delimiter    = $IMAP->get_hierarchy_delimiter(); 
     794    $name_utf8    = rcube_charset_convert($name, 'UTF7-IMAP'); 
     795    $protected    = ($CONFIG['protect_default_folders'] == true && in_array($name, $CONFIG['default_imap_folders'])); 
     796 
     797    $foldersplit  = explode($delimiter, $IMAP->mod_mailbox($name)); 
     798    $level        = count($foldersplit) - 1; 
     799    $display_name = str_repeat('&nbsp;&nbsp;&nbsp;&nbsp;', $level) 
     800        . Q($protected ? rcmail_localize_foldername($name) : rcube_charset_convert($foldersplit[$level], 'UTF7-IMAP')); 
     801 
     802    if ($oldname === null) 
     803        $OUTPUT->command('add_folder_row', $name_utf8, $display_name, $protected, true); 
     804    else 
     805        $OUTPUT->command('replace_folder_row', rcube_charset_convert($oldname, 'UTF7-IMAP'), 
     806            $name_utf8, $display_name, $protected); 
    786807} 
    787808 
  • trunk/roundcubemail/program/steps/settings/save_folder.inc

    r4733 r4793  
    3535 
    3636$delimiter = $IMAP->get_hierarchy_delimiter(); 
    37 $options = strlen($old_imap) ? rcube_folder_options($old_imap) : array(); 
     37$options = strlen($old_imap) ? rcmail_folder_options($old_imap) : array(); 
    3838 
    3939// Folder name checks 
     
    106106            $RCMAIL->user->save_prefs(array('message_threading' => $a_threaded)); 
    107107        } 
    108  
     108   
     109        rcmail_update_folder_row($folder['name']); 
    109110        $OUTPUT->show_message('foldercreated', 'confirmation'); 
    110         $OUTPUT->command('reload', 250); 
    111111        $OUTPUT->send('iframe'); 
    112112    } 
     
    164164        $OUTPUT->show_message('folderupdated', 'confirmation'); 
    165165        if ($rename) { 
    166             $OUTPUT->command('reload', 250); 
     166            rcmail_update_folder_row($folder['name'], $folder['oldname']); 
    167167            $OUTPUT->send('iframe'); 
    168168        } 
Note: See TracChangeset for help on using the changeset viewer.