add move/copy contact widget

pull/6127/head
PhilW 7 years ago
parent 83bd85677b
commit 489da28dab

@ -47,6 +47,7 @@ function rcube_webmail()
this.group2expand = {}; this.group2expand = {};
this.http_request_jobs = {}; this.http_request_jobs = {};
this.menu_stack = []; this.menu_stack = [];
this.entity_selectors = [];
// webmail client settings // webmail client settings
this.dblclick_time = 500; this.dblclick_time = 500;
@ -979,14 +980,14 @@ function rcube_webmail()
if (this.task == 'mail') if (this.task == 'mail')
this.move_messages(props, event); this.move_messages(props, event);
else if (this.task == 'addressbook') else if (this.task == 'addressbook')
this.move_contacts(props); this.move_contacts(props, event);
break; break;
case 'copy': case 'copy':
if (this.task == 'mail') if (this.task == 'mail')
this.copy_messages(props, event); this.copy_messages(props, event);
else if (this.task == 'addressbook') else if (this.task == 'addressbook')
this.copy_contacts(props); this.copy_contacts(props, event);
break; break;
case 'mark': case 'mark':
@ -5903,10 +5904,15 @@ function rcube_webmail()
} }
this.env.selection_sources = $.unique(this.env.selection_sources); this.env.selection_sources = $.unique(this.env.selection_sources);
var groupcount = 0;
if (source.groups)
$.each(this.env.contactgroups, function(){ if (this.source === ref.env.source) groupcount++ });
} }
// if a group is currently selected, and there is at least one contact selected // if a group is currently selected, and there is at least one contact selected
// thend we can enable the group-remove-selected command // thend we can enable the group-remove-selected command
this.enable_command('group-assign-selected', groupcount > 0 && selected && writable);
this.enable_command('group-remove-selected', this.env.group && selected && writable); this.enable_command('group-remove-selected', this.env.group && selected && writable);
this.enable_command('print', selected == 1); this.enable_command('print', selected == 1);
this.enable_command('export-selected', 'copy', selected > 0); this.enable_command('export-selected', 'copy', selected > 0);
@ -5953,6 +5959,9 @@ function rcube_webmail()
this.env.address_group_stack = index < 0 ? [] : this.env.address_group_stack.slice(0, index); this.env.address_group_stack = index < 0 ? [] : this.env.address_group_stack.slice(0, index);
// remove cached contact group selector
this.destroy_entity_selector('contactgroup-selector');
// make sure the current group is on top of the stack // make sure the current group is on top of the stack
if (this.env.group) { if (this.env.group) {
if (!search) search = {}; if (!search) search = {};
@ -6130,8 +6139,15 @@ function rcube_webmail()
}; };
// copy contact(s) to the specified target (group or directory) // copy contact(s) to the specified target (group or directory)
this.copy_contacts = function(to) this.copy_contacts = function(to, event)
{ {
if (!to) {
return this.addressbook_selector(event, function(to, obj) {
var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to];
ref.command('copy', to);
});
}
var dest = to.type == 'group' ? to.source : to.id, var dest = to.type == 'group' ? to.source : to.id,
source = this.env.source, source = this.env.source,
group = this.env.group ? this.env.group : '', group = this.env.group ? this.env.group : '',
@ -6164,8 +6180,15 @@ function rcube_webmail()
}; };
// move contact(s) to the specified target (group or directory) // move contact(s) to the specified target (group or directory)
this.move_contacts = function(to) this.move_contacts = function(to, event)
{ {
if (!to) {
return this.addressbook_selector(event, function(to, obj) {
var to = $(obj).data('source') ? ref.env.contactgroups['G' + $(obj).data('source') + $(obj).data('gid')] : ref.env.address_sources[to];
ref.command('move', to);
});
}
var dest = to.type == 'group' ? to.source : to.id, var dest = to.type == 'group' ? to.source : to.id,
source = this.env.source, source = this.env.source,
group = this.env.group ? this.env.group : ''; group = this.env.group ? this.env.group : '';
@ -6392,6 +6415,10 @@ function rcube_webmail()
var key = 'G'+prop.source+prop.id; var key = 'G'+prop.source+prop.id;
if (this.treelist.remove(key)) { if (this.treelist.remove(key)) {
// make sure there is no cached address book or contact group selectors
this.destroy_entity_selector('addressbook-selector');
this.destroy_entity_selector('contactgroup-selector');
this.triggerEvent('group_delete', { source:prop.source, id:prop.id }); this.triggerEvent('group_delete', { source:prop.source, id:prop.id });
delete this.env.contactfolders[key]; delete this.env.contactfolders[key];
delete this.env.contactgroups[key]; delete this.env.contactgroups[key];
@ -6401,6 +6428,12 @@ function rcube_webmail()
this.list_contacts(prop.source, 0); this.list_contacts(prop.source, 0);
}; };
//assign selected contacts to a group
this.group_assign_selected = function(props, obj, event)
{
this.contactgroup_selector(event, function(to) { ref.group_member_change('add', ref.contact_list.get_selection(), ref.env.source, to); });
};
//remove selected contacts from current active group //remove selected contacts from current active group
this.group_remove_selected = function() this.group_remove_selected = function()
{ {
@ -6416,6 +6449,7 @@ function rcube_webmail()
for (n=0; n<selection.length; n++) { for (n=0; n<selection.length; n++) {
id = selection[n]; id = selection[n];
this.contact_list.remove_row(id, (n == selection.length-1)); this.contact_list.remove_row(id, (n == selection.length-1));
this.contact_list.clear_selection();
} }
} }
}; };
@ -6434,6 +6468,10 @@ function rcube_webmail()
this.env.contactfolders[key] = this.env.contactgroups[key] = prop; this.env.contactfolders[key] = this.env.contactgroups[key] = prop;
this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup'); this.treelist.insert({ id:key, html:link, classes:['contactgroup'] }, prop.source, 'contactgroup');
// make sure there is no cached address book or contact group selectors
this.destroy_entity_selector('addressbook-selector');
this.destroy_entity_selector('contactgroup-selector');
this.triggerEvent('group_insert', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key) }); this.triggerEvent('group_insert', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key) });
}; };
@ -6476,6 +6514,10 @@ function rcube_webmail()
// update list node and re-sort it // update list node and re-sort it
this.treelist.update(key, newnode, true); this.treelist.update(key, newnode, true);
// make sure there is no cached address book or contact group selectors
this.destroy_entity_selector('addressbook-selector');
this.destroy_entity_selector('contactgroup-selector');
this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key), newid:prop.newid }); this.triggerEvent('group_update', { id:prop.id, source:prop.source, name:prop.name, li:this.treelist.get_item(key), newid:prop.newid });
}; };
@ -8174,48 +8216,113 @@ function rcube_webmail()
elem.onclick = function() { ref.command('show-headers', '', elem); }; elem.onclick = function() { ref.command('show-headers', '', elem); };
}; };
// create folder selector popup, position and display it // create folder selector popup
this.folder_selector = function(event, callback) this.folder_selector = function(event, callback)
{ {
var container = this.folder_selector_element; this.entity_selector('folder-selector', callback, this.env.mailboxes_list, function(obj, a) {
var n = 0, s = 0,
delim = ref.env.delimiter,
folder = ref.env.mailboxes[obj],
id = folder.id,
row = $('<li>');
if (!container) { if (folder.virtual)
var rows = [], a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'});
delim = this.env.delimiter, else
ul = $('<ul class="toolbarmenu">'), a.addClass('active').data('id', folder.id);
link = document.createElement('a');
container = $('<div id="folder-selector" class="popupmenu"></div>'); if (folder['class'])
link.href = '#'; row.addClass(folder['class']);
link.className = 'icon';
// loop over sorted folders list // calculate/set indentation level
$.each(this.env.mailboxes_list, function() { while ((s = id.indexOf(delim, s)) >= 0) {
var n = 0, s = 0, n++; s++;
folder = ref.env.mailboxes[this], }
id = folder.id, a.css('padding-left', n ? (n * 16) + 'px' : 0);
a = $(link.cloneNode(false)).attr('rel', folder.id),
row = $('<li>');
if (folder.virtual) // add folder name element
a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'}); a.append($('<span>').text(folder.name));
else
a.addClass('active').data('id', folder.id); return row.append(a);
}, event);
};
// create addressbook selector popup
this.addressbook_selector = function(event, callback)
{
// build addressbook + groups list
var combined_sources = [];
if (folder['class']) // check we really need it before processing
row.addClass(folder['class']); if (!this.entity_selectors['addressbook-selector']) {
$.each(this.env.address_sources, function() {
if (!this.readonly) {
var source = this;
combined_sources.push(source);
// calculate/set indentation level $.each(ref.env.contactgroups, function() {
while ((s = id.indexOf(delim, s)) >= 0) { if (source.id === this.source) {
n++; s++; combined_sources.push(this);
}
});
} }
a.css('padding-left', n ? (n * 16) + 'px' : 0); });
}
this.entity_selector('addressbook-selector', callback, combined_sources, function(obj, a) {
var row = $('<li>');
if (obj.type == 'group') {
a.attr('rel', this.source + ':' + this.id);
a.addClass('contactgroup');
a.data('source', this.source).data('gid', this.id);
a.addClass('active').data('id', this.source + ':' + this.id);
a.css('padding-left', '16px');
}
else {
a.addClass('addressbook');
a.addClass('active').data('id', obj.id);
}
a.append($('<span>').text(obj.name));
// add folder name element return row.append(a);
a.append($('<span>').text(folder.name)); }, event);
};
row.append(a); // create contactgroup selector popup
rows.push(row); this.contactgroup_selector = function(event, callback)
{
this.entity_selector('contactgroup-selector', callback, this.env.contactgroups, function(obj, a) {
if (ref.env.source === obj.source) {
var row = $('<li>');
a.addClass('contactgroup');
a.addClass('active').data('id', obj.id);
a.append($('<span>').text(obj.name));
return row.append(a);
}
}, event);
};
// create selector popup (eg for folders or address books), position and display it
this.entity_selector = function(name, click_callback, entity_list, list_callback, event)
{
var container = this.entity_selectors[name];
if (!container) {
var rows = [],
container = $('<div>').attr('id', name).addClass('popupmenu'),
ul = $('<ul>').addClass('toolbarmenu'),
link = document.createElement('a');
link.href = '#';
link.className = 'icon';
// loop over entity list
$.each(entity_list, function() {
var a = $(link.cloneNode(false)).attr('rel', this.id);
rows.push(list_callback(this, a));
}); });
ul.append(rows).appendTo(container); ul.append(rows).appendTo(container);
@ -8230,18 +8337,23 @@ function rcube_webmail()
// register delegate event handler for folder item clicks // register delegate event handler for folder item clicks
container.on('click', 'a.active', function(e) { container.on('click', 'a.active', function(e) {
container.data('callback')($(this).data('id')); container.data('callback')($(this).data('id'), this);
}); });
this.folder_selector_element = container; this.entity_selectors[name] = container;
} }
container.data('callback', callback); container.data('callback', click_callback);
// position menu on the screen // position menu on the screen
this.show_menu('folder-selector', true, event); this.show_menu(name, true, event);
}; };
this.destroy_entity_selector = function(name)
{
$("#" + name).remove();
delete this.entity_selectors[name];
};
/***********************************************/ /***********************************************/
/********* popup menu functions *********/ /********* popup menu functions *********/

