Merge branch 'dev-elastic'

pull/5838/head
Aleksander Machniak 7 years ago
commit 86a4d78369

@ -1,6 +1,17 @@
CHANGELOG Roundcube Webmail
===========================
- Various improvements for templating engine and skin behaviours
- Support conditional include
- Support for 'link' objects
- Support including files with path relative to templates directory
- Use <button> instead of <input> for submit button on logon screen
- Reset onerror on images if placeholder does not exist to prevent from requests storm
- Unified and simplified code for loading content frame for responses and identities
- Display contact import and advanced search in popup dialogs
- Make possible to set (some) config options from a skin
- Added optional checkbox selection for the list widget
- Make 'compose' command always enabled
- Add .log suffix to all log file names, add option log_file_ext to control this (#313)
- Archive: Fix archiving by sender address on cyrus-imap
- Archive: Add Thunderbird compatible Month option (#5623)

@ -350,7 +350,7 @@ rcube_webmail.prototype.acl_init_form = function(id)
id ? this.get_label('acl.editperms') : this.get_label('acl.newuser'),
buttons,
{
button_classes: ['mainaction'],
button_classes: ['mainaction submit'],
modal: true,
closeOnEscape: true,
close: function(e, ui) {

@ -43,6 +43,7 @@ class archive extends rcube_plugin
'height' => 32,
'title' => 'buttontitle',
'domain' => $this->ID,
'innerclass' => 'inner',
),
'toolbar');
@ -399,8 +400,8 @@ class archive extends rcube_plugin
}
$args['blocks']['main']['options']['archive_mbox'] = array(
'title' => $this->gettext('archivefolder'),
'content' => $select->show($mbox, array('name' => "_archive_mbox"))
'title' => html::label('_archive_mbox', rcube::Q($this->gettext('archivefolder'))),
'content' => $select->show($mbox, array('id' => '_archive_mbox', 'name' => '_archive_mbox'))
);
// add option for structuring the archive folder
@ -413,9 +414,9 @@ class archive extends rcube_plugin
$archive_type->add($this->gettext('archivetypefolder'), 'folder');
$args['blocks']['archive'] = array(
'name' => rcube::Q($this->gettext('settingstitle')),
'name' => rcube::Q($this->gettext('settingstitle')),
'options' => array('archive_type' => array(
'title' => $this->gettext('archivetype'),
'title' => html::label('ff_archive_type', rcube::Q($this->gettext('archivetype'))),
'content' => $archive_type->show($type)
)
)
@ -424,7 +425,7 @@ class archive extends rcube_plugin
else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) {
$chbox = new html_checkbox(array('name' => '_read_on_archive', 'id' => 'ff_read_on_archive', 'value' => 1));
$args['blocks']['main']['options']['read_on_archive'] = array(
'title' => $this->gettext('readonarchive'),
'title' => html::label('ff_read_on_archive', rcube::Q($this->gettext('readonarchive'))),
'content' => $chbox->show($rcmail->config->get('read_on_archive') ? 1 : 0)
);
}

@ -64,7 +64,12 @@ function rcmail_attachment_reminder_dialog()
};
rcmail.env.attachment_reminder = false;
rcmail.show_popup_dialog(rcmail.get_label('attachment_reminder.forgotattachment'), '', buttons);
rcmail.show_popup_dialog(
rcmail.get_label('attachment_reminder.forgotattachment'),
rcmail.get_label('attachment_reminder.missingattachment'),
buttons,
{button_classes: ['mainaction attach', 'send']}
);
};

@ -37,7 +37,7 @@ class attachment_reminder extends rcube_plugin
if ($rcmail->task == 'mail' && $rcmail->action == 'compose') {
if ($rcmail->config->get('attachment_reminder')) {
$this->include_script('attachment_reminder.js');
$this->add_texts('localization/', array('keywords', 'forgotattachment'));
$this->add_texts('localization/', array('keywords', 'forgotattachment', 'missingattachment'));
$rcmail->output->add_label('addattachment', 'send');
}
}

@ -17,6 +17,7 @@
*/
$messages = array();
$messages['missingattachment'] = "Missing attachment?";
$messages['forgotattachment'] = "Did you forget to attach a file?";
$messages['reminderoption'] = "Remind about forgotten attachments";
$messages['keywords'] = "attachment,file,attach,attached,attaching,enclosed,CV,cover letter";

@ -500,7 +500,7 @@ rcube_webmail.prototype.enigma_password_request = function(data)
this.show_popup_dialog(myprompt, this.get_label('enigma.enterkeypasstitle'),
[{
text: this.get_label('save'),
'class': 'mainaction',
'class': 'mainaction save',
click: function(e) {
e.stopPropagation();
@ -519,6 +519,7 @@ rcube_webmail.prototype.enigma_password_request = function(data)
},
{
text: this.get_label('cancel'),
'class': 'cancel',
click: function(e) {
var jq = ref.is_framed() ? window.parent.$ : $;
e.stopPropagation();

@ -48,6 +48,7 @@ class help extends rcube_plugin
'classsel' => 'button-help button-selected',
'innerclass' => 'button-inner',
'label' => 'help.help',
'type' => 'link',
), 'taskbar');
$this->include_script('help.js');

@ -47,21 +47,6 @@ class jqueryui extends rcube_plugin
$this->include_stylesheet("themes/larry/jquery-ui.css");
}
if ($ui_theme == 'larry') {
// patch dialog position function in order to fully fit the close button into the window
$rcmail->output->add_script("jQuery.extend(jQuery.ui.dialog.prototype.options.position, {
using: function(pos) {
var me = jQuery(this),
offset = me.css(pos).offset(),
topOffset = offset.top - 12;
if (topOffset < 0)
me.css('top', pos.top - topOffset);
if (offset.left + me.outerWidth() + 12 > jQuery(window).width())
me.css('left', pos.left - 12);
}
});", 'foot');
}
// jquery UI localization
$jquery_ui_i18n = $rcmail->config->get('jquery_ui_i18n', array('datepicker'));
if (count($jquery_ui_i18n) > 0) {

@ -279,8 +279,7 @@ class password extends rcube_plugin
$submit_button = $rcmail->output->button(array(
'command' => 'plugin.password-save',
'type' => 'input',
'class' => 'button mainaction',
'class' => 'button mainaction save',
'label' => 'save',
));
$form_buttons = html::p(array('class' => 'formbuttons'), $submit_button);

@ -43,6 +43,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
link.html('').append(span);
}
link.attr('aria-haspopup', 'true');
span.text(rcmail.get_label('zipdownload.download'));
rcmail.env.download_link = link;
});

@ -112,6 +112,7 @@ class zipdownload extends rcube_plugin
'command' => "download-$type",
'label' => "zipdownload.download$type",
'classact' => 'active',
'type' => 'link',
)));
}

@ -455,7 +455,7 @@ class rcmail extends rcube
// add some basic labels to client
$this->output->add_label('loading', 'servererror', 'connerror', 'requesttimedout',
'refreshing', 'windowopenerror', 'uploadingmany', 'close');
'refreshing', 'windowopenerror', 'uploadingmany', 'close', 'save', 'cancel');
return $this->output;
}

@ -27,12 +27,14 @@
*/
class rcmail_html_page extends rcmail_output_html
{
protected $inline_warning;
public function write($contents = '')
{
self::reset(true);
// load embed.css from skin folder (if exists)
if ($embed_css = $this->get_skin_file('/embed.css')) {
if ($embed_css = $this->get_skin_file($this->config->get('embed_css_location', '/embed.css'))) {
$this->include_css($embed_css);
}
else { // set default styles for warning blocks inside the attachment part frame
@ -43,6 +45,39 @@ class rcmail_html_page extends rcmail_output_html
));
}
if (empty($contents)) {
$contents = '<html><body></body></html>';
}
if ($this->inline_warning) {
$body_start = 0;
if ($body_pos = strpos($contents, '<body')) {
$body_start = strpos($contents, '>', $body_pos) + 1;
}
$contents = substr_replace($contents, $this->inline_warning, $body_start, 0);
}
parent::write($contents);
}
/**
* Add inline warning with optional button
*
* @param string $text Warning content
* @param string $button_label Button label
* @param string $button_url Button URL
*/
public function register_inline_warning($text, $button_label = null, $button_url = null)
{
$text = rcube::Q($text);
if ($button_label) {
$onclick = "location.href = '$button_url'";
$button = html::tag('button', array('onclick' => $onclick), rcube::Q($button_label));
$text .= html::p(array('class' => 'rcmail-inline-buttons'), $button);
}
$this->inline_warning = html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'), $text);
}
}

