Merge branch 'dev-elastic'

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

@ -1,6 +1,17 @@
CHANGELOG Roundcube Webmail 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) - 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: Fix archiving by sender address on cyrus-imap
- Archive: Add Thunderbird compatible Month option (#5623) - 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'), id ? this.get_label('acl.editperms') : this.get_label('acl.newuser'),
buttons, buttons,
{ {
button_classes: ['mainaction'], button_classes: ['mainaction submit'],
modal: true, modal: true,
closeOnEscape: true, closeOnEscape: true,
close: function(e, ui) { close: function(e, ui) {

@ -43,6 +43,7 @@ class archive extends rcube_plugin
'height' => 32, 'height' => 32,
'title' => 'buttontitle', 'title' => 'buttontitle',
'domain' => $this->ID, 'domain' => $this->ID,
'innerclass' => 'inner',
), ),
'toolbar'); 'toolbar');
@ -399,8 +400,8 @@ class archive extends rcube_plugin
} }
$args['blocks']['main']['options']['archive_mbox'] = array( $args['blocks']['main']['options']['archive_mbox'] = array(
'title' => $this->gettext('archivefolder'), 'title' => html::label('_archive_mbox', rcube::Q($this->gettext('archivefolder'))),
'content' => $select->show($mbox, array('name' => "_archive_mbox")) 'content' => $select->show($mbox, array('id' => '_archive_mbox', 'name' => '_archive_mbox'))
); );
// add option for structuring the archive folder // add option for structuring the archive folder
@ -413,9 +414,9 @@ class archive extends rcube_plugin
$archive_type->add($this->gettext('archivetypefolder'), 'folder'); $archive_type->add($this->gettext('archivetypefolder'), 'folder');
$args['blocks']['archive'] = array( $args['blocks']['archive'] = array(
'name' => rcube::Q($this->gettext('settingstitle')), 'name' => rcube::Q($this->gettext('settingstitle')),
'options' => array('archive_type' => array( '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) '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)) { 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)); $chbox = new html_checkbox(array('name' => '_read_on_archive', 'id' => 'ff_read_on_archive', 'value' => 1));
$args['blocks']['main']['options']['read_on_archive'] = array( $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) '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.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->task == 'mail' && $rcmail->action == 'compose') {
if ($rcmail->config->get('attachment_reminder')) { if ($rcmail->config->get('attachment_reminder')) {
$this->include_script('attachment_reminder.js'); $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'); $rcmail->output->add_label('addattachment', 'send');
} }
} }