@ -381,6 +381,8 @@ $labels['newgroup'] = 'Create new group';
$labels['addgroup'] = 'Add group'; $labels['addgroup'] = 'Add group';
$labels['grouprename'] = 'Rename group'; $labels['grouprename'] = 'Rename group';
$labels['groupdelete'] = 'Delete group'; $labels['groupdelete'] = 'Delete group';
$labels['groupassign'] = 'Assign to group';
$labels['groupremove'] = 'Remove from group';
$labels['groupremoveselected'] = 'Remove selected contacts from group'; $labels['groupremoveselected'] = 'Remove selected contacts from group';
$labels['uponelevel'] = 'Up one level'; $labels['uponelevel'] = 'Up one level';
$labels['previouspage'] = 'Show previous set'; $labels['previouspage'] = 'Show previous set';
@ -608,6 +610,7 @@ $labels['arialabelmessageheaders'] = 'Message headers';
$labels['arialabelforwardingoptions'] = 'Forwarding options'; $labels['arialabelforwardingoptions'] = 'Forwarding options';
$labels['arialabelreplyalloptions'] = 'Reply-all options'; $labels['arialabelreplyalloptions'] = 'Reply-all options';
$labels['arialabelmoremessageactions'] = 'More message actions'; $labels['arialabelmoremessageactions'] = 'More message actions';
$labels['arialabelmorecontactactions'] = 'More contact actions';
$labels['arialabelmarkmessagesas'] = 'Mark selected messages as...'; $labels['arialabelmarkmessagesas'] = 'Mark selected messages as...';
$labels['arialabelcomposeoptions'] = 'Composition options'; $labels['arialabelcomposeoptions'] = 'Composition options';
$labels['arialabelresponsesmenu'] = 'Canned responses menu'; $labels['arialabelresponsesmenu'] = 'Canned responses menu';