@ -297,6 +297,10 @@ EOF;
$this->load_skin('skins/' . $meta['extends']);
}
}
foreach ((array) $meta['config'] as $key => $value) {
$this->config->set($key, $value, true);
}
}
/**
@ -525,7 +529,9 @@ EOF;
}
// write all javascript commands
$this->add_script($commands, 'head_top');
if (!empty($commands)) {
$this->add_script($commands, 'head_top');
}
// allow (legal) iframe content to be loaded
$iframe = $this->framed || $this->env['framed'];
@ -1094,6 +1100,14 @@ EOF;
// include a file
case 'include':
if ($attrib['condition'] && !$this->check_condition($attrib['condition'])) {
break;
}
if ($attrib['file'][0] != '/') {
$attrib['file'] = '/templates/' . $attrib['file'];
}
$old_base_path = $this->base_path;
if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path'];
if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) {
@ -1213,6 +1227,17 @@ EOF;
return $hook['content'];
// return <link> element
case 'link':
if ($attrib['condition'] && !$this->check_condition($attrib['condition'])) {
break;
}
unset($attrib['condition']);
return html::tag('link', $attrib);
// return code for a specified eval expression
case 'exp':
return html::quote($this->eval_expression($attrib['expression']));
@ -1325,7 +1350,7 @@ EOF;
}
}
else {
$attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link';
$attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'button';
}
$command = $attrib['command'];
@ -1461,11 +1486,22 @@ EOF;
$attrib['value'] = $attrib['label'];
}
if ($attrib['command']) {
$attrib['disabled'] = 'disabled';
$attrib['disabled'] = 'disabled';
}
$out = html::tag('input', $attrib, null, array('type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', 'disabled'));
}
else {
if ($attrib['label']) {
$attrib['value'] = $attrib['label'];
}
if ($attrib['command']) {
$attrib['disabled'] = 'disabled';
}
$content = isset($attrib['content']) ? $attrib['content'] : $attrib['label'];
$out = html::tag('button', $attrib, $content, array('type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', 'disabled'));
}
// generate html code for button
if ($btn_content) {
@ -1512,7 +1548,7 @@ EOF;
* @param string $script JS code snippet
* @param string $position Target position [head|head_top|foot]
*/
public function add_script($script, $position='head')
public function add_script($script, $position = 'head')
{
if (!isset($this->scripts[$position])) {
$this->scripts[$position] = "\n" . rtrim($script);
@ -1915,9 +1951,8 @@ EOF;
}
if (rcube_utils::get_boolean($attrib['submit'])) {
$submit = new html_inputfield(array('type' => 'submit', 'id' => 'rcmloginsubmit',
'class' => 'button mainaction', 'value' => $this->app->gettext('login')));
$out .= html::p('formbuttons', $submit->show());
$button_attr = array('type' => 'submit', 'id' => 'rcmloginsubmit', 'class' => 'button mainaction submit');
$out .= html::p('formbuttons', html::tag('button', $button_attr, $this->app->gettext('login')));
}
// surround html output with a form tag
@ -1976,21 +2011,72 @@ EOF;
if ($attrib['type'] == 'search' && !$this->browser->khtml) {
unset($attrib['type'], $attrib['results']);
}
if (empty($attrib['placeholder'])) {
$attrib['placeholder'] = $this->app->gettext('searchplaceholder');
}
$label = html::label(array('for' => $attrib['id'], 'class' => 'voice'), rcube::Q($this->app->gettext('arialabelsearchterms')));
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
$out = $label . $input_q->show();
// @TODO: At some point we'll need support for multiple searchforms on the same page
$this->add_gui_object('qsearchbox', $attrib['id']);
// add form tag around text field
if (empty($attrib['form'])) {
if (empty($attrib['form']) && empty($attrib['no-form'])) {
$out = $this->form_tag(array(
'name' => "rcmqsearchform",
'onsubmit' => self::JS_OBJECT_NAME . ".command('search'); return false",
'style' => "display:inline"
// 'style' => "display:inline"
), $out);
}
if (!empty($attrib['wrapper'])) {
$header = html::tag($attrib['ariatag'] ?: 'h2', array(
'id' => 'aria-label-' . $attrib['label'],
'class' => 'voice'
), rcube::Q($this->app->gettext('arialabel' . $attrib['label'])));
if ($attrib['options']) {
$options_button = $this->button(array(
'type' => 'link',
'href' => '#search-filter',
'class' => 'button options',
'label' => 'options',
'title' => 'options',
'tabindex' => '0',
'innerclass' => 'inner',
'data-popup' => $attrib['options']
));
}
$search_button = $this->button(array(
'type' => 'link',
'href' => '#search',
'class' => 'button search',
'label' => $attrib['buttontitle'],
'title' => $attrib['buttontitle'],
'tabindex' => '0',
'innerclass' => 'inner',
));
$reset_button = $this->button(array(
'type' => 'link',
'command' => 'reset-search',
'class' => 'button reset',
'label' => 'resetsearch',
'title' => 'resetsearch',
'tabindex' => '0',
'innerclass' => 'inner',
));
$out = html::div(array(
'role' => 'search',
'aria-labelledby' => $attrib['label'] ? 'aria-label-' . $attrib['label'] : null,
'class' => $attrib['wrapper'],
), "$header$out\n$options_button\n$reset_button\n$search_button");
}
return $out;
}

@ -558,14 +558,7 @@ function rcube_webmail()
else if (this.gui_objects.responseslist) {
this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:true});
this.responses_list
.addEventListener('select', function(list) {
var win, id = list.get_single_selection();
ref.enable_command('delete', !!id && $.inArray(id, ref.env.readonly_responses) < 0);
if (id && (win = ref.get_frame_window(ref.env.contentframe))) {
ref.set_busy(true);
ref.location_href({ _action:'edit-response', _key:id, _framed:1 }, win);
}
})
.addEventListener('select', function(o) { ref.response_select(o); })
.init()
.focus();
}
@ -592,7 +585,7 @@ function rcube_webmail()
// display 'loading' message on form submit, lock submit button
$('form').submit(function () {
$('input[type=submit]', this).prop('disabled', true);
$('[type=submit]', this).prop('disabled', true);
ref.clear_messages();
ref.display_message('', 'loading');
});
@ -919,24 +912,15 @@ function rcube_webmail()
case 'add':
if (this.task == 'addressbook')
this.load_contact(0, 'add');
else if (this.task == 'settings' && this.env.action == 'responses') {
var frame;
if ((frame = this.get_frame_window(this.env.contentframe))) {
this.set_busy(true);
this.location_href({ _action:'add-response', _framed:1 }, frame);
}
}
else if (this.task == 'settings') {
this.identity_list.clear_selection();
else if (this.task == 'settings' && this.env.action == 'responses')
this.load_response(0, 'add-response');
else if (this.task == 'settings')
this.load_identity(0, 'add-identity');
}
break;
case 'edit':
if (this.task == 'addressbook' && (cid = this.get_single_cid()))
this.load_contact(cid, 'edit');
else if (this.task == 'settings' && props)
this.load_identity(props, 'edit-identity');
else if (this.task == 'mail' && (uid = this.get_single_uid())) {
url = { _mbox: this.get_message_mailbox(uid) };
url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = uid;
@ -947,11 +931,8 @@ function rcube_webmail()
case 'save':
var input, form = this.gui_objects.editform;
if (form) {
// adv. search
if (this.env.action == 'search') {
}
// user prefs
else if ((input = $("input[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) {
if ((input = $("input[name='_pagesize']", form)) && input.length && isNaN(parseInt(input.val()))) {
alert(this.get_label('nopagesizewarning'));
input.focus();
break;
@ -1148,12 +1129,14 @@ function rcube_webmail()
else if (this.contact_list)
a_cids = this.contact_list.get_selection();
if (a_cids.length)
if (a_cids.length) {
this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source }, true);
else if (this.env.group)
break;
}
else if (this.env.group) {
this.http_post('mailto', { _gid: this.env.group, _source: this.env.source }, true);
break;
break;
}
}
}
else if (props && typeof props == 'string') {
@ -1362,19 +1345,41 @@ function rcube_webmail()
break;
case 'import':
if (this.env.action == 'import' && this.gui_objects.importform) {
var file = document.getElementById('rcmimportfile');
if (file && !file.value) {
alert(this.get_label('selectimportfile'));
aborted = true;
break;
}
this.gui_objects.importform.submit();
this.set_busy(true, 'importwait');
this.lock_form(this.gui_objects.importform, true);
}
else
this.goto_url('import', (this.env.source ? '_target='+urlencode(this.env.source)+'&' : ''));
var reload = false,
dialog = $('<iframe>').attr('src', this.url('import', {_framed: 1, _target: this.env.source})),
import_func = function(e) {
var win = dialog[0].contentWindow,
form = win.rcmail.gui_objects.importform;
if (form) {
var lock, file = win.$('#rcmimportfile')[0];
if (file && !file.value) {
alert(win.rcmail.get_label('selectimportfile'));
return;
}
lock = win.rcmail.set_busy(true, 'importwait');
$('input[name="_unlock"]', form).val(lock);
form.submit();
win.rcmail.lock_form(form, true);
// disable Import button
$(e.target).attr('disabled', true);
reload = true;
}
},
close_func = function(event, ui) {
$(this).remove();
if (reload)
ref.command('list');
};
this.simple_dialog(dialog, this.gettext('importcontacts'), import_func, {
close: close_func,
button: 'import',
width: 500,
height: 300
});
break;
case 'export':
@ -2569,10 +2574,9 @@ function rcube_webmail()
// load message list to target frame/window
if (mbox) {
this.set_busy(true, 'loading');
url._mbox = mbox;
if (page)
url._page = page;
url._page = page;
this.set_busy(true, 'loading');
this.location_href(url, target);
}
};
@ -2593,9 +2597,10 @@ function rcube_webmail()
if (typeof url != 'object')
url = {};
url._layout = this.env.layout
url._mbox = mbox;
if (page)
url._page = page;
url._page = page;
// Disable double-click on the list when preview pane is on
// to make the delay when opening a message in preview pane minimal (#5199)
@ -3914,17 +3919,13 @@ function rcube_webmail()
});
// display dialog with missing keys
ref.show_popup_dialog(
ref.simple_dialog(
$('<div>')
.append($('<p>').html(ref.get_label('encryptpubkeysfound')))
.append(ul),
ref.get_label('importpubkeys'),
[{
text: ref.get_label('close'),
click: function() {
(ref.is_framed() ? parent.$ : $)(this).dialog('close');
}
}]
null,
{cancel_label: 'close', cancel_button: 'close'}
);
// delegate handler for import button clicks
@ -4049,21 +4050,12 @@ function rcube_webmail()
content = $('<div>').append(nodes);
$('input:not([disabled]):first', content).attr('checked', true);
this.show_popup_dialog(content, this.get_label('markallread'),
[{
'class': 'mainaction',
text: this.get_label('mark'),
click: function() {
ref.mark_all_read(folder, $('input:checked', this).val());
$(this).dialog('close');
}
this.simple_dialog(content, this.get_label('markallread'),
function() {
ref.mark_all_read(folder, $('input:checked', this).val());
return true;
},
{
text: this.get_label('cancel'),
click: function() {
$(this).dialog('close');
}
}]
{button: 'mark'}
);
return;
@ -4267,7 +4259,7 @@ function rcube_webmail()
this.get_label('restoremessage'),
[{
text: this.get_label('restore'),
'class': 'mainaction',
'class': 'mainaction restore',
click: function(){
ref.restore_compose_form(key, html_mode);
ref.remove_compose_data(key); // remove old copy
@ -4286,6 +4278,7 @@ function rcube_webmail()
},
{
text: this.get_label('ignore'),
'class': 'cancel',
click: function(){
$(this).dialog('close');
show_next(i);
@ -4315,7 +4308,7 @@ function rcube_webmail()
// the message has been sent but not saved, ask the user what to do
if (!saveonly && this.env.is_sent) {
return this.simple_dialog(this.get_label('messageissent'), '',
return this.simple_dialog(this.get_label('messageissent'), '', // TODO: dialog title
function() {
ref.submit_messageform(false, true);
return true;
@ -4474,7 +4467,8 @@ function rcube_webmail()
click: function() { save_func(true); }
}, {
text: this.get_label('cancel'),
click: function() { dialog.dialog('close'); }
click: function() { dialog.dialog('close'); },
'class': 'cancel'
}],
{dialogClass: 'warning'}
);
@ -4500,10 +4494,11 @@ function rcube_webmail()
this.get_label('nosubjecttitle'),
[{
text: this.get_label('sendmessage'),
click: function() { save_func(); },
'class': 'mainaction'
'class': 'mainaction send',
click: function() { save_func(); }
}, {
text: this.get_label('cancel'),
'class': 'cancel',
click: function() {
input_subject.focus();
dialog.dialog('close');
@ -4594,7 +4589,7 @@ function rcube_webmail()
$(this).dialog('close');
};
this.show_popup_dialog(html, this.get_label('newresponse'), buttons, {button_classes: ['mainaction']});
this.show_popup_dialog(html, this.get_label('newresponse'), buttons, {button_classes: ['mainaction save', 'cancel']});
$('#ffresponsetext').val(text);
$('#ffresponsename').select();
@ -5226,9 +5221,6 @@ function rcube_webmail()
if (!search && this.gui_objects.qsearchbox)
search = this.gui_objects.qsearchbox.value;
if (filter)
url._filter = filter;
if (this.gui_objects.search_interval)
url._interval = $(this.gui_objects.search_interval).val();
@ -5245,8 +5237,10 @@ function rcube_webmail()
}
}
if (scope)
url._scope = scope;
url._layout = this.env.layout;
url._filter = filter;
url._scope = scope;
if (mbox && scope != 'all')
url._mbox = mbox;
@ -5455,16 +5449,7 @@ function rcube_webmail()
if (id === null || !this.env.contacts[id] || !this.ksearch_input)
return;
// get cursor pos
var inp_value = this.ksearch_input.value,
cpos = this.get_caret_pos(this.ksearch_input),
p = inp_value.lastIndexOf(this.ksearch_value, cpos),
trigger = false,
insert = '',
delim = ', ',
// replace search string with full address
pre = inp_value.substring(0, p),
end = inp_value.substring(p+this.ksearch_value.length, inp_value.length);
var trigger = false, insert = '', delim = ', ';
this.ksearch_destroy();
@ -5483,10 +5468,7 @@ function rcube_webmail()
trigger = true;
}
this.ksearch_input.value = pre + insert + end;
// set caret to insert pos
this.set_caret_pos(this.ksearch_input, p + insert.length);
this.ksearch_input_replace(this.ksearch_value, insert);
if (trigger) {
this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert, data:this.env.contacts[id], search:this.ksearch_value_last, result_type:'person' });
@ -5498,7 +5480,7 @@ function rcube_webmail()
this.replace_group_recipients = function(id, recipients)
{
if (this.group2expand[id]) {
this.group2expand[id].input.value = this.group2expand[id].input.value.replace(this.group2expand[id].name, recipients);
this.ksearch_input_replace(this.group2expand[id].name, recipients, this.group2expand[id].input);
this.triggerEvent('autocomplete_insert', { field:this.group2expand[id].input, insert:recipients, data:this.group2expand[id], search:this.ksearch_value_last, result_type:'group' });
this.ksearch_value_last = null;
this.group2expand[id] = null;
@ -5509,17 +5491,11 @@ function rcube_webmail()
// address search processor
this.ksearch_get_results = function(props)
{
var inp_value = this.ksearch_input ? this.ksearch_input.value : null;
if (inp_value === null)
return;
if (this.ksearch_pane && this.ksearch_pane.is(":visible"))
this.ksearch_pane.hide();
// get string from cursor position back to the last comma or semicolon
var cpos = this.get_caret_pos(this.ksearch_input),
q = inp_value.substr(0, cpos).split(/[,;]/).pop(),
var q = this.ksearch_input_get(),
min = this.env.autocomplete_min_length,
data = this.ksearch_data;
@ -5585,9 +5561,14 @@ function rcube_webmail()
// create results pane if not present
if (!this.ksearch_pane) {
ul = $('<ul>');
this.ksearch_pane = $('<div>').attr('id', 'rcmKSearchpane').attr('role', 'listbox')
.css({ position:'absolute', 'z-index':30000 }).append(ul).appendTo(document.body);
this.ksearch_pane = $('<div>')
.attr({id: 'rcmKSearchpane', role: 'listbox'})
.css({position: 'absolute', 'z-index': 30000})
.append(ul)
.appendTo(document.body);
this.ksearch_pane.__ul = ul[0];
this.triggerEvent('autocomplete_create', {obj: this.ksearch_pane});
}
ul = this.ksearch_pane.__ul;
@ -5647,6 +5628,65 @@ function rcube_webmail()
this.ksearch_data.num--;
};
// Getter for input value (with support for non-input content-editable elements)
// returns a string from last comma to current cursor position
this.ksearch_input_get = function()
{
if (!this.ksearch_input)
return '';
var sel, range, sp, cp = 0, value = '';
if (this.ksearch_input.value === undefined) {
if ((sel = window.getSelection()) && (range = sel.getRangeAt(0))) {
value = $(range.endContainer).text();
cp = range.endOffset;
}
else {
value = $(this.ksearch_input).text();
}
}
else {
cp = this.get_caret_pos(this.ksearch_input);
value = this.ksearch_input.value;
}
return value.substr(0, cp).split(/[,;]/).pop();
};
// Setter for input value (with support for non-input content-editable elements)
// replaces 'from' string with 'to' and sets cursor position at the end
this.ksearch_input_replace = function(from, to, input)
{
if (!this.ksearch_input && !input)
return;
if (!input)
input = this.ksearch_input;
if (input.value === undefined) {
var node = $(input).contents().filter(function() { return this.nodeType == 3; }).last();
// here we assume there's only one text node
if (node.length) {
$(node)[0].textContent = to;
}
}
else {
var cpos = this.get_caret_pos(input),
p = input.value.lastIndexOf(from, cpos),
pre = input.value.substring(0, p),
end = input.value.substring(p + from.length, input.value.length);
input.value = pre + to + end;
// set caret to insert pos
this.set_caret_pos(input, p + to.length);
}
// run onchange action on the element
$(input).change();
};
this.ksearch_click = function(node)
{
if (this.ksearch_input)
@ -5720,7 +5760,7 @@ function rcube_webmail()
source = this.env.source ? this.env.address_sources[this.env.source] : null;
// we don't have dblclick handler here, so use 50 instead of this.dblclick_time
if (this.env.contentframe && (id = list.get_single_selection()))
if (this.env.contentframe && !list.multi_selecting && (id = list.get_single_selection()))
this.preview_timer = setTimeout(function(){ ref.load_contact(id, 'show'); }, 50);
else if (this.env.contentframe)
this.show_contentframe(false);
@ -5761,7 +5801,6 @@ function rcube_webmail()
// if a group is currently selected, and there is at least one contact selected
// thend we can enable the group-remove-selected command
this.enable_command('group-remove-selected', this.env.group && selected && writable);
this.enable_command('compose', this.env.group || selected);
this.enable_command('print', selected == 1);
this.enable_command('export-selected', 'copy', selected > 0);
this.enable_command('edit', id && writable);
@ -5884,7 +5923,6 @@ function rcube_webmail()
this.contact_list.clear(true);
this.show_contentframe(false);
this.enable_command('delete', 'move', 'copy', 'print', false);
this.enable_command('compose', this.env.group);
};
this.set_group_prop = function(prop)
@ -5923,7 +5961,6 @@ function rcube_webmail()
if (!cid)
this.contact_list.clear_selection();
this.enable_command('compose', rec && rec.email);
this.enable_command('export-selected', 'print', rec && rec._type != 'group');
}
else if (framed)
@ -6537,15 +6574,28 @@ function rcube_webmail()
// load advanced search page
this.advanced_search = function()
{
var win, url = {_form: 1, _action: 'search'}, target = window;
var dialog = $('<iframe>').attr('src', this.url('search', {_form: 1, _framed: 1})),
search_func = function() {
var valid = false, form = {_adv: 1};
if (win = this.get_frame_window(this.env.contentframe)) {
url._framed = 1;
target = win;
this.contact_list.clear_selection();
}
$.each($(dialog[0].contentWindow.rcmail.gui_objects.editform).serializeArray(), function() {
if (this.name.match(/^_search/) && this.value != '') {
form[this.name] = this.value;
valid = true;
}
});
if (valid) {
ref.http_post('search', form, ref.set_busy(true, 'searching'));
return true;
}
};
this.location_href(url, target, true);
this.simple_dialog(dialog, this.gettext('advsearch'), search_func, {
button: 'search',
width: 600,
height: 500
});
return true;
};
@ -6642,18 +6692,12 @@ function rcube_webmail()
this.qrcode = function()
{
var title = this.get_label('qrcode'),
buttons = [{
text: this.get_label('close'),
'class': 'mainaction',
click: function() {
(ref.is_framed() ? parent.$ : $)(this).dialog('destroy');
}
}],
options = {button: false, cancel_button: 'close', width: 310, height: 410},
img = new Image(300, 300);
img.src = this.url('addressbook/qrcode', {_source: this.env.source, _cid: this.env.cid});
return this.show_popup_dialog(img, title, buttons, {width: 310, height: 410});
return this.simple_dialog(img, title, null, options);
};
@ -6664,25 +6708,46 @@ function rcube_webmail()
// preferences section select and load options frame
this.section_select = function(list)
{
var win, id = list.get_single_selection(), target = window,
url = {_action: 'edit-prefs', _section: id};
var win, id = list.get_single_selection();
if (id && (win = this.get_frame_window(this.env.contentframe))) {
this.location_href({_action: 'edit-prefs', _section: id, _framed: 1}, win, true);
}
};
this.response_select = function(list)
{
var id = list.get_single_selection();
this.enable_command('delete', !!id && $.inArray(id, this.env.readonly_responses) < 0);
if (id) {
if (win = this.get_frame_window(this.env.contentframe)) {
url._framed = 1;
target = win;
}
this.location_href(url, target, true);
this.load_response(id, 'edit-response');
}
};
return true;
// load response record
this.load_response = function(id, action)
{
var win;
if (win = this.get_frame_window(this.env.contentframe)) {
if (id || action == 'add-response') {
if (!id)
this.responses_list.clear_selection();
this.location_href({_action: action, _key: id, _framed: 1}, win, true);
}
}
};
this.identity_select = function(list)
{
var id;
if (id = list.get_single_selection()) {
this.enable_command('delete', list.rowcount > 1 && this.env.identities_level < 2);
var id = list.get_single_selection();
this.enable_command('delete', !!id && list.rowcount > 1 && this.env.identities_level < 2);
if (id) {
this.load_identity(id, 'edit-identity');
}
};
@ -6690,22 +6755,16 @@ function rcube_webmail()
// load identity record
this.load_identity = function(id, action)
{
if (action == 'edit-identity' && (!id || id == this.env.iid))
return false;
var win, target = window,
url = {_action: action, _iid: id};
var win;
if (win = this.get_frame_window(this.env.contentframe)) {
url._framed = 1;
target = win;
}
if (id || action == 'add-identity') {
if (!id)
this.identity_list.clear_selection();
if (id || action == 'add-identity') {
this.location_href(url, target, true);
this.location_href({_action: action, _iid: id, _framed: 1}, win, true);
}
}
return true;
};
this.delete_identity = function(id)
@ -7310,7 +7369,7 @@ function rcube_webmail()
obj.className = button[state];
}
// disable/enable input buttons
if (button.type == 'input') {
if (button.type == 'input' || button.type == 'button') {
obj.disabled = state == 'pas';
}
else if (button.type == 'uibutton') {
@ -7441,7 +7500,7 @@ function rcube_webmail()
if (this.messages[key]) {
// replace label
if (this.messages[key].obj)
this.messages[key].obj.html(msg);
$('div.content', this.messages[key].obj).html(msg);
// store label in stack
if (type == 'loading') {
this.messages[key].labels.push({'id': id, 'msg': msg});
@ -7453,7 +7512,7 @@ function rcube_webmail()
}
// create DOM object and display it
var obj = $('<div>').addClass(type).html(msg).data('key', key),
var obj = $('<div>').addClass(type + ' content').html(msg).data('key', key),
cont = $(this.gui_objects.message).append(obj).show();
this.messages[key] = {'obj': obj, 'elements': [id]};
@ -7513,7 +7572,7 @@ function rcube_webmail()
}
else {
o = m[k].labels[i].msg;
m[k].obj.html(o);
$('div.content', m[k].obj).html(o);
}
}
}
@ -7527,7 +7586,7 @@ function rcube_webmail()
this.hide_message_object = function(o, fade)
{
if (fade)
o.fadeOut(600, function() {$(this).remove(); });
o.fadeOut(600, function() { $(this).remove(); });
else
o.hide().remove();
};
@ -7588,8 +7647,11 @@ function rcube_webmail()
var popup = $('<div class="popup">');
if (typeof content == 'object')
if (typeof content == 'object') {
popup.append(content);
if ($(content).is('iframe'))
popup.addClass('iframe');
}
else
popup.html(content);
@ -7615,31 +7677,34 @@ function rcube_webmail()
// assign special classes to dialog buttons
$.each(options.button_classes || [], function(i, v) {
if (v) $($('.ui-dialog-buttonpane button.ui-button', popup.parent()).get(i)).addClass(v);
if (v) $($('.ui-dialog-buttonpane button', popup.parent()).get(i)).addClass(v);
});
return popup;
};
// show_popup_dialog() wrapper for simple dialogs with Save and Cancel buttons
this.simple_dialog = function(content, title, button_func, options)
// show_popup_dialog() wrapper for simple dialogs with action and Cancel buttons
this.simple_dialog = function(content, title, action_func, options)
{
var title = this.get_label(title),
cancel_label = (options || {}).cancel_button || 'cancel',
save_label = (options || {}).button || 'save',
close_func = function(e, ui, dialog) { (ref.is_framed() ? parent.$ : $)(dialog || this).dialog('close'); },
buttons = [{
text: this.get_label((options || {}).button || 'save'),
'class': 'mainaction',
click: function() {
if (button_func())
$(this).dialog('close');
}
},
{
text: ref.get_label('cancel'),
click: function() {
$(this).dialog('close');
}
text: ref.get_label(cancel_label),
'class': 'cancel',
click: close_func
}];
if (!action_func)
buttons[0]['class'] += ' mainaction';
else
buttons.unshift({
text: this.get_label(save_label),
'class': 'mainaction ' + save_label,
click: function(e, ui) { if (action_func(e)) close_func(e, ui, this); }
});
return this.show_popup_dialog(content, title, buttons, options);
};
@ -7926,12 +7991,12 @@ function rcube_webmail()
row = $('<li>');
if (folder.virtual)
a.addClass('virtual').attr('aria-disabled', 'true').attr('tabindex', '-1');
a.addClass('virtual').attr({'aria-disabled': 'true', tabindex: '-1'});
else
a.addClass('active').data('id', folder.id);
if (folder['class'])
a.addClass(folder['class']);
row.addClass(folder['class']);
// calculate/set indentation level
while ((s = id.indexOf(delim, s)) >= 0) {
@ -7957,7 +8022,7 @@ function rcube_webmail()
container.css('max-height', $('li', container)[0].offsetHeight * 10 + 9);
// 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'));
return false;
});
@ -8409,7 +8474,6 @@ function rcube_webmail()
writable = !this.env.address_sources[this.env.source].readonly;
}
}
this.enable_command('compose', (uid && this.contact_list.rows[uid]));
this.enable_command('delete', 'edit', writable);
this.enable_command('export', (this.contact_list && this.contact_list.rowcount > 0));
this.enable_command('export-selected', 'print', false);

@ -60,6 +60,7 @@ function roundcube_browser()
this.webkit = this.agent_lc.indexOf('applewebkit') > 0;
this.ie = (document.all && !window.opera) || (this.win && this.agent_lc.indexOf('trident/') > 0);
this.edge = this.agent_lc.indexOf('edge/') > 0;
if (window.opera) {
this.opera = true; // Opera < 15
@ -108,7 +109,9 @@ function roundcube_browser()
var classname = ' js';
if (this.ie)
classname += ' ie ie'+parseInt(this.vendver);
classname += ' ms ie ie'+parseInt(this.vendver);
else if (this.edge)
classname += ' ms edge';
else if (this.opera)
classname += ' opera';
else if (this.konq)
@ -610,6 +613,18 @@ if (!String.prototype.startsWith) {
};
}
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(searchString, position) {
var subjectString = this.toString();
if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) {
position = subjectString.length;
}
position -= searchString.length;
var lastIndex = subjectString.lastIndexOf(searchString, position);
return lastIndex !== -1 && lastIndex === position;
};
}
// array utility function
jQuery.last = function(arr) {
return arr && arr.length ? arr[arr.length-1] : undefined;

@ -138,6 +138,8 @@ function rcube_text_editor(config, id)
});
};
rcmail.triggerEvent('editor-init', {config: conf, ref: ref});
// textarea identifier
this.id = id;
// reference to active editor (if in HTML mode)