@ -17,6 +17,7 @@
*/ */
$messages = array(); $messages = array();
$messages['missingattachment'] = "Missing attachment?";
$messages['forgotattachment'] = "Did you forget to attach a file?"; $messages['forgotattachment'] = "Did you forget to attach a file?";
$messages['reminderoption'] = "Remind about forgotten attachments"; $messages['reminderoption'] = "Remind about forgotten attachments";
$messages['keywords'] = "attachment,file,attach,attached,attaching,enclosed,CV,cover letter"; $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'), this.show_popup_dialog(myprompt, this.get_label('enigma.enterkeypasstitle'),
[{ [{
text: this.get_label('save'), text: this.get_label('save'),
'class': 'mainaction', 'class': 'mainaction save',
click: function(e) { click: function(e) {
e.stopPropagation(); e.stopPropagation();
@ -519,6 +519,7 @@ rcube_webmail.prototype.enigma_password_request = function(data)
}, },
{ {
text: this.get_label('cancel'), text: this.get_label('cancel'),
'class': 'cancel',
click: function(e) { click: function(e) {
var jq = ref.is_framed() ? window.parent.$ : $; var jq = ref.is_framed() ? window.parent.$ : $;
e.stopPropagation(); e.stopPropagation();

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

@ -47,21 +47,6 @@ class jqueryui extends rcube_plugin
$this->include_stylesheet("themes/larry/jquery-ui.css"); $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 localization
$jquery_ui_i18n = $rcmail->config->get('jquery_ui_i18n', array('datepicker')); $jquery_ui_i18n = $rcmail->config->get('jquery_ui_i18n', array('datepicker'));
if (count($jquery_ui_i18n) > 0) { if (count($jquery_ui_i18n) > 0) {

@ -279,8 +279,7 @@ class password extends rcube_plugin
$submit_button = $rcmail->output->button(array( $submit_button = $rcmail->output->button(array(
'command' => 'plugin.password-save', 'command' => 'plugin.password-save',
'type' => 'input', 'class' => 'button mainaction save',
'class' => 'button mainaction',
'label' => 'save', 'label' => 'save',
)); ));
$form_buttons = html::p(array('class' => 'formbuttons'), $submit_button); $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.html('').append(span);
} }
link.attr('aria-haspopup', 'true');
span.text(rcmail.get_label('zipdownload.download')); span.text(rcmail.get_label('zipdownload.download'));
rcmail.env.download_link = link; rcmail.env.download_link = link;
}); });

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

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

@ -27,12 +27,14 @@
*/ */
class rcmail_html_page extends rcmail_output_html class rcmail_html_page extends rcmail_output_html
{ {
protected $inline_warning;
public function write($contents = '') public function write($contents = '')
{ {
self::reset(true); self::reset(true);
// load embed.css from skin folder (if exists) // 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); $this->include_css($embed_css);
} }
else { // set default styles for warning blocks inside the attachment part frame 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); 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']); $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 // 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 // allow (legal) iframe content to be loaded
$iframe = $this->framed || $this->env['framed']; $iframe = $this->framed || $this->env['framed'];
@ -1094,6 +1100,14 @@ EOF;
// include a file // include a file
case 'include': 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; $old_base_path = $this->base_path;
if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path']; if (!empty($attrib['skin_path'])) $attrib['skinpath'] = $attrib['skin_path'];
if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) { if ($path = $this->get_skin_file($attrib['file'], $skin_path, $attrib['skinpath'])) {
@ -1213,6 +1227,17 @@ EOF;
return $hook['content']; 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 // return code for a specified eval expression
case 'exp': case 'exp':
return html::quote($this->eval_expression($attrib['expression'])); return html::quote($this->eval_expression($attrib['expression']));
@ -1325,7 +1350,7 @@ EOF;
} }
} }
else { else {
$attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link'; $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'button';
} }
$command = $attrib['command']; $command = $attrib['command'];
@ -1461,11 +1486,22 @@ EOF;
$attrib['value'] = $attrib['label']; $attrib['value'] = $attrib['label'];
} }
if ($attrib['command']) { if ($attrib['command']) {
$attrib['disabled'] = 'disabled'; $attrib['disabled'] = 'disabled';
} }
$out = html::tag('input', $attrib, null, array('type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex', '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 // generate html code for button
if ($btn_content) { if ($btn_content) {
@ -1512,7 +1548,7 @@ EOF;
* @param string $script JS code snippet * @param string $script JS code snippet
* @param string $position Target position [head|head_top|foot] * @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])) { if (!isset($this->scripts[$position])) {
$this->scripts[$position] = "\n" . rtrim($script); $this->scripts[$position] = "\n" . rtrim($script);
@ -1915,9 +1951,8 @@ EOF;
} }
if (rcube_utils::get_boolean($attrib['submit'])) { if (rcube_utils::get_boolean($attrib['submit'])) {
$submit = new html_inputfield(array('type' => 'submit', 'id' => 'rcmloginsubmit', $button_attr = array('type' => 'submit', 'id' => 'rcmloginsubmit', 'class' => 'button mainaction submit');
'class' => 'button mainaction', 'value' => $this->app->gettext('login'))); $out .= html::p('formbuttons', html::tag('button', $button_attr, $this->app->gettext('login')));
$out .= html::p('formbuttons', $submit->show());
} }
// surround html output with a form tag // surround html output with a form tag
@ -1976,21 +2011,72 @@ EOF;
if ($attrib['type'] == 'search' && !$this->browser->khtml) { if ($attrib['type'] == 'search' && !$this->browser->khtml) {
unset($attrib['type'], $attrib['results']); 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); $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']); $this->add_gui_object('qsearchbox', $attrib['id']);
// add form tag around text field // add form tag around text field
if (empty($attrib['form'])) { if (empty($attrib['form']) && empty($attrib['no-form'])) {
$out = $this->form_tag(array( $out = $this->form_tag(array(
'name' => "rcmqsearchform", 'name' => "rcmqsearchform",
'onsubmit' => self::JS_OBJECT_NAME . ".command('search'); return false", 'onsubmit' => self::JS_OBJECT_NAME . ".command('search'); return false",
'style' => "display:inline" // 'style' => "display:inline"
), $out); ), $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; return $out;
} }

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

@ -60,6 +60,7 @@ function roundcube_browser()
this.webkit = this.agent_lc.indexOf('applewebkit') > 0; this.webkit = this.agent_lc.indexOf('applewebkit') > 0;
this.ie = (document.all && !window.opera) || (this.win && this.agent_lc.indexOf('trident/') > 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) { if (window.opera) {
this.opera = true; // Opera < 15 this.opera = true; // Opera < 15
@ -108,7 +109,9 @@ function roundcube_browser()
var classname = ' js'; var classname = ' js';
if (this.ie) 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) else if (this.opera)
classname += ' opera'; classname += ' opera';
else if (this.konq) 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 // array utility function
jQuery.last = function(arr) { jQuery.last = function(arr) {
return arr && arr.length ? arr[arr.length-1] : undefined; 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 // textarea identifier
this.id = id; this.id = id;
// reference to active editor (if in HTML mode) // 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.lang_to_word = this.org_lang_to_word;
this.langlist_codes = this.array_keys(this.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.change_lang_pic_placement = 'right';
this.report_state_change = true; 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_no_suggestions = "No suggestions";
this.lang_learn_word = "Add to dictionary"; this.lang_learn_word = "Add to dictionary";
this.use_ok_pic = false; // added by roundcube
this.show_spell_img = false; // roundcube mod. this.show_spell_img = false; // roundcube mod.
this.decoration = true; this.decoration = true;
this.use_close_btn = false; this.use_close_btn = false;
@ -505,7 +506,7 @@ this.showErrorWindow = function(elm, id)
item = document.createElement('td'), item = document.createElement('td'),
dummy = document.createElement('span'); 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') $(item).attr('googie_action_btn', '1').css('cursor', 'default')
.mouseover(ref.item_onmouseover) .mouseover(ref.item_onmouseover)
.mouseout(ref.item_onmouseout) .mouseout(ref.item_onmouseout)
@ -572,7 +573,7 @@ this.showErrorWindow = function(elm, id)
var edit_row = document.createElement('tr'), var edit_row = document.createElement('tr'),
edit = document.createElement('td'), edit = document.createElement('td'),
edit_input = document.createElement('input'), edit_input = document.createElement('input'),
ok_pic = document.createElement('img'), ok_pic = document.createElement('button'), // roundcube mod.
edit_form = document.createElement('form'); edit_form = document.createElement('form');
var onsub = function () { var onsub = function () {
@ -592,10 +593,18 @@ this.showErrorWindow = function(elm, id)
.val($(elm).text()).attr('googie_action_btn', '1'); .val($(elm).text()).attr('googie_action_btn', '1');
$(edit).css('cursor', 'default').attr('googie_action_btn', '1'); $(edit).css('cursor', 'default').attr('googie_action_btn', '1');
$(ok_pic).attr('src', this.img_dir + 'ok.gif') // roundcube modified image use
.width(32).height(16) if (this.use_ok_pic) {
.css({'cursor': 'pointer', 'margin-left': '2px', 'margin-right': '2px'}) $('<img>').attr('src', this.img_dir + 'ok.gif')
.click(onsub); .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') $(edit_form).attr('googie_action_btn', '1')
.css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'}) .css({'margin': 0, 'padding': 0, 'cursor': 'default', 'white-space': 'nowrap'})
@ -643,6 +652,9 @@ this.showErrorWindow = function(elm, id)
table.appendChild(list); table.appendChild(list);
this.error_window.appendChild(table); this.error_window.appendChild(table);
// roundcube plugin api hook
rcmail.triggerEvent('googiespell_create', {obj: this.error_window});
// calculate and set position // calculate and set position
var height = $(this.error_window).height(), var height = $(this.error_window).height(),
width = $(this.error_window).width(), 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, top = pos.top + height + 20 < pageheight ? pos.top + 20 : pos.top - height,
left = pos.left + width < pagewidth ? pos.left : pos.left - width; 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 // Dummy for IE - dropdown bug fix
if (document.all && !window.opera) { if (document.all && !window.opera) {

@ -62,6 +62,7 @@ function rcube_list_widget(list, p)
this.toggleselect = false; this.toggleselect = false;
this.aria_listbox = false; this.aria_listbox = false;
this.parent_focus = true; this.parent_focus = true;
this.checkbox_selection = false;
this.drag_active = false; this.drag_active = false;
this.col_drag_active = false; this.col_drag_active = false;
@ -189,7 +190,7 @@ init_row: function(row)
$(row) $(row)
.attr('role', 'option') .attr('role', 'option')
.attr('aria-labelledby', lbl_id) .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) if (document.all)
@ -380,6 +381,27 @@ insert_row: function(row, before)
row = domrow; 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) if (before && tbody.childNodes.length)
tbody.insertBefore(row, (typeof before == 'object' && before.parentNode == tbody) ? before : tbody.firstChild); tbody.insertBefore(row, (typeof before == 'object' && before.parentNode == tbody) ? before : tbody.firstChild);
else else
@ -437,7 +459,7 @@ focus: function(e)
var focus_elem = null; var focus_elem = null;
if (this.last_selected && this.rows[this.last_selected]) { 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) // 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]) { if (this.last_selected && this.rows[this.last_selected]) {
$(this.rows[this.last_selected].obj) $(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'); $(this.list).removeClass('focus');
@ -922,7 +944,7 @@ col_tagname: function()
get_cell: function(row, index) 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]) { if (this.last_selected && this.rows[this.last_selected]) {
$(this.rows[this.last_selected].obj).removeClass('focused') $(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 // 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'); $(this.rows[id].obj).addClass('focused');
// set cursor focus to link inside selected row // set cursor focus to link inside selected row
if (this.focused) 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) if (!this.selection.length)
@ -1199,6 +1221,9 @@ clear_selection: function(id, no_event)
this.selection = []; 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) { if (num_select && !this.selection.length && !no_event) {
this.triggerEvent('select'); this.triggerEvent('select');
this.last_selected = null; this.last_selected = null;
@ -1257,6 +1282,9 @@ highlight_row: function(id, multiple, norecur)
this.clear_selection(null, true); this.clear_selection(null, true);
this.selection[0] = id; this.selection[0] = id;
$(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true'); $(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true');
if (this.checkbox_selection)
$('.selection > input', this.rows[id].obj).prop('checked', true);
} }
} }
else { else {
@ -1265,6 +1293,10 @@ highlight_row: function(id, multiple, norecur)
if (p === false) { // select row if (p === false) { // select row
this.selection.push(id); this.selection.push(id);
$(this.rows[id].obj).addClass('selected').attr('aria-selected', 'true'); $(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) if (!norecur && !this.rows[id].expanded)
this.highlight_children(id, true); this.highlight_children(id, true);
} }
@ -1274,6 +1306,10 @@ highlight_row: function(id, multiple, norecur)
this.selection = pre.concat(post); this.selection = pre.concat(post);
$(this.rows[id].obj).removeClass('selected').removeAttr('aria-selected'); $(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) if (!norecur && !this.rows[id].expanded)
this.highlight_children(id, false); this.highlight_children(id, false);
} }
@ -1529,8 +1565,10 @@ drag_mouse_move: function(e)
return false; return false;
} }
var subject_col = self.subject_column();
$('> ' + self.col_tagname(), self.rows[uid].obj).each(function(n, cell) { $('> ' + 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 // remove elements marked with "skip-on-drag" class
cell = $(cell).clone(); cell = $(cell).clone();
$(cell).find('.skip-on-drag').remove(); $(cell).find('.skip-on-drag').remove();
@ -1824,6 +1862,11 @@ column_replace: function(from, to)
this.init_header(); this.init_header();
this.triggerEvent('column_replace'); 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 $doctype = 'xhtml';
public static $lc_tags = true; public static $lc_tags = true;
public static $common_attrib = array('id','class','style','title','align','unselectable','tabindex','role'); 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'); public static $bool_attrib = array('checked','multiple','disabled','selected','autofocus','readonly');

@ -31,6 +31,7 @@ class rcube_config
private $prop = array(); private $prop = array();
private $errors = array(); private $errors = array();
private $userprefs = array(); private $userprefs = array();
private $immutable = array();
/** /**
@ -410,12 +411,17 @@ class rcube_config
/** /**
* Setter for a config parameter * Setter for a config parameter
* *
* @param string $name Parameter name * @param string $name Parameter name
* @param mixed $value Parameter value * @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; $this->prop[$name] = $value;
if ($immutable) {
$this->immutable[$name] = $value;
}
} }
/** /**
@ -426,7 +432,7 @@ class rcube_config
public function merge($prefs) public function merge($prefs)
{ {
$prefs = $this->fix_legacy_props($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'; $labels['login'] = 'Login';
// taskbar // taskbar
$labels['menu'] = 'Menu';
$labels['logout'] = 'Logout'; $labels['logout'] = 'Logout';
$labels['mail'] = 'Mail'; $labels['mail'] = 'Mail';
$labels['settings'] = 'Settings'; $labels['settings'] = 'Settings';
@ -54,6 +55,7 @@ $labels['priority'] = 'Priority';
$labels['organization'] = 'Organization'; $labels['organization'] = 'Organization';
$labels['readstatus'] = 'Read status'; $labels['readstatus'] = 'Read status';
$labels['listoptions'] = 'List options...'; $labels['listoptions'] = 'List options...';
$labels['listoptionstitle'] = 'List options';
$labels['mailboxlist'] = 'Folders'; $labels['mailboxlist'] = 'Folders';
$labels['messagesfromto'] = 'Messages $from to $to of $count'; $labels['messagesfromto'] = 'Messages $from to $to of $count';
@ -226,6 +228,7 @@ $labels['quotastorage'] = 'Disk space';
$labels['quotamessage'] = 'Messages count'; $labels['quotamessage'] = 'Messages count';
$labels['quicksearch'] = 'Quick search'; $labels['quicksearch'] = 'Quick search';
$labels['searchplaceholder'] = 'Search...';
$labels['resetsearch'] = 'Reset search'; $labels['resetsearch'] = 'Reset search';
$labels['searchmod'] = 'Search modifiers'; $labels['searchmod'] = 'Search modifiers';
$labels['msgtext'] = 'Entire message'; $labels['msgtext'] = 'Entire message';
@ -586,6 +589,8 @@ $labels['personalfolder'] = 'Private Folder';
$labels['otherfolder'] = 'Other User\'s Folder'; $labels['otherfolder'] = 'Other User\'s Folder';
$labels['sharedfolder'] = 'Public Folder'; $labels['sharedfolder'] = 'Public Folder';
$labels['findfolders'] = 'Find folders'; $labels['findfolders'] = 'Find folders';
$labels['findcontacts'] = 'Find contacts';
$labels['findmail'] = 'Find mail messages';
$labels['namespace.personal'] = 'Personal'; $labels['namespace.personal'] = 'Personal';
$labels['namespace.other'] = 'Other users'; $labels['namespace.other'] = 'Other users';
$labels['namespace.shared'] = 'Shared'; $labels['namespace.shared'] = 'Shared';
@ -601,6 +606,7 @@ $labels['version'] = 'Version';
$labels['source'] = 'Source'; $labels['source'] = 'Source';
$labels['license'] = 'License'; $labels['license'] = 'License';
$labels['support'] = 'Get support'; $labels['support'] = 'Get support';
$labels['savedsearches'] = 'Saved searches';
// units // units
$labels['B'] = 'B'; $labels['B'] = 'B';
@ -661,6 +667,7 @@ $labels['arialabelmarkmessagesas'] = 'Mark selected messages as...';
$labels['arialabelcomposeoptions'] = 'Composition options'; $labels['arialabelcomposeoptions'] = 'Composition options';
$labels['arialabelresponsesmenu'] = 'Canned responses menu'; $labels['arialabelresponsesmenu'] = 'Canned responses menu';
$labels['arialabelattachmentuploadform'] = 'Attachment upload form'; $labels['arialabelattachmentuploadform'] = 'Attachment upload form';
$labels['arialabelattachmentmenu'] = 'Attachment options';
$labels['arialabelattachmentpreview'] = 'Attachment preview'; $labels['arialabelattachmentpreview'] = 'Attachment preview';
$labels['ariasummarycomposecontacts'] = 'List of contacts and groups to select as recipients'; $labels['ariasummarycomposecontacts'] = 'List of contacts and groups to select as recipients';
$labels['arialabelcontactexportoptions'] = 'Contact export options'; $labels['arialabelcontactexportoptions'] = 'Contact export options';
@ -669,6 +676,7 @@ $labels['arialabelpreferencesform'] = 'Preferences form';
$labels['arialabelidentityeditfrom'] = 'Identity edit form'; $labels['arialabelidentityeditfrom'] = 'Identity edit form';
$labels['arialabelresonseeditfrom'] = 'Response edit form'; $labels['arialabelresonseeditfrom'] = 'Response edit form';
$labels['arialabelsearchterms'] = 'Search terms'; $labels['arialabelsearchterms'] = 'Search terms';
$labels['arialabeldropactionmenu'] = 'Drag-n-Drop action menu';
$labels['helplistnavigation'] = 'List keyboard navigation'; $labels['helplistnavigation'] = 'List keyboard navigation';
$labels['helplistkeyboardnavigation'] = "Arrows up/down: Move row focus/selection. $labels['helplistkeyboardnavigation'] = "Arrows up/down: Move row focus/selection.

@ -260,7 +260,8 @@ function rcmail_directory_list($attrib)
// add some labels to client // add some labels to client
$OUTPUT->add_label('deletegroupconfirm', 'groupdeleting', 'addingmember', 'removingmember', $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); 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( $content = html::div($attrib, html::img(array(
'src' => $photo_img, 'src' => $photo_img,
'alt' => $RCMAIL->gettext('contactphoto'), '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')) { if ($CONTACT_COLTYPES['photo'] && ($RCMAIL->action == 'edit' || $RCMAIL->action == 'add')) {

@ -180,12 +180,15 @@ $OUTPUT->set_pagetitle($RCMAIL->gettext('importcontacts'));
$OUTPUT->add_handlers(array( $OUTPUT->add_handlers(array(
'importstep' => $importstep, 'importstep' => $importstep,
'importnav' => 'rcmail_import_buttons',
)); ));
// render page // 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 .= $abook->show();
} }
$form .= html::tag('input', array('type' => 'hidden', 'name' => '_unlock', 'value' => ''));
// selector for group import options // selector for group import options
if (count($writable_books) >= 1 || $writable_books[0]->groups) { if (count($writable_books) >= 1 || $writable_books[0]->groups) {
$select = new html_select(array('name' => '_groups', 'id' => 'rcmimportgroups', 'is_escaped' => true)); $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); 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. * 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 // Re-set list header
$OUTPUT->command('set_group_prop', null); $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) { if (!$sid) {
// unselect currently selected directory/group // unselect currently selected directory/group
$OUTPUT->command('unselect_directory'); $OUTPUT->command('unselect_directory');
@ -259,7 +249,7 @@ function rcmail_contact_search()
$OUTPUT->command('update_group_commands'); $OUTPUT->command('update_group_commands');
// send response // send response
$OUTPUT->send($adv ? 'iframe' : null); $OUTPUT->send();
} }
function rcmail_contact_search_form($attrib) function rcmail_contact_search_form($attrib)

@ -291,6 +291,7 @@ $OUTPUT->include_script('publickey.js');
// register UI objects // register UI objects
$OUTPUT->add_handlers(array( $OUTPUT->add_handlers(array(
'composeformhead' => 'rcmail_compose_form_head',
'composeheaders' => 'rcmail_compose_headers', 'composeheaders' => 'rcmail_compose_headers',
'composesubject' => 'rcmail_compose_subject', 'composesubject' => 'rcmail_compose_subject',
'composebody' => 'rcmail_compose_body', '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) function rcmail_compose_headers($attrib)
{ {
global $RCMAIL, $MESSAGE; global $RCMAIL, $MESSAGE;
@ -457,7 +465,7 @@ function rcmail_compose_headers($attrib)
// pass the following attributes to the form class // pass the following attributes to the form class
$field_attrib = array('name' => $fname, 'spellcheck' => 'false'); $field_attrib = array('name' => $fname, 'spellcheck' => 'false');
foreach ($attrib as $attr => $value) { 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; $field_attrib[$attr] = $value;
} }
} }
@ -1805,6 +1813,10 @@ function get_form_tags($attrib)
{ {
global $RCMAIL, $MESSAGE_FORM, $COMPOSE; global $RCMAIL, $MESSAGE_FORM, $COMPOSE;
if (rcube_utils::get_boolean((string) $attrib['noform'])) {
return array('', '');
}
$form_start = ''; $form_start = '';
if (!$MESSAGE_FORM) { if (!$MESSAGE_FORM) {
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task)); $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'); $a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc');
if (!empty($attrib['optionsmenuicon'])) { if (!empty($attrib['optionsmenuicon'])) {
$list_menu = rcmail_options_menu_link($attrib); $list_menu = rcmail_options_menu_link();
} }
$cells = $coltypes = array(); $cells = $coltypes = array();
@ -659,7 +659,7 @@ function rcmail_message_list_head($attrib, $a_show_cols)
return $cells; return $cells;
} }
function rcmail_options_menu_link($attrib) function rcmail_options_menu_link($attrib = array())
{ {
global $RCMAIL; global $RCMAIL;
@ -669,11 +669,14 @@ function rcmail_options_menu_link($attrib)
if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') { if (is_string($attrib['optionsmenuicon']) && $attrib['optionsmenuicon'] != 'true') {
$inner = html::img(array('src' => $RCMAIL->output->abs_url($attrib['optionsmenuicon'], true), 'alt' => $title)); $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( return html::a(array(
'href' => '#list-options', 'href' => '#list-options',
'onclick' => $onclick, 'onclick' => $onclick,
'class' => 'listmenu', 'class' => isset($attrib['class']) ? $attrib['class'] : 'listmenu',
'id' => 'listmenulink', 'id' => 'listmenulink',
'title' => $title, 'title' => $title,
'tabindex' => '0', 'tabindex' => '0',
@ -1701,18 +1704,21 @@ function rcmail_address_string($input, $max=null, $linked=false, $addicon=null,
} }
if ($addicon && $_SESSION['writeable_abook']) { 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( $address .= html::a(array(
'href' => "#add", 'href' => "#add",
'title' => $RCMAIL->gettext('addtoaddressbook'), 'title' => $label,
'class' => 'rcmaddcontact', 'class' => 'rcmaddcontact',
'onclick' => sprintf("return %s.command('add-contact','%s',this)", 'onclick' => sprintf("return %s.command('add-contact','%s',this)",
rcmail_output::JS_OBJECT_NAME, rcube::JQ($string)), rcmail_output::JS_OBJECT_NAME, rcube::JQ($string)),
), ),
html::img(array( $addicon == 'virtual' ? '' : $icon
'src' => $RCMAIL->output->abs_url($addicon, true), );
'alt' => "Add contact",
'class' => 'noselect',
)));
} }
} }
else { else {

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

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

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

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

@ -671,6 +671,18 @@ img.uploading
margin-left: 10px; 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 ******/ /***** common table settings ******/
table.records-table thead tr th, table.records-table thead tr th,
@ -1355,7 +1367,7 @@ ul.toolbarmenu li.separator_above
padding: 0; padding: 0;
} }
#folder-selector li a span #folder-selector li span
{ {
background: url(images/icons/folders.png) no-repeat 6px 0; background: url(images/icons/folders.png) no-repeat 6px 0;
display: block; display: block;
@ -1377,23 +1389,23 @@ ul.toolbarmenu li.separator_above
color: white; color: white;
} }
#folder-selector li a.inbox span #folder-selector li.inbox span
{ {
background-position: 6px -18px; background-position: 6px -18px;
} }
#folder-selector li a.drafts span #folder-selector li.drafts span
{ {
background-position: 6px -37px; background-position: 6px -37px;
} }
#folder-selector li a.sent span #folder-selector li.sent span
{ {
background-position: 6px -54px; background-position: 6px -54px;
} }
#folder-selector li a.trash span #folder-selector li.trash span
{ {
background-position: 6px -91px; background-position: 6px -91px;
} }
#folder-selector li a.junk span #folder-selector li.junk span
{ {
background-position: 6px -73px; background-position: 6px -73px;
} }

@ -116,3 +116,13 @@
border-right: 1px solid #555; border-right: 1px solid #555;
border-bottom: 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"> <div id="taskbar">
<roundcube:button command="mail" label="mail" class="button-mail" /> <roundcube:button command="mail" type="link" label="mail" class="button-mail" />
<roundcube:button command="addressbook" label="contacts" class="button-addressbook" /> <roundcube:button command="addressbook" type="link" label="contacts" class="button-addressbook" />
<roundcube:container name="taskbar" id="taskbar" /> <roundcube:container name="taskbar" id="taskbar" />
<roundcube:button command="settings" label="settings" class="button-settings" /> <roundcube:button command="settings" type="link" label="settings" class="button-settings" />
<roundcube:button command="logout" label="logout" class="button-logout" /> <roundcube:button command="logout" type="link" label="logout" class="button-logout" />
</div> </div>

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

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

@ -28,7 +28,7 @@
<div class="searchbox" role="search"> <div class="searchbox" role="search">
<input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" /> <input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
<a class="iconbutton searchicon"></a> <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> </div>
<div id="folderlist-content" class="boxlistcontent"> <div id="folderlist-content" class="boxlistcontent">

@ -4,22 +4,10 @@
<title><roundcube:object name="pagetitle" /></title> <title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" /> <roundcube:include file="/includes/links.html" />
</head> </head>
<body> <body class="iframe">
<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>
<div class="boxcontent"> <div class="boxcontent">
<roundcube:object name="importstep" /> <roundcube:object name="importstep" />
<p><br />
<roundcube:object name="importnav" class="button" />
</p>
</div>
</div> </div>
</body> </body>

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

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

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

@ -41,9 +41,9 @@
<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform"> <div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelcontactsearchform" /></h2> <h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelcontactsearchform" /></h2>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelquicksearchbox" /></label> <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: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"> <div id="searchmenu" class="popupmenu" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3> <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> <label for="contactsearchbox" class="voice"><roundcube:label name="arialabelcontactsearchbox" /></label>
<roundcube:object name="searchform" id="contactsearchbox" /> <roundcube:object name="searchform" id="contactsearchbox" />
<a id="searchmenulink" class="iconbutton searchicon"> </a> <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>
</div> </div>
<roundcube:object name="addressbooks" id="directorylist" class="treelist listing" summary="ariasummarycomposecontacts" /> <roundcube:object name="addressbooks" id="directorylist" class="treelist listing" summary="ariasummarycomposecontacts" />

@ -4,15 +4,12 @@
<title><roundcube:object name="pagetitle" /></title> <title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" /> <roundcube:include file="/includes/links.html" />
</head> </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"> <div id="contact-details" class="boxcontent">
<roundcube:object name="searchform" id="advsearchform" class="tabbed" size=30 /> <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> </div>
<roundcube:include file="/includes/footer.html" /> <roundcube:include file="/includes/footer.html" />

@ -28,7 +28,7 @@
<label for="foldersearch" class="voice"><roundcube:label name="arialabelsearchterms" /></label> <label for="foldersearch" class="voice"><roundcube:label name="arialabelsearchterms" /></label>
<input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" /> <input type="text" name="q" id="foldersearch" placeholder="<roundcube:label name='findfolders' />" />
<a class="iconbutton searchicon"></a> <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> </div>
<div id="folderslist-content" class="scroller withfooter"> <div id="folderslist-content" class="scroller withfooter">

@ -4,31 +4,12 @@
<title><roundcube:object name="pagetitle" /></title> <title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" /> <roundcube:include file="/includes/links.html" />
</head> </head>
<roundcube:if condition="env:extwin" /><body class="extwin"><roundcube:else /><body><roundcube:endif /> <body class="iframe fullheight">
<roundcube:include file="/includes/header.html" />
<div id="mainscreen">
<h1 class="voice"><roundcube:label name="addressbook" /> : <roundcube:label name="importcontacts" /></h1> <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 class="boxcontent">
<div id="addressbooktoolbar" class="toolbar" role="toolbar" aria-labelledby="aria-label-toolbar"> <roundcube:object name="importstep" class="propform" />
<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> </div>
<roundcube:include file="/includes/footer.html" /> <roundcube:include file="/includes/footer.html" />

@ -38,9 +38,9 @@
<div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform"> <div id="quicksearchbar" class="searchbox" role="search" aria-labelledby="aria-label-searchform">
<h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelmailsearchform" /></h2> <h2 id="aria-label-searchform" class="voice"><roundcube:label name="arialabelmailsearchform" /></h2>
<label for="quicksearchbox" class="voice"><roundcube:label name="arialabelmailquicksearchbox" /></label> <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: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"> <div id="searchmenu" class="popupmenu" data-editable="true">
<h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3> <h3 id="aria-label-searchmenu" class="voice"><roundcube:label name="searchmod" /></h3>
@ -116,8 +116,8 @@
<!-- list footer --> <!-- list footer -->
<div id="messagelistfooter"> <div id="messagelistfooter">
<div id="listcontrols"> <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="#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" class="iconbutton threadmode disabled" classAct="iconbutton threadmode" id="mailthreadmode" title="threads" content="Threads" /> <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>
<div id="listselectors"> <div id="listselectors">

@ -123,6 +123,13 @@ function rcube_mail_ui()
{ {
rcmail.addEventListener('message', message_displayed); rcmail.addEventListener('message', message_displayed);
$.widget('ui.dialog', $.ui.dialog, {
open: function() {
this._super();
dialog_open(this);
return this;
}});
/*** prepare minmode functions ***/ /*** prepare minmode functions ***/
$('#taskbar a').each(function(i,elem){ $('#taskbar a').each(function(i,elem){
$(elem).append('<span class="tooltip">' + $('.button-inner', this).html() + '</span>') $(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 // Mail view layout initialization and change handler
function mail_layout(p) function mail_layout(p)
{ {
@ -873,7 +896,10 @@ function rcube_mail_ui()
item = $(item); item = $(item);
if (!item.children('.drop').length) 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) { .on('click keypress', function(e) {
if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) { if (e.type != 'keypress' || rcube_event.get_keycode(e) == 13) {
attachmentmenu(this, e); attachmentmenu(this, e);

Loading…
Cancel
Save