@ -441,6 +441,8 @@ $labels['newgroup'] = 'Create new group';
$labels['addgroup'] = 'Add group'; $labels['addgroup'] = 'Add group';
$labels['grouprename'] = 'Rename group'; $labels['grouprename'] = 'Rename group';
$labels['groupdelete'] = 'Delete group'; $labels['groupdelete'] = 'Delete group';
$labels['groupassign'] = 'Assign to group';
$labels['groupremove'] = 'Remove from group';
$labels['groupremoveselected'] = 'Remove selected contacts from group'; $labels['groupremoveselected'] = 'Remove selected contacts from group';
$labels['uponelevel'] = 'Up one level'; $labels['uponelevel'] = 'Up one level';
@ -692,6 +694,7 @@ $labels['arialabelmessageheaders'] = 'Message headers';
$labels['arialabelforwardingoptions'] = 'Forwarding options'; $labels['arialabelforwardingoptions'] = 'Forwarding options';
$labels['arialabelreplyalloptions'] = 'Reply-all options'; $labels['arialabelreplyalloptions'] = 'Reply-all options';
$labels['arialabelmoremessageactions'] = 'More message actions'; $labels['arialabelmoremessageactions'] = 'More message actions';
$labels['arialabelmorecontactactions'] = 'More contact actions';
$labels['arialabelmarkmessagesas'] = 'Mark selected messages as...'; $labels['arialabelmarkmessagesas'] = 'Mark selected messages as...';
$labels['arialabelcomposeoptions'] = 'Composition options'; $labels['arialabelcomposeoptions'] = 'Composition options';
$labels['arialabelresponsesmenu'] = 'Canned responses menu'; $labels['arialabelresponsesmenu'] = 'Canned responses menu';