@ -59,7 +59,7 @@ function GoogieSpell(img_dir, server_url, has_dict)
};
this.lang_to_word = this.org_lang_to_word;
this.langlist_codes = this.array_keys(this.lang_to_word);
this.show_change_lang_pic = true;
this.show_change_lang_pic = false; // roundcube mod.
this.change_lang_pic_placement = 'right';
this.report_state_change = true;
@ -74,6 +74,7 @@ function GoogieSpell(img_dir, server_url, has_dict)
this.lang_no_suggestions = "No suggestions";
this.lang_learn_word = "Add to dictionary";
this.use_ok_pic = false; // added by roundcube
this.show_spell_img = false; // roundcube mod.
this.decoration = true;
this.use_close_btn = false;
@ -505,7 +506,7 @@ this.showErrorWindow = function(elm, id)
item = document.createElement('td'),
dummy = document.createElement('span');
$(dummy).text(this.lang_learn_word);
$(dummy).text(this.lang_learn_word).addClass('googie_add_to_dict');
$(item).attr('googie_action_btn', '1').css('cursor', 'default')
.mouseover(ref.item_onmouseover)
.mouseout(ref.item_onmouseout)
@ -572,7 +573,7 @@ this.showErrorWindow = function(elm, id)
var edit_row = document.createElement('tr'),
edit = document.createElement('td'),
edit_input = document.createElement('input'),
ok_pic = document.createElement('img'),
ok_pic = document.createElement('button'), // roundcube mod.
edit_form = document.createElement('form');
var onsub = function () {
@ -592,10 +593,18 @@ this.showErrorWindow = function(elm, id)
.val($(elm).text()).attr('googie_action_btn', '1');
$(edit).css('cursor', 'default').attr('googie_action_btn', '1');
$(ok_pic).attr('src', this.img_dir + 'ok.gif')
.width(32).height(16)
.css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
.click(onsub);
// roundcube modified image use
if (this.use_ok_pic) {
$('<img>').attr('src', this.img_dir + 'ok.gif')
.width(32).height(16)
.css({cursor: 'pointer', 'margin-left': '2px', 'margin-right': '2px'})
.appendTo(ok_pic);
}
else {
$(ok_pic).text('OK');
}
$(ok_pic).addClass('mainaction save googie_ok_button').click(onsub);
$(edit_form).attr('googie_action_btn', '1')
.css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'})
@ -643,6 +652,9 @@ this.showErrorWindow = function(elm, id)
table.appendChild(list);
this.error_window.appendChild(table);
// roundcube plugin api hook
rcmail.triggerEvent('googiespell_create', {obj: this.error_window});
// calculate and set position
var height = $(this.error_window).height(),
width = $(this.error_window).width(),
@ -651,7 +663,10 @@ this.showErrorWindow = function(elm, id)
top = pos.top + height + 20 < pageheight ? pos.top + 20 : pos.top - height,
left = pos.left + width < pagewidth ? pos.left : pos.left - width;
$(this.error_window).css({'top': top+'px', 'left': left+'px'}).show();
if (left < 0) left = 0;
if (top < 0) top = 0;
$(this.error_window).css({'top': top+'px', 'left': left+'px', position: 'absolute'}).show();
// Dummy for IE - dropdown bug fix
if (document.all && !window.opera) {

@ -62,6 +62,7 @@ function rcube_list_widget(list, p)
this.toggleselect = false;
this.aria_listbox = false;
this.parent_focus = true;
this.checkbox_selection = false;
this.drag_active = false;
this.col_drag_active = false;
@ -189,7 +190,7 @@ init_row: function(row)
$(row)
.attr('role', 'option')
.attr('aria-labelledby', lbl_id)
.find(this.col_tagname()).eq(this.subject_col).attr('id', lbl_id);
.find(this.col_tagname()).eq(this.subject_column()).attr('id', lbl_id);
}
if (document.all)
@ -380,6 +381,27 @@ insert_row: function(row, before)
row = domrow;
}
if (this.checkbox_selection) {
var cell = document.createElement(this.col_tagname()),
chbox = document.createElement('input');
chbox.type = 'checkbox';
chbox.onchange = function(e) {
self.select_row(row.uid, CONTROL_KEY, true);
e.stopPropagation();
};
cell.className = 'selection';
cell.onclick = function(e) {
// this event handler fixes checkbox selection on touch devices
if (e.target.nodeName != 'INPUT')
chbox.click();
e.stopPropagation();
};
cell.appendChild(chbox);
row.insertBefore(cell, row.firstChild);
}
if (before && tbody.childNodes.length)
tbody.insertBefore(row, (typeof before == 'object' && before.parentNode == tbody) ? before : tbody.firstChild);
else
@ -437,7 +459,7 @@ focus: function(e)
var focus_elem = null;
if (this.last_selected && this.rows[this.last_selected]) {
focus_elem = $(this.rows[this.last_selected].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0');
focus_elem = $(this.rows[this.last_selected].obj).find(this.col_tagname()).eq(this.subject_column()).attr('tabindex', '0');
}
// Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
@ -472,7 +494,7 @@ blur: function(e)
if (this.last_selected && this.rows[this.last_selected]) {
$(this.rows[this.last_selected].obj)
.find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
.find(this.col_tagname()).eq(this.subject_column()).removeAttr('tabindex');
}
$(this.list).removeClass('focus');
@ -922,7 +944,7 @@ col_tagname: function()
get_cell: function(row, index)
{
return $(this.col_tagname(), row).eq(index);
return $(this.col_tagname(), row).eq(index + (this.checkbox_selection ? 1 : 0));
},
/**
@ -971,7 +993,7 @@ select_row: function(id, mod_key, with_mouse)
if (this.last_selected && this.rows[this.last_selected]) {
$(this.rows[this.last_selected].obj).removeClass('focused')
.find(this.col_tagname()).eq(this.subject_col).removeAttr('tabindex');
.find(this.col_tagname()).eq(this.subject_column()).removeAttr('tabindex');
}
// unselect if toggleselect is active and the same row was clicked again
@ -987,7 +1009,7 @@ select_row: function(id, mod_key, with_mouse)
$(this.rows[id].obj).addClass('focused');
// set cursor focus to link inside selected row
if (this.focused)
this.focus_noscroll($(this.rows[id].obj).find(this.col_tagname()).eq(this.subject_col).attr('tabindex', '0'));
this.focus_noscroll($(this.rows[id].obj).find(this.col_tagname()).eq(this.subject_column()).attr('tabindex', '0'));
}
if (!this.selection.length)
@ -1199,6 +1221,9 @@ clear_selection: function(id, no_event)
this.selection = [];
}
if (this.checkbox_selection)
$(this.row_tagname() + ':not(.selected) > .selection > input:checked', this.list).prop('checked', false);
if (num_select && !this.selection.length && !no_event) {
this.triggerEvent('select');
this.last_selected = null;
@ -1257,6 +1282,9 @@ highlight_row: function(id, multiple, norecur)
this.clear_selection(null, true);
this.selection[0] = id;
$(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true');
if (this.checkbox_selection)
$('.selection > input', this.rows[id].obj).prop('checked', true);
}
}
else {
@ -1265,6 +1293,10 @@ highlight_row: function(id, multiple, norecur)
if (p === false) { // select row
this.selection.push(id);
$(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true');
if (this.checkbox_selection)
$('.selection > input', this.rows[id].obj).prop('checked', true);
if (!norecur && !this.rows[id].expanded)
this.highlight_children(id, true);
}
@ -1274,6 +1306,10 @@ highlight_row: function(id, multiple, norecur)
this.selection = pre.concat(post);
$(this.rows[id].obj).removeClass('selected').removeAttr('aria-selected');
if (this.checkbox_selection)
$('.selection > input', this.rows[id].obj).prop('checked', false);
if (!norecur && !this.rows[id].expanded)
this.highlight_children(id, false);
}
@ -1529,8 +1565,10 @@ drag_mouse_move: function(e)
return false;
}
var subject_col = self.subject_column();
$('> ' + self.col_tagname(), self.rows[uid].obj).each(function(n, cell) {
if (self.subject_col < 0 || (self.subject_col >= 0 && self.subject_col == n)) {
if (subject_col < 0 || (subject_col >= 0 && subject_col == n)) {
// remove elements marked with "skip-on-drag" class
cell = $(cell).clone();
$(cell).find('.skip-on-drag').remove();
@ -1824,6 +1862,11 @@ column_replace: function(from, to)
this.init_header();
this.triggerEvent('column_replace');
},
subject_column: function()
{
return this.subject_col + (this.checkbox_selection ? 1 : 0);
}
};

@ -32,7 +32,7 @@ class html
public static $doctype = 'xhtml';
public static $lc_tags = true;
public static $common_attrib = array('id','class','style','title','align','unselectable','tabindex','role');
public static $containers = array('iframe','div','span','p','h1','h2','h3','ul','form','textarea','table','thead','tbody','tr','th','td','style','script');
public static $containers = array('iframe','div','span','p','h1','h2','h3','ul','form','textarea','table','thead','tbody','tr','th','td','style','script','a');
public static $bool_attrib = array('checked','multiple','disabled','selected','autofocus','readonly');

@ -31,6 +31,7 @@ class rcube_config
private $prop = array();
private $errors = array();
private $userprefs = array();
private $immutable = array();
/**
@ -410,12 +411,17 @@ class rcube_config
/**
* Setter for a config parameter
*
* @param string $name Parameter name
* @param mixed $value Parameter value
* @param string $name Parameter name
* @param mixed $value Parameter value
* @param bool $immutable Make the value immutable
*/
public function set($name, $value)
public function set($name, $value, $immutable = false)
{
$this->prop[$name] = $value;
if ($immutable) {
$this->immutable[$name] = $value;
}
}
/**
@ -426,7 +432,7 @@ class rcube_config
public function merge($prefs)
{
$prefs = $this->fix_legacy_props($prefs);
$this->prop = array_merge($this->prop, $prefs, $this->userprefs);
$this->prop = array_merge($this->prop, $prefs, $this->userprefs, $this->immutable);
}
/**

@ -26,6 +26,7 @@ $labels['server'] = 'Server';
$labels['login'] = 'Login';
// taskbar
$labels['menu'] = 'Menu';
$labels['logout'] = 'Logout';
$labels['mail'] = 'Mail';
$labels['settings'] = 'Settings';
@ -54,6 +55,7 @@ $labels['priority'] = 'Priority';
$labels['organization'] = 'Organization';
$labels['readstatus'] = 'Read status';
$labels['listoptions'] = 'List options...';
$labels['listoptionstitle'] = 'List options';
$labels['mailboxlist'] = 'Folders';
$labels['messagesfromto'] = 'Messages $from to $to of $count';
@ -226,6 +228,7 @@ $labels['quotastorage'] = 'Disk space';
$labels['quotamessage'] = 'Messages count';
$labels['quicksearch'] = 'Quick search';
$labels['searchplaceholder'] = 'Search...';
$labels['resetsearch'] = 'Reset search';
$labels['searchmod'] = 'Search modifiers';
$labels['msgtext'] = 'Entire message';
@ -586,6 +589,8 @@ $labels['personalfolder'] = 'Private Folder';
$labels['otherfolder'] = 'Other User\'s Folder';
$labels['sharedfolder'] = 'Public Folder';
$labels['findfolders'] = 'Find folders';
$labels['findcontacts'] = 'Find contacts';
$labels['findmail'] = 'Find mail messages';
$labels['namespace.personal'] = 'Personal';
$labels['namespace.other'] = 'Other users';
$labels['namespace.shared'] = 'Shared';
@ -601,6 +606,7 @@ $labels['version'] = 'Version';
$labels['source'] = 'Source';
$labels['license'] = 'License';
$labels['support'] = 'Get support';
$labels['savedsearches'] = 'Saved searches';
// units
$labels['B'] = 'B';
@ -661,6 +667,7 @@ $labels['arialabelmarkmessagesas'] = 'Mark selected messages as...';
$labels['arialabelcomposeoptions'] = 'Composition options';
$labels['arialabelresponsesmenu'] = 'Canned responses menu';
$labels['arialabelattachmentuploadform'] = 'Attachment upload form';
$labels['arialabelattachmentmenu'] = 'Attachment options';
$labels['arialabelattachmentpreview'] = 'Attachment preview';
$labels['ariasummarycomposecontacts'] = 'List of contacts and groups to select as recipients';
$labels['arialabelcontactexportoptions'] = 'Contact export options';
@ -669,6 +676,7 @@ $labels['arialabelpreferencesform'] = 'Preferences form';
$labels['arialabelidentityeditfrom'] = 'Identity edit form';
$labels['arialabelresonseeditfrom'] = 'Response edit form';
$labels['arialabelsearchterms'] = 'Search terms';
$labels['arialabeldropactionmenu'] = 'Drag-n-Drop action menu';
$labels['helplistnavigation'] = 'List keyboard navigation';
$labels['helplistkeyboardnavigation'] = "Arrows up/down: Move row focus/selection.

@ -260,7 +260,8 @@ function rcmail_directory_list($attrib)
// add some labels to client
$OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember',
'newgroup', 'grouprename', 'searchsave', 'namex', 'save'
'newgroup', 'grouprename', 'searchsave', 'namex', 'save', 'import', 'importcontacts',
'advsearch', 'search'
);
return html::tag('ul', $attrib, $out, html::$common_attrib);
@ -859,7 +860,7 @@ function rcmail_contact_photo($attrib)
$content = html::div($attrib, html::img(array(
'src' => $photo_img,
'alt' => $RCMAIL->gettext('contactphoto'),
'onerror' => 'this.src = rcmail.env.photo_placeholder',
'onerror' => 'this.src = rcmail.env.photo_placeholder; this.onerror = null',
)));
if ($CONTACT_COLTYPES['photo'] && ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add')) {

@ -180,12 +180,15 @@ $OUTPUT->set_pagetitle($RCMAIL->gettext('importcontacts'));
$OUTPUT->add_handlers(array(
'importstep' => $importstep,
'importnav' => 'rcmail_import_buttons',
));
// render page
$OUTPUT->send('importcontacts');
if ($OUTPUT->template_exists('contactimport')) {
$OUTPUT->send('contactimport');
}
else {
$OUTPUT->send('importcontacts'); // deprecated
}
/**
@ -227,6 +230,8 @@ function rcmail_import_form($attrib)
$form .= $abook->show();
}
$form .= html::tag('input', array('type' => 'hidden', 'name' => '_unlock', 'value' => ''));
// selector for group import options
if (count($writable_books) >= 1 || $writable_books[0]->groups) {
$select = new html_select(array('name' => '_groups', 'id' => 'rcmimportgroups', 'is_escaped' => true));
@ -291,33 +296,6 @@ function rcmail_import_confirm($attrib)
return html::div($attrib, $content);
}
/**
* Create navigation buttons for the current import step
*/
function rcmail_import_buttons($attrib)
{
global $IMPORT_STATS, $OUTPUT;
$target = rcube_utils::get_input_value('_target', rcube_utils::INPUT_GPC);
$attrib += array('type' => 'input');
unset($attrib['name']);
if (is_object($IMPORT_STATS)) {
$attrib['class'] = trim($attrib['class'] . ' mainaction');
$out = $OUTPUT->button(array('command' => 'list', 'prop' => $target, 'label' => 'done') + $attrib);
}
else {
$cancel = $OUTPUT->button(array('command' => 'list', 'label' => 'cancel') + $attrib);
$attrib['class'] = trim($attrib['class'] . ' mainaction');
$out = $OUTPUT->button(array('command' => 'import', 'label' => 'import') + $attrib);
$out .= '&nbsp;';
$out .= $cancel;
}
return $out;
}
/**
* Returns the matching group id. If group doesn't exist, it'll be created if allowed.
*/

@ -240,16 +240,6 @@ function rcmail_contact_search()
// Re-set list header
$OUTPUT->command('set_group_prop', null);
if ($adv) {
$OUTPUT->command('parent.set_env', array(
'search_request' => $search_request,
'pagecount' => ceil($result->count / $PAGE_SIZE),
'search_id' => $sid,
'source' => '',
'group' => '',
));
}
if (!$sid) {
// unselect currently selected directory/group
$OUTPUT->command('unselect_directory');
@ -259,7 +249,7 @@ function rcmail_contact_search()
$OUTPUT->command('update_group_commands');
// send response
$OUTPUT->send($adv ? 'iframe' : null);
$OUTPUT->send();
}
function rcmail_contact_search_form($attrib)

@ -291,6 +291,7 @@ $OUTPUT->include_script('publickey.js');
// register UI objects
$OUTPUT->add_handlers(array(
'composeformhead' => 'rcmail_compose_form_head',
'composeheaders' => 'rcmail_compose_headers',
'composesubject' => 'rcmail_compose_subject',
'composebody' => 'rcmail_compose_body',
@ -411,6 +412,13 @@ function rcmail_process_compose_params(&$COMPOSE)
}
}
function rcmail_compose_form_head($attrib)
{
list($form_start,) = get_form_tags($attrib);
return $form_start;
}
function rcmail_compose_headers($attrib)
{
global $RCMAIL, $MESSAGE;
@ -457,7 +465,7 @@ function rcmail_compose_headers($attrib)
// pass the following attributes to the form class
$field_attrib = array('name' => $fname, 'spellcheck' => 'false');
foreach ($attrib as $attr => $value) {
if (in_array($attr, $allow_attrib)) {
if (stripos($attr, 'data-') === 0 || in_array($attr, $allow_attrib)) {
$field_attrib[$attr] = $value;
}
}
@ -1805,6 +1813,10 @@ function get_form_tags($attrib)
{
global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
if (rcube_utils::get_boolean((string) $attrib['noform'])) {
return array('', '');
}
$form_start = '';
if (!$MESSAGE_FORM) {
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task));

@ -594,7 +594,7 @@ function rcmail_message_list_head($attrib, $a_show_cols)
$a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc');
if (!empty($attrib['optionsmenuicon'])) {
$list_menu = rcmail_options_menu_link($attrib);
$list_menu = rcmail_options_menu_link();
}
$cells = $coltypes = array();
@ -659,7 +659,7 @@ function rcmail_message_list_head($attrib, $a_show_cols)
return $cells;
}
function rcmail_options_menu_link($attrib)
function rcmail_options_menu_link($attrib = array())
{
global $RCMAIL;
@ -669,11 +669,14 @@ function rcmail_options_menu_link($attrib)
if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') {
$inner = html::img(array('src' => $RCMAIL->output->abs_url($attrib['optionsmenuicon'], true), 'alt' => $title));
}
else if ($attrib['innerclass']) {
$inner = html::span($attrib['innerclass'], $inner);
}
return html::a(array(
'href' => '#list-options',
'onclick' => $onclick,
'class' => 'listmenu',
'class' => isset($attrib['class']) ? $attrib['class'] : 'listmenu',
'id' => 'listmenulink',
'title' => $title,
'tabindex' => '0',
@ -1701,18 +1704,21 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null,
}
if ($addicon && $_SESSION['writeable_abook']) {
$label = $RCMAIL->gettext('addtoaddressbook');
$icon = html::img(array(
'src' => $RCMAIL->output->abs_url($addicon, true),
'alt' => $label,
'class' => 'noselect',
));
$address .= html::a(array(
'href' => "#add",
'title' => $RCMAIL->gettext('addtoaddressbook'),
'title' => $label,
'class' => 'rcmaddcontact',
'onclick' => sprintf("return %s.command('add-contact','%s',this)",
rcmail_output::JS_OBJECT_NAME, rcube::JQ($string)),
),
html::img(array(
'src' => $RCMAIL->output->abs_url($addicon, true),
'alt' => "Add contact",
'class' => 'noselect',
)));
$addicon == 'virtual' ? '' : $icon
);
}
}
else {

@ -183,24 +183,23 @@ if (empty($_GET['_thumb']) && $attachment->is_valid()) {
header("Content-Length: " . strlen($content));
echo $content;
}
else { // html warning with a button to load the file anyway
// html warning with a button to load the file anyway
else {
$OUTPUT = new rcmail_html_page();
$OUTPUT->write(html::tag('html', null, html::tag('body', 'embed',
html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'),
$OUTPUT->register_inline_warning(
$RCMAIL->gettext(array(
'name' => 'attachmentvalidationerror',
'vars' => array(
'expected' => $mimetype . ($file_extension ? " (.$file_extension)" : ''),
'detected' => $real_mimetype . ($extensions[0] ? " (.$extensions[0])" : ''),
'name' => 'attachmentvalidationerror',
'vars' => array(
'expected' => $mimetype . ($file_extension ? " (.$file_extension)" : ''),
'detected' => $real_mimetype . ($extensions[0] ? " (.$extensions[0])" : ''),
)
)
))
. html::p(array('class' => 'rcmail-inline-buttons'),
html::tag('button', array(
'onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_nocheck' => 1))) . "'"
),
$RCMAIL->gettext('showanyway'))
)
))));
),
$RCMAIL->gettext('showanyway'),
$RCMAIL->url(array_merge($_GET, array('_nocheck' => 1)))
);
$OUTPUT->write();
}
exit;
@ -222,15 +221,16 @@ if (empty($_GET['_thumb']) && $attachment->is_valid()) {
// deliver part content
if ($mimetype == 'text/html' && empty($_GET['_download'])) {
header("Content-Type: text/html; charset=" . $attachment->charset);
$OUTPUT = new rcmail_html_page();
// Check if we have enough memory to handle the message in it
// #1487424: we need up to 10x more memory than the body
if (!rcube_utils::mem_check($attachment->size * 10)) {
$out = '<html><body>'
. $RCMAIL->gettext('messagetoobig'). ' '
. html::a($RCMAIL->url(array_merge($_GET, array('download' => 1))), $RCMAIL->gettext('download'))
. '</body></html>';
$OUTPUT->register_inline_warning(
$RCMAIL->gettext('messagetoobig'),
$RCMAIL->gettext('download'),
$RCMAIL->url(array_merge($_GET, array('_download' => 1)))
);
}
else {
// render HTML body
@ -238,23 +238,14 @@ if (empty($_GET['_thumb']) && $attachment->is_valid()) {
// insert remote objects warning into HTML body
if ($REMOTE_OBJECTS) {
$body_start = 0;
if ($body_pos = strpos($out, '<body')) {
$body_start = strpos($out, '>', $body_pos) + 1;
}
$out = substr($out, 0, $body_start)
. html::div(array('class' => 'rcmail-inline-message rcmail-inline-warning'),
rcube::Q($RCMAIL->gettext('blockedimages')) . '&nbsp;' .
html::tag('button',
array('onclick' => "location.href='" . $RCMAIL->url(array_merge($_GET, array('_safe' => 1))) . "'"),
rcube::Q($RCMAIL->gettext('showimages')))
)
. substr($out, $body_start);
$OUTPUT->register_inline_warning(
$RCMAIL->gettext('blockedimages'),
$RCMAIL->gettext('showimages'),
$RCMAIL->url(array_merge($_GET, array('_safe' => 1)))
);
}
}
$OUTPUT = new rcmail_html_page();
$OUTPUT->write($out);
exit;
}

@ -336,7 +336,7 @@ function rcmail_message_contactphoto($attrib)
'_email' => $MESSAGE->sender['mailto'],
));
$attrib['onerror'] = "this.src = '$placeholder'";
$attrib['onerror'] = "this.src = '$placeholder'; this.onerror = null";
}
else {
$photo_img = $placeholder;

@ -191,6 +191,7 @@ $OUTPUT->add_handlers(array(
'folderframe' => 'rcmail_folder_frame',
'folderfilter' => 'rcmail_folder_filter',
'quotadisplay' => array($RCMAIL, 'quota_display'),
'searchform' => array($OUTPUT, 'search_form'),
));
$OUTPUT->send('folders');

@ -353,7 +353,7 @@ function rcmail_user_prefs($current = null)
'alt' => $skin,
'width' => 64,
'height' => 64,
'onerror' => "this.src = rcmail.assets_path('program/resources/blank.gif')",
'onerror' => "this.src = rcmail.assets_path('program/resources/blank.gif'); this.onerror = null",
));
$skinnames[] = mb_strtolower($skinname);
@ -1111,9 +1111,10 @@ function rcmail_user_prefs($current = null)
continue 2;
}
$attrs = array('id' => '_drafts_mbox', 'name' => '_drafts_mbox', 'onchange' => $onchange);
$blocks['main']['options']['drafts_mbox'] = array(
'title' => rcube::Q($RCMAIL->gettext('drafts')),
'content' => $select->show($config['drafts_mbox'], array('name' => "_drafts_mbox", 'onchange' => $onchange)),
'title' => html::label($attrs['id'], rcube::Q($RCMAIL->gettext('drafts'))),
'content' => $select->show($config['drafts_mbox'], $attrs),
);
}
@ -1122,9 +1123,10 @@ function rcmail_user_prefs($current = null)
continue 2;
}
$attrs = array('id' => '_sent_mbox', 'name' => '_sent_mbox', 'onchange' => '');
$blocks['main']['options']['sent_mbox'] = array(
'title' => rcube::Q($RCMAIL->gettext('sent')),
'content' => $select->show($config['sent_mbox'], array('name' => "_sent_mbox", 'onchange' => '')),
'title' => html::label($attrs['id'], rcube::Q($RCMAIL->gettext('sent'))),
'content' => $select->show($config['sent_mbox'], $attrs),
);
}
@ -1133,9 +1135,10 @@ function rcmail_user_prefs($current = null)
continue 2;
}
$attrs = array('id' => '_junk_mbox', 'name' => '_junk_mbox', 'onchange' => $onchange);
$blocks['main']['options']['junk_mbox'] = array(
'title' => rcube::Q($RCMAIL->gettext('junk')),
'content' => $select->show($config['junk_mbox'], array('name' => "_junk_mbox", 'onchange' => $onchange)),
'title' => html::label($attrs['id'], rcube::Q($RCMAIL->gettext('junk'))),
'content' => $select->show($config['junk_mbox'], $attrs),
);
}
@ -1144,9 +1147,10 @@ function rcmail_user_prefs($current = null)
continue 2;
}
$attrs = array('id' => '_trash_mbox', 'name' => '_trash_mbox', 'onchange' => $onchange);
$blocks['main']['options']['trash_mbox'] = array(
'title' => rcube::Q($RCMAIL->gettext('trash')),
'content' => $select->show($config['trash_mbox'], array('name' => "_trash_mbox", 'onchange' => $onchange)),
'title' => html::label($attrs['id'], rcube::Q($RCMAIL->gettext('trash'))),
'content' => $select->show($config['trash_mbox'], $attrs),
);
}
break;
@ -1414,7 +1418,7 @@ function rcmail_settings_tabs($attrib)
$action['href'] = $RCMAIL->url(array('_action' => $action['action']));
}
$button = $OUTPUT->button($action);
$button = $OUTPUT->button($action + array('type' => 'link'));
$attr = $attrib;
$cmd = $action['action'] ?: $action['command'];

@ -671,6 +671,18 @@ img.uploading
margin-left: 10px;
}
.ui-dialog iframe {
width: 100%;
height: 100%;
border: 0;
}
.ui-dialog-content.iframe {
padding: 0 !important;
overflow: hidden !important;
}
/***** common table settings ******/
table.records-table thead tr th,
@ -1355,7 +1367,7 @@ ul.toolbarmenu li.separator_above
padding: 0;
}
#folder-selector li a span
#folder-selector li span
{
background: url(images/icons/folders.png) no-repeat 6px 0;
display: block;
@ -1377,23 +1389,23 @@ ul.toolbarmenu li.separator_above
color: white;
}
#folder-selector li a.inbox span
#folder-selector li.inbox span
{
background-position: 6px -18px;
}
#folder-selector li a.drafts span
#folder-selector li.drafts span
{
background-position: 6px -37px;
}
#folder-selector li a.sent span
#folder-selector li.sent span
{
background-position: 6px -54px;
}
#folder-selector li a.trash span
#folder-selector li.trash span
{
background-position: 6px -91px;
}
#folder-selector li a.junk span
#folder-selector li.junk span
{
background-position: 6px -73px;
}

@ -116,3 +116,13 @@
border-right: 1px solid #555;
border-bottom: 1px solid #555;
}
.googie_ok_button {
background: url(images/googiespell/ok.gif) no-repeat center center transparent;
width: 32px;
height: 16px;
cursor: pointer;
padding: 0 2px;
text-indent: -5000px;
border: 0;
}