@ -92,6 +92,40 @@
background-position: -170px -32px; background-position: -170px -32px;
} }
#abooktoolbar a.contactmenu {
background: url(images/mail_toolbar.png) 0 0 no-repeat transparent;
background-position: -320px 0;
width: 34px;
}
#contactmenu li a
{
background: url(images/contactactions.png) no-repeat 7px 0;
background-position: 7px 20px;
}
#contactmenu li a.assigngroup
{
background-position: 7px 0;
}
#contactmenu li a.removegroup
{
background-position: 7px -19px;
}
#contactmenu li a.movelink
{
background: url(images/messageactions.png) no-repeat 7px 0;
background-position: 6px -160px;
}
#contactmenu li a.copylink
{
background: url(images/messageactions.png) no-repeat 7px 0;
background-position: 6px -142px;
}
#abookcountbar #abookcountbar
{ {
margin-top: 4px; margin-top: 4px;
@ -142,7 +176,9 @@
} }
#directorylist li a, #directorylist li a,
#savedsearchlist li a #savedsearchlist li a,
#addressbook-selector li a,
#contactgroup-selector li a
{ {
cursor: default; cursor: default;
display: block; display: block;
@ -200,6 +236,23 @@
background-position: 6px -162px; background-position: 6px -162px;
} }
#addressbook-selector li a.addressbook
{
padding-left: 25px;
}
#addressbook-selector li a.contactgroup
{
padding-left: 45px !important;
background-position: 25px -143px;
}
#contactgroup-selector li a
{
padding-left: 25px;
background-position: 5px -143px;
}
#contacts-table #contacts-table
{ {
width: 100%; width: 100%;

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

@ -31,6 +31,7 @@
</span> </span>
<roundcube:button command="advanced-search" type="link" class="buttonPas search" classAct="button search" classSel="button searchSel" title="advsearch" content=" " /> <roundcube:button command="advanced-search" type="link" class="buttonPas search" classAct="button search" classSel="button searchSel" title="advsearch" content=" " />
<roundcube:container name="toolbar" id="abooktoolbar" /> <roundcube:container name="toolbar" id="abooktoolbar" />
<roundcube:button name="contactmenulink" id="contactmenulink" type="link" class="button contactmenu" title="moreactions" onclick="rcmail_ui.show_popup('contactmenu');return false" content=" " />
</div> </div>
<div id="quicksearchbar"> <div id="quicksearchbar">
@ -56,6 +57,18 @@
</ul> </ul>
</div> </div>
<div id="contactmenu" class="popupmenu">
<ul class="toolbarmenu" id="contactmenumenu">
<roundcube:button type="link-menuitem" class="assigngroup" command="group-assign-selected" label="groupassign" classAct="assigngroup active" innerclass="folder-selector-link" />
<roundcube:button type="link-menuitem" class="removegroup" command="group-remove-selected" label="groupremove" classAct="removegroup active" />
<roundcube:if condition="env:writable_source &gt; 1" />
<roundcube:button type="link-menuitem" class="movelink" command="move" label="moveto" classAct="movelink active" innerclass="folder-selector-link" />
<roundcube:button type="link-menuitem" class="copylink" command="copy" label="copyto" classAct="copylink active" innerclass="folder-selector-link" />
<roundcube:endif />
<roundcube:container name="contactmenu" id="contactmenumenu" />
</ul>
</div>
<div id="mainscreen"> <div id="mainscreen">
<div id="directorylistbox"> <div id="directorylistbox">
@ -107,7 +120,6 @@
<ul> <ul>
<li><roundcube:button command="group-rename" type="link" label="grouprename" classAct="active" /></li> <li><roundcube:button command="group-rename" type="link" label="grouprename" classAct="active" /></li>
<li><roundcube:button command="group-delete" type="link" label="groupdelete" classAct="active" /></li> <li><roundcube:button command="group-delete" type="link" label="groupdelete" classAct="active" /></li>
<li><roundcube:button command="group-remove-selected" type="link" label="groupremoveselected" classAct="active" /></li>
<li class="separator_above"><roundcube:button command="search-create" type="link" label="searchsave" classAct="active" /></li> <li class="separator_above"><roundcube:button command="search-create" type="link" label="searchsave" classAct="active" /></li>
<li><roundcube:button command="search-delete" type="link" label="searchdelete" classAct="active" /></li> <li><roundcube:button command="search-delete" type="link" label="searchdelete" classAct="active" /></li>
<roundcube:container name="groupoptions" id="groupoptionsmenu" /> <roundcube:container name="groupoptions" id="groupoptionsmenu" />

@ -403,3 +403,37 @@ a.deletebutton {
#import-box .propform { #import-box .propform {
max-width: 50em; max-width: 50em;
} }
ul.toolbarmenu li span.assigngroup {
background-position: 0 -2358px;
}
ul.toolbarmenu li span.removegroup {
background-position: 0 -2384px;
}
#addressbook-selector li a,
#contactgroup-selector li a {
padding-left: 2px;
}
#addressbook-selector li a span,
#contactgroup-selector li a span {
background: url(images/listicons.png) 4px 20px no-repeat;
display: block;
height: 17px;
min-height: 14px;
padding: 4px 4px 1px 28px;
overflow: hidden;
max-width: 120px;
text-overflow: ellipsis;
}
#addressbook-selector li a.addressbook span {
background-position: 4px -2222px;
}
#addressbook-selector li a.contactgroup span,
#contactgroup-selector li a.contactgroup span {
background-position: 4px -2245px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 15 KiB