@ -1,7 +1,7 @@
<div id="taskbar">
<roundcube:button command="mail" label="mail" class="button-mail" />
<roundcube:button command="addressbook" label="contacts" class="button-addressbook" />
<roundcube:button command="mail" type="link" label="mail" class="button-mail" />
<roundcube:button command="addressbook" type="link" label="contacts" class="button-addressbook" />
<roundcube:container name="taskbar" id="taskbar" />
<roundcube:button command="settings" label="settings" class="button-settings" />
<roundcube:button command="logout" label="logout" class="button-logout" />
<roundcube:button command="settings" type="link" label="settings" class="button-settings" />
<roundcube:button command="logout" type="link" label="logout" class="button-logout" />
</div>

@ -41,8 +41,8 @@
<div id="exportmenu" class="popupmenu">
<ul>
<li><roundcube:button command="export" label="exportall" prop="sub" classAct="exportalllink active" class="exportalllink" /></li>
<li><roundcube:button command="export-selected" label="exportsel" prop="sub" classAct="exportsellink active" class="exportsellink" /></li>
<li><roundcube:button command="export" type="link" label="exportall" prop="sub" classAct="exportalllink active" class="exportalllink" /></li>
<li><roundcube:button command="export-selected" type="link" label="exportsel" prop="sub" classAct="exportsellink active" class="exportsellink" /></li>
</ul>
</div>
@ -105,19 +105,19 @@
<div id="groupoptionsmenu" class="popupmenu">
<ul>
<li><roundcube:button command="group-rename" label="grouprename" classAct="active" /></li>
<li><roundcube:button command="group-delete" label="groupdelete" classAct="active" /></li>
<li><roundcube:button command="group-remove-selected" label="groupremoveselected" classAct="active" /></li>
<li class="separator_above"><roundcube:button command="search-create" label="searchsave" classAct="active" /></li>
<li><roundcube:button command="search-delete" label="searchdelete" 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-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><roundcube:button command="search-delete" type="link" label="searchdelete" classAct="active" /></li>
<roundcube:container name="groupoptions" id="groupoptionsmenu" />
</ul>
</div>
<div id="dragmenu" class="popupmenu">
<ul>
<li><roundcube:button command="move" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
<li><roundcube:button command="copy" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
<li><roundcube:button command="move" type="link" onclick="return rcmail.drag_menu_action('move')" label="move" classAct="active" /></li>
<li><roundcube:button command="copy" type="link" onclick="return rcmail.drag_menu_action('copy')" label="copy" classAct="active" /></li>
</ul>
</div>

@ -7,10 +7,8 @@
</head>
<body class="iframe">
<div id="contact-title" class="boxtitle"><roundcube:label name="advsearch" /></div>
<div id="contact-details" class="boxcontent">
<div class="boxcontent">
<roundcube:object name="searchform" id="advsearchform" size=30 />
<p><roundcube:button command="save" type="input" class="button mainaction" label="search" /></p>
</div>
<script type="text/javascript">rcube_init_tabs('advsearchform')</script>

@ -28,7 +28,7 @@
<div class="searchbox" role="search">
<input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
<a class="iconbutton searchicon"></a>
<roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" width="13" height="13" />
<roundcube:button command="reset-foldersearch" type="link" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" width="13" height="13" />
</div>
</div>
<div id="folderlist-content" class="boxlistcontent">

@ -4,22 +4,10 @@
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<body>
<roundcube:include file="/includes/taskbar.html" />
<roundcube:include file="/includes/header.html" />
<div id="mainscreen" class="box darkbg">
<div class="boxtitle"><roundcube:label name="importcontacts" /></div>
<body class="iframe">
<div class="boxcontent">
<roundcube:object name="importstep" />
<p><br />
<roundcube:object name="importnav" class="button" />
</p>
</div>
</div>
</body>

@ -20,15 +20,17 @@
margin-bottom: 0.8em;
}
.rcmail-inline-message > button {
margin-left: 1em;
vertical-align: baseline;
}
.rcmail-inline-message em {
font-size: 90%;
}
.rcmail-inline-buttons {
margin-bottom: 0;
display: inline;
}
.rcmail-inline-buttons > button {
margin-left: 1em;
vertical-align: baseline;
line-height: 12px;
}