@ -26,6 +26,7 @@
<roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" title="printcontact" /> <roundcube:button command="print" type="link" class="button print disabled" classAct="button print" classSel="button print pressed" label="print" title="printcontact" />
<roundcube:button command="advanced-search" type="link" class="button search disabled" classAct="button search" classSel="button search pressed" label="advanced" title="advsearch" /> <roundcube:button command="advanced-search" type="link" class="button search disabled" classAct="button search" classSel="button search pressed" label="advanced" title="advsearch" />
<roundcube:container name="toolbar" id="addressbooktoolbar" /> <roundcube:container name="toolbar" id="addressbooktoolbar" />
<roundcube:button name="contactmenulink" id="contactmenulink" type="link" class="button more" label="more" title="moreactions" onclick="UI.toggle_popup('contactmenu',event);return false" aria-haspopup="true" aria-expanded="false" aria-owns="contactmenu-menu" />
<div id="exportmenu" class="popupmenu" aria-hidden="true"> <div id="exportmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-exportmenu" class="voice"><roundcube:label name="arialabelcontactexportoptions" /></h3> <h3 id="aria-label-exportmenu" class="voice"><roundcube:label name="arialabelcontactexportoptions" /></h3>
@ -35,6 +36,18 @@
</ul> </ul>
</div> </div>
<div id="contactmenu" class="popupmenu" aria-hidden="true">
<h3 id="aria-label-contactmenu" class="voice"><roundcube:label name="arialabelmorecontactactions" /></h3>
<ul id="contactmenu-menu" class="toolbarmenu iconized" role="menu" aria-labelledby="aria-label-contactmenu">
<roundcube:button type="link-menuitem" command="group-assign-selected" label="groupassign" class="icon" classAct="icon active" innerclass="icon assigngroup folder-selector-link" />
<roundcube:button type="link-menuitem" command="group-remove-selected" label="groupremove" class="icon" classAct="icon active" innerclass="icon removegroup" />
<roundcube:if condition="env:writable_source &gt; 1" />
<roundcube:button type="link-menuitem" command="move" label="moveto" class="icon" classAct="icon active" innerclass="icon move folder-selector-link" />
<roundcube:button type="link-menuitem" command="copy" label="copyto" class="icon" classAct="icon active" innerclass="icon copy folder-selector-link" />
<roundcube:endif />
<roundcube:container name="contactmenu" id="contactmenu-menu" />
</ul>
</div>
</div> </div>
<!-- search box --> <!-- search box -->
@ -103,7 +116,7 @@
<roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" label="last" /> <roundcube:button command="lastpage" type="link" class="icon lastpage disabled" classAct="icon lastpage" title="lastpage" label="last" />
</div> </div>
<div class="boxfooter"> <div class="boxfooter">
<roundcube:button command="add" type="link" title="newcontact" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="addcontact" /><roundcube:button command="delete" type="link" title="deletecontact" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="deletecontact" /><roundcube:button command="group-remove-selected" type="link" title="groupremoveselected" class="listbutton removegroup disabled" classAct="listbutton removegroup" innerClass="inner" label="groupremoveselected" /> <roundcube:button command="add" type="link" title="newcontact" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" label="addcontact" /><roundcube:button command="delete" type="link" title="deletecontact" class="listbutton delete disabled" classAct="listbutton delete" innerClass="inner" label="deletecontact" />
<span class="countdisplay" aria-live="polite" aria-relevant="text"> <span class="countdisplay" aria-live="polite" aria-relevant="text">
<span class="voice"><roundcube:label name="contacts" /></span> <span class="voice"><roundcube:label name="contacts" /></span>
<roundcube:object name="recordsCountDisplay" label="fromtoshort" /> <roundcube:object name="recordsCountDisplay" label="fromtoshort" />

Loading…
Cancel
Save