@ -92,3 +92,13 @@
border: 0;
}
.googie_ok_button {
background: url(images/googiespell/ok.gif) no-repeat center center transparent !important;
width: 32px;
height: 16px;
cursor: pointer;
margin: 0 5px;
padding: 0;
border: 0 !important;
text-indent: -5000px;
}

@ -19,7 +19,7 @@ var UI = new rcube_mail_ui();
<roundcube:container name="topline-right" id="topline-right" />
<roundcube:if condition="!env:extwin &amp;&amp; !env:framed" />
<span class="username"><roundcube:object name="username" /></span>
<roundcube:button command="logout" label="logout" class="button-logout" />
<roundcube:button command="logout" label="logout" type="link" class="button-logout" />
<roundcube:elseif condition="env:extwin" />
<roundcube:button name="close" type="link" label="close" class="closelink" onclick="self.close()" />
<roundcube:endif />
@ -29,11 +29,11 @@ var UI = new rcube_mail_ui();
<div id="topnav">
<h2 id="aria-label-tasknav" class="voice"><roundcube:label name="arialabeltasknav" /></h2>
<div id="taskbar" class="topright" role="navigation" aria-labelledby="aria-label-tasknav">
<roundcube:button command="mail" label="mail" class="button-mail" classSel="button-mail button-selected" innerClass="button-inner" />
<roundcube:button command="addressbook" label="contacts" class="button-addressbook" classSel="button-addressbook button-selected" innerClass="button-inner" />
<roundcube:button command="mail" label="mail" type="link" class="button-mail" classSel="button-mail button-selected" innerClass="button-inner" />
<roundcube:button command="addressbook" label="contacts" type="link" class="button-addressbook" classSel="button-addressbook button-selected" innerClass="button-inner" />
<roundcube:container name="taskbar" id="taskbar" />
<roundcube:button command="settings" label="settings" class="button-settings" classSel="button-settings button-selected" innerClass="button-inner" />
<roundcube:button command="logout" label="logout" class="button-logout" classSel="button-logout" innerClass="button-inner" />
<roundcube:button command="settings" label="settings" type="link" class="button-settings" classSel="button-settings button-selected" innerClass="button-inner" />
<roundcube:button command="logout" label="logout" type="link" class="button-logout" classSel="button-logout" innerClass="button-inner" />
<span class="minmodetoggle" role="presentation"></span>
</div>
<roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" alt="Logo" onclick="if(window.rcmail)rcmail.command('switch-task','mail')" />

@ -7,3 +7,4 @@
<script type="text/javascript" src="/ui.js"></script>
<roundcube:add_label name="errortitle" />
<roundcube:add_label name="toggleadvancedoptions" />
<roundcube:add_label name="options" />

@ -71,6 +71,7 @@ textarea {
input[type="text"]:focus,
input[type="password"]:focus,
button:focus,
input.button:focus,
textarea:focus {
border-color: #4787b1;
@ -107,6 +108,7 @@ label input + span {
/*** buttons ***/
button,
input.button {
display: inline-block;
margin: 0 2px;
@ -119,6 +121,7 @@ input.button {
outline: none;
}
.formbuttons button,
.formbuttons input.button {
color: #ddd;
font-size: 110%;
@ -129,6 +132,8 @@ input.button {
box-shadow: 0 1px 1px 0 #ccc;
}
.formbuttons button:hover,
.formbuttons button:focus,
.formbuttons input.button:hover,
.formbuttons input.button:focus,
input.button.mainaction:hover,
@ -138,17 +143,20 @@ input.button.mainaction:focus {
box-shadow: 0 0 5px 2px rgba(71,135,177, 0.6);
}
.formbuttons button:active,
.formbuttons input.button:active {
color: #fff;
background: #5f5f5f;
}
button.mainaction,
input.button.mainaction {
color: #ededed;
border-color: #1f262c;
background: #2c2f33;
}
button.mainaction:active,
input.button.mainaction:active {
color: #fff;
background: #515151;
@ -158,12 +166,15 @@ input.button.mainaction:active {
background: linear-gradient(to bottom, #2a2e31 0%, #505050 100%);
}
button[disabled],
button[disabled]:hover,
input.button[disabled],
input.button[disabled]:hover,
input.button.mainaction[disabled] {
color: #aaa !important;
}
button.mainaction,
input.mainaction {
font-weight: bold;
}
@ -195,6 +206,7 @@ a.button,
white-space: nowrap;
}
button:focus,
a.button:focus,
input.button:focus {
border-color: #017db6;
@ -203,6 +215,7 @@ input.button:focus {
}
label.disabled,
button.disabled,
a.button.disabled {
color: #999;
}
@ -210,16 +223,16 @@ a.button.disabled {
a.button.disabled,
input.button.disabled,
input.button[disabled],
button.disabled,
button[disabled],
button.disabled:hover,
button[disabled]:hover,
a.button.disabled:hover,
input.button.disabled:hover,
input.button[disabled]:hover {
border-color: #c6c6c6;
}
a.button.disabled span.inner {
opacity: 0.4;
}
.buttongroup a.button {
margin: 0;
border-width: 0 1px 0 0;
@ -241,6 +254,7 @@ a.button.disabled span.inner {
a.button.pressed,
a.button:active,
button:active,
input.button:active {
background: #f7f7f7;
}
@ -1731,13 +1745,13 @@ ul.proplist li {
border-color: #666;
}
#login-form input.button {
#login-form button.button {
color: #444;
border-color: #f9f9f9;
background-color: #f9f9f9;
}
#login-form input.button:active {
#login-form button.button:active {
color: #333;
background-color: #dcdcdc;
}
@ -2413,6 +2427,17 @@ ul.toolbarmenu li span.copy {
margin: 8px 0;
}
.ui-dialog iframe {
width: 100%;
height: 100%;
border: 0;
}
.ui-dialog-content.iframe {
padding: 0 !important;
overflow: hidden !important;
}
.hint {
margin: 4px 0;
color: #999;
@ -2503,23 +2528,23 @@ ul.toolbarmenu li span.copy {
text-overflow: ellipsis;
}
#folder-selector li a.virtual {
#folder-selector li a.virtual span {
opacity: .2;
}
#folder-selector li a.inbox span {
#folder-selector li.inbox span {
background-position: 4px -2049px;
}
#folder-selector li a.drafts span {
#folder-selector li.drafts span {
background-position: 4px -1388px;
}
#folder-selector li a.sent span {
#folder-selector li.sent span {
background-position: 4px -2074px;
}
#folder-selector li a.trash span {
#folder-selector li.trash span {
background-position: 4px -1508px;
}
#folder-selector li a.junk span {
#folder-selector li.junk span {
background-position: 4px -2100px;
}
@ -3009,6 +3034,8 @@ _:not(), _:-moz-handler-blocked, .mozilla .mce-btn-small i {
box-shadow: none;
}
button.mce-close,
.mce-btn button,
.mce-textbox:focus {
box-shadow: none;
outline: none;

@ -41,9 +41,9 @@
<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelcontactsearchform" /></h2>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label>
<roundcube:button command="menu-open" prop="searchmenu" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
<roundcube:button command="menu-open" type="link" prop="searchmenu" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
<roundcube:object name="searchform" id="quicksearchbox" />
<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
<roundcube:button command="reset-search" type="link" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
<div id="searchmenu" class="popupmenu" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>

@ -50,7 +50,7 @@
<label for="contactsearchbox" class="voice"><roundcube:label name="arialabelcontactsearchbox" /></label>
<roundcube:object name="searchform" id="contactsearchbox" />
<a id="searchmenulink" class="iconbutton searchicon"> </a>
<roundcube:button command="reset-search" class="iconbutton reset" title="resetsearch" content=" " />
<roundcube:button command="reset-search" type="link" class="iconbutton reset" title="resetsearch" content=" " />
</div>
</div>
<roundcube:object name="addressbooks" id="directorylist" class="treelist listing" summary="ariasummarycomposecontacts" />

@ -4,15 +4,12 @@
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<body class="iframe">
<body class="iframe fullheight">
<h1 class="boxtitle"><roundcube:label name="advsearch" /></h1>
<h1 class="voice"><roundcube:label name="addressbook" /> : <roundcube:label name="advsearch" /></h1>
<div id="contact-details" class="boxcontent">
<roundcube:object name="searchform" id="advsearchform" class="tabbed" size=30 />
<p class="formbuttons">
<roundcube:button command="save" type="input" class="button mainaction" label="search" />
</p>
</div>
<roundcube:include file="/includes/footer.html" />

@ -28,7 +28,7 @@
<label for="foldersearch" class="voice"><roundcube:label name="arialabelsearchterms" /></label>
<input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
<a class="iconbutton searchicon"></a>
<roundcube:button command="reset-foldersearch" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
<roundcube:button command="reset-foldersearch" type="link" id="folderlistsearch-reset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
</div>
</div>
<div id="folderslist-content" class="scroller withfooter">

@ -4,31 +4,12 @@
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<roundcube:if condition="env:extwin" /><body class="extwin"><roundcube:else /><body><roundcube:endif />
<roundcube:include file="/includes/header.html" />
<div id="mainscreen">
<body class="iframe fullheight">
<h1 class="voice"><roundcube:label name="addressbook" /> : <roundcube:label name="importcontacts" /></h1>
<h2 id="aria-label-toolbar" class="voice"><roundcube:label name="arialabeltoolbar" /></h2>
<div id="addressbooktoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar">
<roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" />
</div>
<div id="mainscreencontent" class="uibox contentbox">
<h2 class="boxtitle"><roundcube:label name="importcontacts" /></h2>
<div id="import-box" class="boxcontent">
<roundcube:object name="importstep" class="propform" />
<br/>
<p class="formbuttons">
<roundcube:object name="importnav" class="button" />
</p>
</div>
</div>
<div class="boxcontent">
<roundcube:object name="importstep" class="propform" />
</div>
<roundcube:include file="/includes/footer.html" />

@ -38,9 +38,9 @@
<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelmailsearchform" /></h2>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelmailquicksearchbox" /></label>
<roundcube:button command="menu-open" prop="searchmenu" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
<roundcube:button command="menu-open" prop="searchmenu" type="link" id="searchmenulink" class="iconbutton searchoptions" title="searchmod" label="options" aria-haspopup="true" aria-expanded="false" aria-owns="searchmenu-menu" />
<roundcube:object name="searchform" id="quicksearchbox" />
<roundcube:button command="reset-search" id="searchreset" class="iconbutton reset" title="resetsearch" label="resetsearch" />
<roundcube:button command="reset-search" id="searchreset" type="link" class="iconbutton reset" title="resetsearch" label="resetsearch" />
<div id="searchmenu" class="popupmenu" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
@ -116,8 +116,8 @@
<!-- list footer -->
<div id="messagelistfooter">
<div id="listcontrols">
<roundcube:button href="#list" command="set-listmode" prop="list" class="iconbutton listmode disabled" classAct="iconbutton listmode" id="maillistmode" title="list" content="List" />
<roundcube:button href="#threads" command="set-listmode" prop="threads" class="iconbutton threadmode disabled" classAct="iconbutton threadmode" id="mailthreadmode" title="threads" content="Threads" />
<roundcube:button href="#list" command="set-listmode" prop="list" type="link" class="iconbutton listmode disabled" classAct="iconbutton listmode" id="maillistmode" title="list" content="List" />
<roundcube:button href="#threads" command="set-listmode" prop="threads"type="link" class="iconbutton threadmode disabled" classAct="iconbutton threadmode" id="mailthreadmode" title="threads" content="Threads" />
</div>
<div id="listselectors">

@ -123,6 +123,13 @@ function rcube_mail_ui()
{
rcmail.addEventListener('message', message_displayed);
$.widget('ui.dialog', $.ui.dialog, {
open: function() {
this._super();
dialog_open(this);
return this;
}});
/*** prepare minmode functions ***/
$('#taskbar a').each(function(i,elem){
$(elem).append('<span class="tooltip">' + $('.button-inner', this).html() + '</span>')
@ -460,6 +467,22 @@ function rcube_mail_ui()
}
}
// modify dialog position to fully fit the close button into the window
function dialog_open(dialog)
{
var me = $(dialog.uiDialog),
offset = me.offset(),
position = me.position(),
width = me.outerWidth(),
maxWidth = $(window).width(),
topOffset = offset.top - 12;
if (topOffset < 0)
me.css('top', position.top - topOffset);
if (offset.left + width + 12 > maxWidth)
me.css('left', position.left - 12);
}
// Mail view layout initialization and change handler
function mail_layout(p)
{
@ -873,7 +896,10 @@ function rcube_mail_ui()
item = $(item);
if (!item.children('.drop').length)
item.append($('<a class="drop skip-content" tabindex="0" aria-haspopup="true">Show options</a>')
var label = rcmail.gettext('options');
item.append($('<a>')
.attr({'class': 'drop skip-content', tabindex: 0, 'aria-haspopup': true, title: label})
.text(label)
.on('click keypress', function(e) {
if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
attachmentmenu(this, e);

Loading…
Cancel
Save