summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.htaccess21
-rw-r--r--CHANGELOG8
-rw-r--r--config/.htaccess2
-rw-r--r--config/defaults.inc.php8
-rw-r--r--index.php14
-rw-r--r--logs/.gitignore1
-rw-r--r--logs/.htaccess2
-rw-r--r--plugins/archive/archive.js9
-rw-r--r--plugins/help/skins/larry/templates/help.html1
-rw-r--r--plugins/http_authentication/http_authentication.php10
-rw-r--r--plugins/managesieve/skins/larry/templates/managesieve.html1
-rw-r--r--plugins/newmail_notifier/newmail_notifier.js2
-rw-r--r--plugins/password/drivers/ldap.php2
-rw-r--r--plugins/password/drivers/ldap_simple.php2
-rw-r--r--program/.htaccess4
-rw-r--r--program/include/rcmail.php3
-rw-r--r--program/include/rcmail_output_html.php19
-rw-r--r--program/js/app.js413
-rw-r--r--program/js/common.js78
-rw-r--r--program/js/editor.js3
-rw-r--r--program/js/list.js16
-rw-r--r--program/lib/Roundcube/rcube.php4
-rw-r--r--program/lib/Roundcube/rcube_browser.php36
-rw-r--r--program/lib/Roundcube/rcube_contacts.php8
-rw-r--r--program/lib/Roundcube/rcube_csv2vcard.php10
-rw-r--r--program/lib/Roundcube/rcube_db.php29
-rw-r--r--program/lib/Roundcube/rcube_imap.php9
-rw-r--r--program/lib/Roundcube/rcube_message.php14
-rw-r--r--program/lib/Roundcube/rcube_plugin.php2
-rw-r--r--program/lib/Roundcube/rcube_plugin_api.php5
-rw-r--r--program/lib/Roundcube/rcube_session.php45
-rw-r--r--program/lib/Roundcube/rcube_spellchecker.php8
-rw-r--r--program/lib/Roundcube/rcube_user.php34
-rw-r--r--program/lib/Roundcube/rcube_vcard.php2
-rw-r--r--program/localization/en_US/labels.inc3
-rw-r--r--program/localization/en_US/messages.inc1
-rw-r--r--program/steps/mail/compose.inc16
-rw-r--r--program/steps/mail/func.inc4
-rw-r--r--program/steps/mail/get.inc23
-rw-r--r--program/steps/mail/sendmail.inc1
-rw-r--r--program/steps/settings/edit_folder.inc4
-rw-r--r--program/steps/settings/folders.inc4
-rw-r--r--program/steps/settings/func.inc21
-rw-r--r--program/steps/settings/save_folder.inc6
-rw-r--r--program/steps/utils/error.inc1
l---------public_html/.htaccess1
-rw-r--r--public_html/index.php27
l---------public_html/plugins1
l---------public_html/program/js1
l---------public_html/program/resources1
l---------public_html/robots.txt1
l---------public_html/skins1
-rw-r--r--skins/classic/functions.js2
-rw-r--r--skins/classic/mail.css2
-rw-r--r--skins/larry/addressbook.css4
-rw-r--r--skins/larry/ie7hacks.css3
-rw-r--r--skins/larry/iehacks.css4
-rw-r--r--skins/larry/images/ajaxloader_dark.gifbin0 -> 1849 bytes
-rw-r--r--skins/larry/images/filetypes.pngbin8551 -> 9018 bytes
-rw-r--r--skins/larry/images/listicons.pngbin33661 -> 26546 bytes
-rw-r--r--skins/larry/images/messages_dark.pngbin0 -> 1779 bytes
-rw-r--r--skins/larry/includes/footer.html2
-rw-r--r--skins/larry/mail.css26
-rw-r--r--skins/larry/settings.css14
-rw-r--r--skins/larry/styles.css107
-rw-r--r--skins/larry/svggradients.css4
-rw-r--r--skins/larry/templates/addressbook.html1
-rw-r--r--skins/larry/templates/compose.html4
-rw-r--r--skins/larry/templates/folders.html1
-rw-r--r--skins/larry/templates/identities.html1
-rw-r--r--skins/larry/templates/importcontacts.html3
-rw-r--r--skins/larry/templates/login.html2
-rw-r--r--skins/larry/templates/mail.html4
-rw-r--r--skins/larry/templates/message.html2
-rw-r--r--skins/larry/templates/messageerror.html2
-rw-r--r--skins/larry/templates/messagepart.html1
-rw-r--r--skins/larry/templates/plugin.html1
-rw-r--r--skins/larry/templates/responses.html1
-rw-r--r--skins/larry/templates/settings.html1
-rw-r--r--skins/larry/ui.js37
-rw-r--r--temp/.gitignore1
-rw-r--r--temp/.htaccess2
-rw-r--r--tests/Framework/Browser.php33
83 files changed, 844 insertions, 363 deletions
diff --git a/.htaccess b/.htaccess
index dc6e62f38..43ab5b04f 100644
--- a/.htaccess
+++ b/.htaccess
@@ -26,12 +26,27 @@ php_value session.gc_probability 1
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^favicon\.ico$ skins/larry/images/favicon.ico
+
# security rules
-RewriteRule \.git - [F]
-RewriteRule ^/?(README(.md)?|INSTALL|LICENSE|CHANGELOG|UPGRADING)$ - [F]
-RewriteRule ^/?(SQL|bin) - [F]
+RewriteRule ^/?(\.git|SQL|bin|config|logs|temp|tests|program\/(include|lib|localization|steps)) - [F]
+RewriteRule /?(README(.md)?|composer\.json-dist|composer\.json|package\.xml)$ - [F]
</IfModule>
+# deny access to all files not containing a "." (dot)
+# to block access to different README, Changelog, INSTALL, etc.
+# files of various skins and plugins.
+<FilesMatch "^[^\.]+$">
+ # Apache 2.4
+ <IfModule mod_authz_core.c>
+ Require all denied
+ </IfModule>
+ # Apache 2.2
+ <IfModule !mod_authz_core.c>
+ Order Allow,Deny
+ Deny from all
+ </IfModule>
+</FilesMatch>
+
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
</IfModule>
diff --git a/CHANGELOG b/CHANGELOG
index 0ae6254b5..9f43e02c2 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,12 @@
CHANGELOG Roundcube Webmail
===========================
+- Larry skin: Improved status message display for better visibility (#1488974)
+- Fix Internet Explorer 11 detection (#1489434)
+- Fix date column width to fit the widest possible date format (#1489368)
+- Move certain user preference options to a collapsed "advanced" block (#1488829)
+- Add file type icons for Powerpoint and Open Office presentations (#1489225)
+- Fix operations on folders with trailing spaces in name (#1489419)
- Improve identity selection based on From: header (#1489378)
- Fix issue where mails with inline images of the same name contained only the first image multiple times (#1489406)
- Use left/right arrow keys to collapse/expand thread and spacebar to select a row, change Ctrl key behavior (#1489392)
@@ -88,7 +94,7 @@ RELEASE 0.9.5
-------------
- Fix failing vCard import when email address field contains spaces (#1489386)
- Fix default spell-check configuration after Google suspended their spell service
-- Fix vulnerability in handling _session argument of utils/save-prefs (#1489382)
+- Fix vulnerability in handling _session argument of utils/save-prefs [CVE-2013-6172] (#1489382)
- Fix iframe onload for upload errors handling (#1489379)
- Fix address matching in Return-Path header on identity selection (#1489374)
- Fix text wrapping issue with long unwrappable lines (#1489371)
diff --git a/config/.htaccess b/config/.htaccess
deleted file mode 100644
index 8e6a345dc..000000000
--- a/config/.htaccess
+++ /dev/null
@@ -1,2 +0,0 @@
-Order allow,deny
-Deny from all \ No newline at end of file
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
index ab0e188bd..b4c0206ca 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -252,6 +252,10 @@ $config['enable_installer'] = false;
// don't allow these settings to be overriden by the user
$config['dont_override'] = array();
+// define which settings should be listed under the 'advanced' block
+// which is hidden by default
+$config['advanced_prefs'] = array();
+
// provide an URL where a user can get support for this Roundcube installation
// PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE!
$config['support_url'] = '';
@@ -272,10 +276,10 @@ $config['user_aliases'] = false;
// use this folder to store log files (must be writeable for apache user)
// This is used by the 'file' log driver.
-$config['log_dir'] = 'logs/';
+$config['log_dir'] = RCUBE_INSTALL_PATH . 'logs/';
// use this folder to store temp files (must be writeable for apache user)
-$config['temp_dir'] = 'temp/';
+$config['temp_dir'] = RCUBE_INSTALL_PATH . 'temp/';
// expire files in temp_dir after 48 hours
// possible units: s, m, h, d, w
diff --git a/index.php b/index.php
index 1719abc48..03c3f4322 100644
--- a/index.php
+++ b/index.php
@@ -193,12 +193,6 @@ if (empty($RCMAIL->user->ID)) {
$session_error = true;
}
- if ($OUTPUT->ajax_call)
- $OUTPUT->redirect(array('_err' => 'session'), 2000);
-
- if (!empty($_REQUEST['_framed']))
- $OUTPUT->command('redirect', $RCMAIL->url(array('_err' => 'session')));
-
// check if installer is still active
if ($RCMAIL->config->get('enable_installer') && is_readable('./installer/index.php')) {
$OUTPUT->add_footer(html::div(array('style' => "background:#ef9398; border:2px solid #dc5757; padding:0.5em; margin:2em auto; width:50em"),
@@ -211,8 +205,14 @@ if (empty($RCMAIL->user->ID)) {
);
}
- if ($session_error || $_REQUEST['_err'] == 'session')
+ if ($session_error || $_REQUEST['_err'] == 'session') {
$OUTPUT->show_message('sessionerror', 'error', null, true, -1);
+ }
+
+ if ($OUTPUT->ajax_call || !empty($_REQUEST['_framed'])) {
+ $OUTPUT->command('session_error', $RCMAIL->url(array('_err' => 'session')));
+ $OUTPUT->send('iframe');
+ }
$plugin = $RCMAIL->plugins->exec_hook('unauthenticated', array('task' => 'login', 'error' => $session_error));
diff --git a/logs/.gitignore b/logs/.gitignore
new file mode 100644
index 000000000..f59ec20aa
--- /dev/null
+++ b/logs/.gitignore
@@ -0,0 +1 @@
+* \ No newline at end of file
diff --git a/logs/.htaccess b/logs/.htaccess
deleted file mode 100644
index 8e6a345dc..000000000
--- a/logs/.htaccess
+++ /dev/null
@@ -1,2 +0,0 @@
-Order allow,deny
-Deny from all \ No newline at end of file
diff --git a/plugins/archive/archive.js b/plugins/archive/archive.js
index 2e5267425..6ed4f971a 100644
--- a/plugins/archive/archive.js
+++ b/plugins/archive/archive.js
@@ -8,7 +8,7 @@ function rcmail_archive(prop)
if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length))
return;
- if (rcmail_is_archive()) {
+ if (!rcmail_is_archive()) {
if (!rcmail.env.archive_type) {
// simply move to archive folder (if no partition type is set)
rcmail.command('move', rcmail.env.archive_folder);
@@ -22,9 +22,6 @@ function rcmail_archive(prop)
function rcmail_is_archive()
{
- if (!rcmail.env.archive_folder)
- return false;
-
// check if current folder is an archive folder or one of its children
if (rcmail.env.mailbox == rcmail.env.archive_folder
|| rcmail.env.mailbox.startsWith(rcmail.env.archive_folder + rcmail.env.delimiter)
@@ -37,12 +34,12 @@ function rcmail_is_archive()
if (window.rcmail) {
rcmail.addEventListener('init', function(evt) {
// register command (directly enable in message view mode)
- rcmail.register_command('plugin.archive', rcmail_archive, rcmail.env.uid && rcmail_is_archive());
+ rcmail.register_command('plugin.archive', rcmail_archive, rcmail.env.uid && !rcmail_is_archive());
// add event-listener to message list
if (rcmail.message_list)
rcmail.message_list.addEventListener('select', function(list) {
- rcmail.enable_command('plugin.archive', list.get_selection().length > 0 && rcmail_is_archive());
+ rcmail.enable_command('plugin.archive', list.get_selection().length > 0 && !rcmail_is_archive());
});
// set css style for archive folder
diff --git a/plugins/help/skins/larry/templates/help.html b/plugins/help/skins/larry/templates/help.html
index bfd3f1141..f1d1f232e 100644
--- a/plugins/help/skins/larry/templates/help.html
+++ b/plugins/help/skins/larry/templates/help.html
@@ -21,7 +21,6 @@
<div class="iframebox help_<roundcube:var name='env:action' />">
<roundcube:object name="helpcontent" id="helpcontentframe" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/plugins/http_authentication/http_authentication.php b/plugins/http_authentication/http_authentication.php
index d86e1791c..83f29c84f 100644
--- a/plugins/http_authentication/http_authentication.php
+++ b/plugins/http_authentication/http_authentication.php
@@ -29,7 +29,7 @@ class http_authentication extends rcube_plugin
function startup($args)
{
- if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
+ if (!empty($_SERVER['PHP_AUTH_USER'])) {
$rcmail = rcmail::get_instance();
$rcmail->add_shutdown_function(array('http_authentication', 'shutdown'));
@@ -38,7 +38,8 @@ class http_authentication extends rcube_plugin
$args['action'] = 'login';
}
// Set user password in session (see shutdown() method for more info)
- else if (!empty($_SESSION['user_id']) && empty($_SESSION['password'])) {
+ else if (!empty($_SESSION['user_id']) && empty($_SESSION['password'])
+ && !empty($_SERVER['PHP_AUTH_PW'])) {
$_SESSION['password'] = $rcmail->encrypt($_SERVER['PHP_AUTH_PW']);
}
}
@@ -61,9 +62,10 @@ class http_authentication extends rcube_plugin
return $args;
}
- if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
+ if (!empty($_SERVER['PHP_AUTH_USER'])) {
$args['user'] = $_SERVER['PHP_AUTH_USER'];
- $args['pass'] = $_SERVER['PHP_AUTH_PW'];
+ if (!empty($_SERVER['PHP_AUTH_PW']))
+ $args['pass'] = $_SERVER['PHP_AUTH_PW'];
}
$args['cookiecheck'] = false;
diff --git a/plugins/managesieve/skins/larry/templates/managesieve.html b/plugins/managesieve/skins/larry/templates/managesieve.html
index 25bbbaf69..4fa5e2a84 100644
--- a/plugins/managesieve/skins/larry/templates/managesieve.html
+++ b/plugins/managesieve/skins/larry/templates/managesieve.html
@@ -40,7 +40,6 @@
<div class="iframebox">
<roundcube:object name="filterframe" id="filter-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/plugins/newmail_notifier/newmail_notifier.js b/plugins/newmail_notifier/newmail_notifier.js
index 2beacf650..69f1867c9 100644
--- a/plugins/newmail_notifier/newmail_notifier.js
+++ b/plugins/newmail_notifier/newmail_notifier.js
@@ -108,7 +108,7 @@ function newmail_notifier_desktop(body)
lang: "",
body: body,
tag: "newmail_notifier",
- icon: "plugins/newmail_notifier/mail.png",
+ icon: "plugins/newmail_notifier/mail.png"
});
popup.onclick = function() {
this.close();
diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php
index cf62debcf..739958ad7 100644
--- a/plugins/password/drivers/ldap.php
+++ b/plugins/password/drivers/ldap.php
@@ -146,7 +146,7 @@ class rcube_ldap_password
return '';
}
- $base = $rcmail->config->get('password_ldap_search_base');
+ $base = self::substitute_vars($rcmail->config->get('password_ldap_search_base'));
$filter = self::substitute_vars($rcmail->config->get('password_ldap_search_filter'));
$options = array (
'scope' => 'sub',
diff --git a/plugins/password/drivers/ldap_simple.php b/plugins/password/drivers/ldap_simple.php
index c5d828fab..47e3b07de 100644
--- a/plugins/password/drivers/ldap_simple.php
+++ b/plugins/password/drivers/ldap_simple.php
@@ -187,6 +187,8 @@ class rcube_ldap_simple_password
$search_base = $rcmail->config->get('password_ldap_search_base');
$search_filter = $rcmail->config->get('password_ldap_search_filter');
+
+ $search_base = rcube_ldap_password::substitute_vars($search_base);
$search_filter = rcube_ldap_password::substitute_vars($search_filter);
$this->_debug("C: Search $search_base for $search_filter");
diff --git a/program/.htaccess b/program/.htaccess
deleted file mode 100644
index be9e7e25a..000000000
--- a/program/.htaccess
+++ /dev/null
@@ -1,4 +0,0 @@
-<IfModule mod_rewrite.c>
-RewriteEngine On
-RewriteRule !^(js|resources) - [F]
-</IfModule>
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 4b3f13760..8abe87303 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -413,6 +413,9 @@ class rcmail extends rcube
$this->output->set_env('comm_path', $this->comm_path);
$this->output->set_charset(RCUBE_CHARSET);
+ if ($this->user && $this->user->ID)
+ $this->output->set_env('user_id', $this->user->get_hash());
+
// add some basic labels to client
$this->output->add_label('loading', 'servererror', 'requesttimedout', 'refreshing');
diff --git a/program/include/rcmail_output_html.php b/program/include/rcmail_output_html.php
index 8a960673d..6db826e2e 100644
--- a/program/include/rcmail_output_html.php
+++ b/program/include/rcmail_output_html.php
@@ -310,12 +310,14 @@ class rcmail_output_html extends rcmail_output
*/
public function reset($all = false)
{
+ $framed = $this->framed;
$env = $all ? null : array_intersect_key($this->env, array('extwin'=>1, 'framed'=>1));
parent::reset();
// let some env variables survive
$this->env = $this->js_env = $env;
+ $this->framed = $framed || $this->env['framed'];
$this->js_labels = array();
$this->js_commands = array();
$this->script_files = array();
@@ -323,6 +325,11 @@ class rcmail_output_html extends rcmail_output
$this->header = '';
$this->footer = '';
$this->body = '';
+
+ // load defaults
+ if (!$all) {
+ $this->__construct();
+ }
}
/**
@@ -1203,8 +1210,6 @@ class rcmail_output_html extends rcmail_output
*/
public function include_script($file, $position='head')
{
- static $sa_files = array();
-
if (!preg_match('|^https?://|i', $file) && $file[0] != '/') {
$file = $this->scripts_path . $file;
if ($fs = @filemtime($file)) {
@@ -1212,17 +1217,13 @@ class rcmail_output_html extends rcmail_output
}
}
- if (in_array($file, $sa_files)) {
- return;
- }
-
- $sa_files[] = $file;
-
if (!is_array($this->script_files[$position])) {
$this->script_files[$position] = array();
}
- $this->script_files[$position][] = $file;
+ if (!in_array($file, $this->script_files[$position])) {
+ $this->script_files[$position][] = $file;
+ }
}
/**
diff --git a/program/js/app.js b/program/js/app.js
index 1f75e219c..636db82d4 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -34,7 +34,7 @@ function rcube_webmail()
// webmail client settings
this.dblclick_time = 500;
- this.message_time = 4000;
+ this.message_time = 5000;
this.identifier_expr = new RegExp('[^0-9a-z\-_]', 'gi');
// environment defaults
@@ -187,6 +187,8 @@ function rcube_webmail()
if (this.env.permaurl)
this.enable_command('permaurl', 'extwin', true);
+ this.local_storage_prefix = 'roundcube.' + (this.env.user_id || 'anonymous') + '.';
+
switch (this.task) {
case 'mail':
@@ -218,12 +220,8 @@ function rcube_webmail()
// load messages
this.command('list');
- }
- if (this.gui_objects.qsearchbox) {
- if (this.env.search_text != null)
- this.gui_objects.qsearchbox.value = this.env.search_text;
- $(this.gui_objects.qsearchbox).focusin(function() { rcmail.message_list && rcmail.message_list.blur(); });
+ $(this.gui_objects.qsearchbox).val(this.env.search_text).focusin(function() { rcmail.message_list.blur(); });
}
this.set_button_titles();
@@ -285,10 +283,10 @@ function rcube_webmail()
return rcube_event.cancel(e);
});
- // avoid textarea loosing focus when hitting the save-response button/link
- for (var i=0; this.buttons['save-response'] && i < this.buttons['save-response'].length; i++) {
- $('#'+this.buttons['save-response'][i].id).mousedown(function(e){ return rcube_event.cancel(e); })
- }
+ // avoid textarea loosing focus when hitting the save-response button/link
+ for (var i=0; this.buttons['save-response'] && i < this.buttons['save-response'].length; i++) {
+ $('#'+this.buttons['save-response'][i].id).mousedown(function(e){ return rcube_event.cancel(e); })
+ }
}
document.onmouseup = function(e){ return p.doc_mouse_up(e); };
@@ -351,7 +349,7 @@ function rcube_webmail()
this.env.contactfolders = $.extend($.extend({}, this.env.address_sources), this.env.contactgroups);
this.enable_command('add', 'import', this.env.writable_source);
- this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'advanced-search', true);
+ this.enable_command('list', 'listgroup', 'pushgroup', 'popgroup', 'listsearch', 'search', 'reset-search', 'advanced-search', true);
if (this.gui_objects.contactslist) {
this.contact_list = new rcube_list_widget(this.gui_objects.contactslist,
@@ -369,8 +367,8 @@ function rcube_webmail()
this.gui_objects.contactslist.parentNode.onmousedown = function(e){ return p.click_on_list(e); };
document.onmouseup = function(e){ return p.doc_mouse_up(e); };
- if (this.gui_objects.qsearchbox)
- $(this.gui_objects.qsearchbox).focusin(function() { rcmail.contact_list.blur(); });
+
+ $(this.gui_objects.qsearchbox).focusin(function() { rcmail.contact_list.blur(); });
this.update_group_commands();
this.command('list');
@@ -394,9 +392,6 @@ function rcube_webmail()
this.init_contact_form();
}
- if (this.gui_objects.qsearchbox)
- this.enable_command('search', 'reset-search', true);
-
break;
case 'settings':
@@ -408,9 +403,6 @@ function rcube_webmail()
else if (this.env.action == 'edit-identity' || this.env.action == 'add-identity') {
this.enable_command('save', 'edit', 'toggle-editor', true);
this.enable_command('delete', this.env.identities_level < 2);
-
- if (this.env.action == 'add-identity')
- $("input[type='text']").first().select();
}
else if (this.env.action == 'folders') {
this.enable_command('subscribe', 'unsubscribe', 'create-folder', 'rename-folder', true);
@@ -419,7 +411,6 @@ function rcube_webmail()
this.enable_command('save', 'folder-size', true);
parent.rcmail.env.exists = this.env.messagecount;
parent.rcmail.enable_command('purge', this.env.messagecount);
- $("input[type='text']").first().select();
}
else if (this.env.action == 'responses') {
this.enable_command('add', true);
@@ -445,7 +436,7 @@ function rcube_webmail()
}
else if (this.gui_objects.responseslist) {
this.responses_list = new rcube_list_widget(this.gui_objects.responseslist, {multiselect:false, draggable:false, keyboard:false});
- this.responses_list.addEventListener('select', function(list){
+ this.responses_list.addEventListener('select', function(list) {
var win, id = list.get_single_selection();
p.enable_command('delete', !!id && $.inArray(id, p.env.readonly_responses) < 0);
if (id && (win = p.get_frame_window(p.env.contentframe))) {
@@ -489,6 +480,11 @@ function rcube_webmail()
break;
}
+ // select first input field in an edit form
+ if (this.gui_objects.editform)
+ $("input,select,textarea", this.gui_objects.editform)
+ .not(':hidden').not(':disabled').first().select();
+
// unset contentframe variable if preview_pane is enabled
if (this.env.contentframe && !$('#' + this.env.contentframe).is(':visible'))
this.env.contentframe = null;
@@ -584,9 +580,12 @@ function rcube_webmail()
}
// check input before leaving compose step
- if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands)<0) {
+ if (this.task == 'mail' && this.env.action == 'compose' && $.inArray(command, this.env.compose_commands) < 0 && !this.env.server_error) {
if (this.cmp_hash != this.compose_field_hash() && !confirm(this.get_label('notsentwarning')))
return false;
+
+ // remove copy from local storage if compose screen is left intentionally
+ this.remove_compose_data(this.env.compose_id);
}
// process external commands
@@ -621,10 +620,10 @@ function rcube_webmail()
break;
// commands to switch task
+ case 'logout':
case 'mail':
case 'addressbook':
case 'settings':
- case 'logout':
this.switch_task(command);
break;
@@ -644,6 +643,7 @@ function rcube_webmail()
var form = this.gui_objects.messageform,
win = this.open_window('');
+ this.save_compose_form_local();
$("input[name='_action']", form).val('compose');
form.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 });
form.target = win.name;
@@ -1298,8 +1298,10 @@ function rcube_webmail()
return;
var url = this.get_task_url(task);
- if (task=='mail')
+ if (task == 'mail')
url += '&_mbox=INBOX';
+ else if (task == 'logout')
+ this.clear_compose_data();
this.redirect(url);
};
@@ -1706,7 +1708,7 @@ function rcube_webmail()
url += (url.match(/\?/) ? '&' : '?') + '_extwin=1';
if (this.env.standard_windows)
- extwin = window.open(url, wname);
+ var extwin = window.open(url, wname);
else {
var win = this.is_framed() ? parent.window : window,
page = $(win),
@@ -1727,7 +1729,7 @@ function rcube_webmail()
}
// focus window, delayed to bring to front
- window.setTimeout(function() { extwin.focus(); }, 10);
+ window.setTimeout(function() { extwin && extwin.focus(); }, 10);
return extwin;
};
@@ -3123,6 +3125,60 @@ function rcube_webmail()
}
}
+ // check for locally stored compose data
+ if (window.localStorage) {
+ var index = this.local_storage_get_item('compose.index', []);
+
+ for (var key, i = 0; i < index.length; i++) {
+ key = index[i], formdata = this.local_storage_get_item('compose.' + key, null, true);
+ if (!formdata) {
+ continue;
+ }
+ // restore saved copy of current compose_id
+ if (formdata.changed && key == this.env.compose_id) {
+ this.restore_compose_form(key, html_mode);
+ break;
+ }
+ // skip records from 'other' drafts
+ if (this.env.draft_id && formdata.draft_id && formdata.draft_id != this.env.draft_id) {
+ continue;
+ }
+ // show dialog asking to restore the message
+ if (formdata.changed && formdata.session != this.env.session_id) {
+ this.show_popup_dialog(
+ this.get_label('restoresavedcomposedata')
+ .replace('$date', new Date(formdata.changed).toLocaleString())
+ .replace('$subject', formdata._subject)
+ .replace(/\n/g, '<br/>'),
+ this.get_label('restoremessage'),
+ [{
+ text: this.get_label('restore'),
+ click: function(){
+ ref.restore_compose_form(key, html_mode);
+ ref.remove_compose_data(key); // remove old copy
+ ref.save_compose_form_local(); // save under current compose_id
+ $(this).dialog('close');
+ }
+ },
+ {
+ text: this.get_label('delete'),
+ click: function(){
+ ref.remove_compose_data(key);
+ $(this).dialog('close');
+ }
+ },
+ {
+ text: this.get_label('ignore'),
+ click: function(){
+ $(this).dialog('close');
+ }
+ }]
+ );
+ break;
+ }
+ }
+ }
+
if (input_to.val() == '')
input_to.focus();
else if (input_subject.val() == '')
@@ -3560,6 +3616,8 @@ function rcube_webmail()
this.env.draft_id = id;
$("input[name='_draft_saveid']").val(id);
+
+ this.remove_compose_data(this.env.compose_id);
};
this.auto_save_start = function()
@@ -3567,6 +3625,20 @@ function rcube_webmail()
if (this.env.draft_autosave)
this.save_timer = setTimeout(function(){ ref.command("savedraft"); }, this.env.draft_autosave * 1000);
+ // save compose form content to local storage every 5 seconds
+ if (!this.local_save_timer && window.localStorage) {
+ // track typing activity and only save on changes
+ this.compose_type_activity = this.compose_type_activity_last = 0;
+ $(document).bind('keypress', function(e){ ref.compose_type_activity++; });
+
+ this.local_save_timer = setInterval(function(){
+ if (ref.compose_type_activity > ref.compose_type_activity_last) {
+ ref.save_compose_form_local();
+ ref.compose_type_activity_last = ref.compose_type_activity;
+ }
+ }, 5000);
+ }
+
// Unlock interface now that saving is complete
this.busy = false;
};
@@ -3595,6 +3667,115 @@ function rcube_webmail()
return str;
};
+ // store the contents of the compose form to localstorage
+ this.save_compose_form_local = function()
+ {
+ var formdata = { session:this.env.session_id, changed:new Date().getTime() },
+ ed, empty = true;
+
+ // get fresh content from editor
+ if (window.tinyMCE && (ed = tinyMCE.get(this.env.composebody))) {
+ tinyMCE.triggerSave();
+ }
+
+ if (this.env.draft_id) {
+ formdata.draft_id = this.env.draft_id;
+ }
+
+ $('input, select, textarea', this.gui_objects.messageform).each(function(i, elem) {
+ switch (elem.tagName.toLowerCase()) {
+ case 'input':
+ if (elem.type == 'button' || elem.type == 'submit' || (elem.type == 'hidden' && elem.name != '_is_html')) {
+ break;
+ }
+ formdata[elem.name] = elem.type != 'checkbox' || elem.checked ? $(elem).val() : '';
+
+ if (formdata[elem.name] != '' && elem.type != 'hidden')
+ empty = false;
+ break;
+
+ case 'select':
+ formdata[elem.name] = $('option:checked', elem).val();
+ break;
+
+ default:
+ formdata[elem.name] = $(elem).val();
+ if (formdata[elem.name] != '')
+ empty = false;
+ }
+ });
+
+ if (window.localStorage && !empty) {
+ var index = this.local_storage_get_item('compose.index', []),
+ key = this.env.compose_id;
+
+ if ($.inArray(key, index) < 0) {
+ index.push(key);
+ }
+ this.local_storage_set_item('compose.' + key, formdata, true);
+ this.local_storage_set_item('compose.index', index);
+ }
+ };
+
+ // write stored compose data back to form
+ this.restore_compose_form = function(key, html_mode)
+ {
+ var ed, formdata = this.local_storage_get_item('compose.' + key, true);
+
+ if (formdata && typeof formdata == 'object') {
+ $.each(formdata, function(k, value) {
+ if (k[0] == '_') {
+ var elem = $("*[name='"+k+"']");
+ if (elem[0] && elem[0].type == 'checkbox') {
+ elem.prop('checked', value != '');
+ }
+ else {
+ elem.val(value);
+ }
+ }
+ });
+
+ // initialize HTML editor
+ if (formdata._is_html == '1') {
+ if (!html_mode) {
+ tinyMCE.execCommand('mceAddControl', false, this.env.composebody);
+ this.triggerEvent('aftertoggle-editor', { mode:'html' });
+ }
+ }
+ else if (html_mode) {
+ tinyMCE.execCommand('mceRemoveControl', false, this.env.composebody);
+ this.triggerEvent('aftertoggle-editor', { mode:'plain' });
+ }
+ }
+ };
+
+ // remove stored compose data from localStorage
+ this.remove_compose_data = function(key)
+ {
+ if (window.localStorage) {
+ var index = this.local_storage_get_item('compose.index', []);
+
+ if ($.inArray(key, index) >= 0) {
+ this.local_storage_remove_item('compose.' + key);
+ this.local_storage_set_item('compose.index', $.grep(index, function(val,i) { return val != key; }));
+ }
+ }
+ };
+
+ // clear all stored compose data of this user
+ this.clear_compose_data = function()
+ {
+ if (window.localStorage) {
+ var index = this.local_storage_get_item('compose.index', []);
+
+ for (var i=0; i < index.length; i++) {
+ this.local_storage_remove_item('compose.' + index[i]);
+ }
+ this.local_storage_remove_item('compose.index');
+ }
+ }
+
+
this.change_identity = function(obj, show_sig)
{
if (!obj || !obj.options)
@@ -4027,7 +4208,7 @@ function rcube_webmail()
case 38: // arrow up
case 40: // arrow down
if (!this.ksearch_visible())
- break;
+ return;
var dir = key==38 ? 1 : 0;
@@ -4062,8 +4243,7 @@ function rcube_webmail()
case 37: // left
case 39: // right
- if (mod != SHIFT_KEY)
- return;
+ return;
}
// start timer
@@ -4126,8 +4306,10 @@ function rcube_webmail()
if (this.ksearch_input.setSelectionRange)
this.ksearch_input.setSelectionRange(cpos, cpos);
- if (trigger)
+ if (trigger) {
this.triggerEvent('autocomplete_insert', { field:this.ksearch_input, insert:insert });
+ this.compose_type_activity++;
+ }
};
this.replace_group_recipients = function(id, recipients)
@@ -4136,6 +4318,7 @@ function rcube_webmail()
this.group2expand[id].input.value = this.group2expand[id].input.value.replace(this.group2expand[id].name, recipients);
this.triggerEvent('autocomplete_insert', { field:this.group2expand[id].input, insert:recipients });
this.group2expand[id] = null;
+ this.compose_type_activity++;
}
};
@@ -4820,8 +5003,6 @@ function rcube_webmail()
$('input.datepicker').datepicker();
}
- $("input[type='text']:visible").first().focus();
-
// Submit search form on Enter
if (this.env.action == 'search')
$(this.gui_objects.editform).append($('<input type="submit">').hide())
@@ -5438,7 +5619,10 @@ function rcube_webmail()
this.init_subscription_list = function()
{
- var p = this;
+ var p = this, delim = RegExp.escape(this.env.delimiter);
+
+ this.last_sub_rx = RegExp('['+delim+']?[^'+delim+']+$');
+
this.subscription_list = new rcube_list_widget(this.gui_objects.subscriptionlist,
{multiselect:false, draggable:true, keyboard:false, toggleselect:true});
this.subscription_list.addEventListener('select', function(o){ p.subscription_select(o); });
@@ -5449,6 +5633,7 @@ function rcube_webmail()
row.obj.onmouseout = function() { p.unfocus_subscription(row.id); };
};
this.subscription_list.init();
+
$('#mailboxroot')
.mouseover(function(){ p.focus_subscription(this.id); })
.mouseout(function(){ p.unfocus_subscription(this.id); })
@@ -5456,9 +5641,7 @@ function rcube_webmail()
this.focus_subscription = function(id)
{
- var row, folder,
- delim = RegExp.escape(this.env.delimiter),
- reg = RegExp('['+delim+']?[^'+delim+']+$');
+ var row, folder;
if (this.drag_active && this.env.mailbox && (row = document.getElementById(id)))
if (this.env.subscriptionrows[id] &&
@@ -5466,8 +5649,8 @@ function rcube_webmail()
) {
if (this.check_droptarget(folder) &&
!this.env.subscriptionrows[this.get_folder_row_id(this.env.mailbox)][2] &&
- (folder != this.env.mailbox.replace(reg, '')) &&
- (!folder.startsWith(this.env.mailbox + this.env.delimiter))
+ folder != this.env.mailbox.replace(this.last_sub_rx, '') &&
+ !folder.startsWith(this.env.mailbox + this.env.delimiter)
) {
this.env.dstfolder = folder;
$(row).addClass('droptarget');
@@ -5480,7 +5663,8 @@ function rcube_webmail()
var row = $('#'+id);
this.env.dstfolder = null;
- if (this.env.subscriptionrows[id] && row[0])
+
+ if (this.env.subscriptionrows[id] && row.length)
row.removeClass('droptarget');
else
$(this.subscription_list.frame).removeClass('droptarget');
@@ -5506,21 +5690,20 @@ function rcube_webmail()
this.subscription_move_folder = function(list)
{
- var delim = RegExp.escape(this.env.delimiter),
- reg = RegExp('['+delim+']?[^'+delim+']+$');
-
- if (this.env.mailbox && this.env.dstfolder !== null && (this.env.dstfolder != this.env.mailbox) &&
- (this.env.dstfolder != this.env.mailbox.replace(reg, ''))
+ if (this.env.mailbox && this.env.dstfolder !== null &&
+ this.env.dstfolder != this.env.mailbox &&
+ this.env.dstfolder != this.env.mailbox.replace(this.last_sub_rx, '')
) {
- reg = new RegExp('[^'+delim+']*['+delim+']', 'g');
- var basename = this.env.mailbox.replace(reg, ''),
- newname = this.env.dstfolder === '' ? basename : this.env.dstfolder+this.env.delimiter+basename;
+ var path = this.env.mailbox.split(this.env.delimiter),
+ basename = path.pop(),
+ newname = this.env.dstfolder === '' ? basename : this.env.dstfolder + this.env.delimiter + basename;
if (newname != this.env.mailbox) {
this.http_post('rename-folder', {_folder_oldname: this.env.mailbox, _folder_newname: newname}, this.set_busy(true, 'foldermoving'));
this.subscription_list.draglayer.hide();
}
}
+
this.drag_active = false;
this.unfocus_subscription(this.get_folder_row_id(this.env.dstfolder));
};
@@ -5549,7 +5732,7 @@ function rcube_webmail()
if (!this.gui_objects.subscriptionlist)
return false;
- var row, n, i, tmp, tmp_name, folders, rowid, list = [], slist = [],
+ var row, n, i, tmp, tmp_name, rowid, folders = [], list = [], slist = [],
tbody = this.gui_objects.subscriptionlist.tBodies[0],
refrow = $('tr', tbody).get(1),
id = 'rcmrow'+((new Date).getTime());
@@ -5564,8 +5747,7 @@ function rcube_webmail()
row = $(refrow).clone(true);
// set ID, reset css class
- row.attr('id', id);
- row.attr('class', class_name);
+ row.attr({id: id, 'class': class_name});
// set folder name
row.find('td:first').html(display_name);
@@ -5577,10 +5759,21 @@ function rcube_webmail()
// add to folder/row-ID map
this.env.subscriptionrows[id] = [name, display_name, 0];
- // sort folders, to find a place where to insert the row
- folders = [];
- $.each(this.env.subscriptionrows, function(k,v){ folders.push(v) });
- folders.sort(function(a,b){ return a[0] < b[0] ? -1 : (a[0] > b[0] ? 1 : 0) });
+ // sort folders (to find a place where to insert the row)
+ // replace delimiter with \0 character to fix sorting
+ // issue where 'Abc Abc' would be placed before 'Abc/def'
+ var replace_from = RegExp(RegExp.escape(this.env.delimiter), 'g'),
+ replace_to = String.fromCharCode(0);
+ $.each(this.env.subscriptionrows, function(k,v) {
+ var n = v[0];
+ n = n.replace(replace_from, replace_to);
+ v.push(n);
+ folders.push(v);
+ });
+ folders.sort(function(a, b) {
+ var len = a.length - 1; n1 = a[len], n2 = b[len];
+ return n1 < n2 ? -1 : 1;
+ });
for (n in folders) {
// protected folder
@@ -5642,7 +5835,7 @@ function rcube_webmail()
tbody = this.gui_objects.subscriptionlist.tBodies[0],
folders = this.env.subscriptionrows,
id = this.get_folder_row_id(oldfolder),
- regex = new RegExp('^'+RegExp.escape(oldfolder)),
+ prefix_len = oldfolder.length,
subscribed = $('input[name="_subscribed[]"]', $('#'+id)).prop('checked'),
// find subfolders of renamed folder
list = this.get_subfolders(oldfolder);
@@ -5667,7 +5860,7 @@ function rcube_webmail()
row.after(tmprow);
row = tmprow;
// update folder index
- name = name.replace(regex, newfolder);
+ name = newfolder + name.slice(prefix_len);
$('input[name="_subscribed[]"]', row).val(name);
this.env.subscriptionrows[id][0] = name;
// update the name if level is changed
@@ -5912,59 +6105,36 @@ function rcube_webmail()
// mouse over button
this.button_over = function(command, id)
{
- var n, button, obj, a_buttons = this.buttons[command],
- len = a_buttons ? a_buttons.length : 0;
-
- for (n=0; n<len; n++) {
- button = a_buttons[n];
- if (button.id == id && button.status == 'act') {
- obj = document.getElementById(button.id);
- if (obj && button.over) {
- if (button.type == 'image')
- obj.src = button.over;
- else
- obj.className = button.over;
- }
- }
- }
+ this.button_event(command, id, 'over');
};
// mouse down on button
this.button_sel = function(command, id)
{
- var n, button, obj, a_buttons = this.buttons[command],
- len = a_buttons ? a_buttons.length : 0;
-
- for (n=0; n<len; n++) {
- button = a_buttons[n];
- if (button.id == id && button.status == 'act') {
- obj = document.getElementById(button.id);
- if (obj && button.sel) {
- if (button.type == 'image')
- obj.src = button.sel;
- else
- obj.className = button.sel;
- }
- this.buttons_sel[id] = command;
- }
- }
+ this.button_event(command, id, 'sel');
};
// mouse out of button
this.button_out = function(command, id)
{
+ this.button_event(command, id, 'act');
+ };
+
+ // event of button
+ this.button_event = function(command, id, event)
+ {
var n, button, obj, a_buttons = this.buttons[command],
len = a_buttons ? a_buttons.length : 0;
for (n=0; n<len; n++) {
button = a_buttons[n];
if (button.id == id && button.status == 'act') {
- obj = document.getElementById(button.id);
- if (obj && button.act) {
- if (button.type == 'image')
- obj.src = button.act;
- else
- obj.className = button.act;
+ if (button[event] && (obj = document.getElementById(button.id))) {
+ obj[button.type == 'image' ? 'src' : 'className'] = button[event];
+ }
+
+ if (event == 'sel') {
+ this.buttons_sel[id] = command;
}
}
}
@@ -6039,7 +6209,7 @@ function rcube_webmail()
this.triggerEvent('message', { message:msg, type:type, timeout:timeout, object:obj });
if (timeout > 0)
- setTimeout(function() { ref.hide_message(id, type == 'loading'); }, timeout);
+ setTimeout(function() { ref.hide_message(id, type != 'loading'); }, timeout);
return id;
};
@@ -6152,8 +6322,8 @@ function rcube_webmail()
// enable/disable buttons for page shifting
this.set_page_buttons = function()
{
- this.enable_command('nextpage', 'lastpage', (this.env.pagecount > this.env.current_page));
- this.enable_command('previouspage', 'firstpage', (this.env.current_page > 1));
+ this.enable_command('nextpage', 'lastpage', this.env.pagecount > this.env.current_page);
+ this.enable_command('previouspage', 'firstpage', this.env.current_page > 1);
};
// mark a mailbox as selected and set environment variable
@@ -6163,14 +6333,10 @@ function rcube_webmail()
this.treelist.select(name);
}
else if (this.gui_objects.folderlist) {
- var current_li, target_li;
-
- if ((current_li = $('li.selected', this.gui_objects.folderlist))) {
- current_li.removeClass('selected').addClass('unfocused');
- }
- if ((target_li = this.get_folder_li(name, prefix, encode))) {
- $(target_li).removeClass('unfocused').addClass('selected');
- }
+ $('li.selected', this.gui_objects.folderlist)
+ .removeClass('selected').addClass('unfocused');
+ $(this.get_folder_li(name, prefix, encode))
+ .removeClass('unfocused').addClass('selected');
// trigger event hook
this.triggerEvent('selectfolder', { folder:name, prefix:prefix });
@@ -6199,8 +6365,6 @@ function rcube_webmail()
name = this.html_identifier(name, encode);
return document.getElementById(prefix+name);
}
-
- return null;
};
// for reordering column array (Konqueror workaround)
@@ -6744,6 +6908,20 @@ function rcube_webmail()
setTimeout(function(){ ref.keep_alive(); ref.start_keepalive(); }, 30000);
};
+ // handler for session errors detected on the server
+ this.session_error = function(redirect_url)
+ {
+ this.env.server_error = 401;
+
+ // save message in local storage and do not redirect
+ if (this.env.action == 'compose') {
+ this.save_compose_form_local();
+ }
+ else if (redirect_url) {
+ window.setTimeout(function(){ ref.redirect(redirect_url, true); }, 2000);
+ }
+ };
+
// callback when an iframe finished loading
this.iframe_loaded = function(unlock)
{
@@ -7265,7 +7443,28 @@ function rcube_webmail()
this.set_cookie = function(name, value, expires)
{
setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure);
- }
+ };
+
+ // wrapper for localStorage.getItem(key)
+ this.local_storage_get_item = function(key, deflt, encrypted)
+ {
+ // TODO: add encryption
+ var item = localStorage.getItem(this.local_storage_prefix + key);
+ return item !== null ? JSON.parse(item) : (deflt || null);
+ };
+
+ // wrapper for localStorage.setItem(key, data)
+ this.local_storage_set_item = function(key, data, encrypted)
+ {
+ // TODO: add encryption
+ return localStorage.setItem(this.local_storage_prefix + key, JSON.stringify(data));
+ };
+
+ // wrapper for localStorage.removeItem(key)
+ this.local_storage_remove_item = function(key)
+ {
+ return localStorage.removeItem(this.local_storage_prefix + key);
+ };
} // end object rcube_webmail
diff --git a/program/js/common.js b/program/js/common.js
index 02934f734..14091463f 100644
--- a/program/js/common.js
+++ b/program/js/common.js
@@ -37,47 +37,44 @@ function roundcube_browser()
this.vendver = n.vendorSub ? parseFloat(n.vendorSub) : 0;
this.product = n.product ? n.product : '';
this.platform = String(n.platform).toLowerCase();
- this.lang = (n.language) ? n.language.substring(0,2) :
- (n.browserLanguage) ? n.browserLanguage.substring(0,2) :
- (n.systemLanguage) ? n.systemLanguage.substring(0,2) : 'en';
+ this.lang = n.language ? n.language.substring(0,2) :
+ n.browserLanguage ? n.browserLanguage.substring(0,2) :
+ n.systemLanguage ? n.systemLanguage.substring(0,2) : 'en';
- this.win = (this.platform.indexOf('win') >= 0);
- this.mac = (this.platform.indexOf('mac') >= 0);
- this.linux = (this.platform.indexOf('linux') >= 0);
- this.unix = (this.platform.indexOf('unix') >= 0);
+ this.win = this.platform.indexOf('win') >= 0;
+ this.mac = this.platform.indexOf('mac') >= 0;
+ this.linux = this.platform.indexOf('linux') >= 0;
+ this.unix = this.platform.indexOf('unix') >= 0;
this.dom = document.getElementById ? true : false;
- this.dom2 = (document.addEventListener && document.removeEventListener);
-
- this.ie = (document.all && !window.opera);
- this.ie4 = (this.ie && !this.dom);
- this.ie5 = (this.dom && this.appver.indexOf('MSIE 5')>0);
- this.ie8 = (this.dom && this.appver.indexOf('MSIE 8')>0);
- this.ie9 = (this.dom && this.appver.indexOf('MSIE 9')>0);
- this.ie7 = (this.dom && this.appver.indexOf('MSIE 7')>0);
- this.ie6 = (this.dom && !this.ie8 && !this.ie7 && this.appver.indexOf('MSIE 6')>0);
-
- this.ns = ((this.ver < 5 && this.name == 'Netscape') || (this.ver >= 5 && this.vendor.indexOf('Netscape') >= 0));
- this.chrome = (this.agent_lc.indexOf('chrome') > 0);
- this.safari = (!this.chrome && (this.agent_lc.indexOf('safari') > 0 || this.agent_lc.indexOf('applewebkit') > 0));
- this.konq = (this.agent_lc.indexOf('konqueror') > 0);
- this.mz = (this.dom && !this.ie && !this.ns && !this.chrome && !this.safari && !this.konq && this.agent.indexOf('Mozilla') >= 0);
- this.iphone = (this.safari && (this.agent_lc.indexOf('iphone') > 0 || this.agent_lc.indexOf('ipod') > 0));
- this.ipad = (this.safari && this.agent_lc.indexOf('ipad') > 0);
- this.opera = window.opera ? true : false;
-
- if (this.opera && window.RegExp)
- this.vendver = (/opera(\s|\/)([0-9\.]+)/.test(this.agent_lc)) ? parseFloat(RegExp.$2) : -1;
- else if (this.chrome && window.RegExp)
- this.vendver = (/chrome\/([0-9\.]+)/.test(this.agent_lc)) ? parseFloat(RegExp.$1) : 0;
- else if (!this.vendver && this.safari)
- this.vendver = (/(safari|applewebkit)\/([0-9]+)/.test(this.agent_lc)) ? parseInt(RegExp.$2) : 0;
- else if ((!this.vendver && this.mz) || this.agent.indexOf('Camino')>0)
- this.vendver = (/rv:([0-9\.]+)/.test(this.agent)) ? parseFloat(RegExp.$1) : 0;
- else if (this.ie && window.RegExp)
- this.vendver = (/msie\s+([0-9\.]+)/.test(this.agent_lc)) ? parseFloat(RegExp.$1) : 0;
- else if (this.konq && window.RegExp)
- this.vendver = (/khtml\/([0-9\.]+)/.test(this.agent_lc)) ? parseFloat(RegExp.$1) : 0;
+ this.dom2 = document.addEventListener && document.removeEventListener;
+
+ this.ie = (document.all && !window.opera) || (this.win && this.agent_lc.indexOf('trident/') > 0);
+
+ if (this.ie) {
+ this.ie6 = this.appver.indexOf('MSIE 6') > 0;
+ this.ie7 = this.appver.indexOf('MSIE 7') > 0;
+ this.ie8 = this.appver.indexOf('MSIE 8') > 0;
+ this.ie9 = this.appver.indexOf('MSIE 9') > 0;
+ }
+ else {
+ this.chrome = this.agent_lc.indexOf('chrome') > 0;
+ this.safari = !this.chrome && (this.agent_lc.indexOf('safari') > 0 || this.agent_lc.indexOf('applewebkit') > 0);
+ this.konq = this.agent_lc.indexOf('konqueror') > 0;
+ this.mz = this.dom && !this.chrome && !this.safari && !this.konq && this.agent.indexOf('Mozilla') >= 0;
+ this.iphone = this.safari && (this.agent_lc.indexOf('iphone') > 0 || this.agent_lc.indexOf('ipod') > 0);
+ this.ipad = this.safari && this.agent_lc.indexOf('ipad') > 0;
+ this.opera = window.opera ? true : false;
+ }
+
+ if (!this.vendver) {
+ // common version strings
+ this.vendver = /(khtml|chrome|safari|applewebkit|opera|msie)(\s|\/)([0-9\.]+)/.test(this.agent_lc) ? parseFloat(RegExp.$3) : 0;
+
+ // any other (Mozilla, Camino, IE>=11)
+ if (!this.vendver)
+ this.vendver = /rv:([0-9\.]+)/.test(this.agent) ? parseFloat(RegExp.$1) : 0;
+ }
// get real language out of safari's user agent
if (this.safari && (/;\s+([a-z]{2})-[a-z]{2}\)/.test(this.agent_lc)))
@@ -86,11 +83,6 @@ function roundcube_browser()
this.tablet = /ipad|android|xoom|sch-i800|playbook|tablet|kindle/i.test(this.agent_lc);
this.mobile = /iphone|ipod|blackberry|iemobile|opera mini|opera mobi|mobile/i.test(this.agent_lc);
this.touch = this.mobile || this.tablet;
- this.dhtml = ((this.ie4 && this.win) || this.ie5 || this.ie6 || this.ns4 || this.mz);
- this.vml = (this.win && this.ie && this.dom && !this.opera);
- this.pngalpha = (this.mz || (this.opera && this.vendver >= 6) || (this.ie && this.mac && this.vendver >= 5) ||
- (this.ie && this.win && this.vendver >= 5.5) || this.safari);
- this.opacity = (this.mz || (this.ie && this.vendver >= 5.5 && !this.opera) || (this.safari && this.vendver >= 100));
this.cookies = n.cookieEnabled;
// test for XMLHTTP support
diff --git a/program/js/editor.js b/program/js/editor.js
index 020971d6e..df3d41240 100644
--- a/program/js/editor.js
+++ b/program/js/editor.js
@@ -61,6 +61,9 @@ function rcmail_editor_init(config)
if (!active)
rcmail.spellcheck_state();
});
+ ed.onKeyPress.add(function(ed, e) {
+ rcmail.compose_type_activity++;
+ });
}
}
diff --git a/program/js/list.js b/program/js/list.js
index e1d57745c..4ae6c0c66 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -795,8 +795,10 @@ select_row: function(id, mod_key, with_mouse)
break;
case CONTROL_KEY:
- if (with_mouse)
+ if (with_mouse) {
+ this.shift_start = id;
this.highlight_row(id, true);
+ }
break;
case CONTROL_SHIFT_KEY:
@@ -893,7 +895,7 @@ select_children: function(uid)
for (i=0; i<len; i++)
if (!this.in_selection(children[i]))
- this.select_row(children[i], CONTROL_KEY);
+ this.select_row(children[i], CONTROL_KEY, true);
},
@@ -1133,13 +1135,15 @@ key_press: function(e)
// Stop propagation so that the browser doesn't scroll
rcube_event.cancel(e);
return this.use_arrow_key(keyCode, mod_key);
+
case 32:
rcube_event.cancel(e);
return this.select_row(this.last_selected, mod_key, true);
+
case 37: // Left arrow key
case 39: // Right arrow key
case 107: // Plus sign on a numeric keypad
- case 109: // Minus sign on a numeric keypad
+ case 109: // Minus sign on a numeric keypad
// Stop propagation
rcube_event.cancel(e);
var ret = this.use_plusminus_key(keyCode, mod_key);
@@ -1148,20 +1152,26 @@ key_press: function(e)
this.triggerEvent('keypress');
this.modkey = 0;
return ret;
+
case 36: // Home
this.select_first(mod_key);
return rcube_event.cancel(e);
+
case 35: // End
this.select_last(mod_key);
return rcube_event.cancel(e);
+
case 27:
if (this.drag_active)
return this.drag_mouse_up(e);
+
if (this.col_drag_active) {
this.selected_column = null;
return this.column_drag_mouse_up(e);
}
+
return rcube_event.cancel(e);
+
default:
this.key_pressed = keyCode;
this.modkey = mod_key;
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 399f84fd8..e0fa22c3c 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -1537,6 +1537,10 @@ class rcube
!empty($response) ? join('; ', $response) : ''));
}
}
+ else {
+ // allow plugins to catch sending errors with the same parameters as in 'message_before_send'
+ $this->plugins->exec_hook('message_send_error', $plugin + array('error' => $error));
+ }
if (is_resource($msg_body)) {
fclose($msg_body);
diff --git a/program/lib/Roundcube/rcube_browser.php b/program/lib/Roundcube/rcube_browser.php
index 34128291b..e53e31200 100644
--- a/program/lib/Roundcube/rcube_browser.php
+++ b/program/lib/Roundcube/rcube_browser.php
@@ -28,32 +28,24 @@ class rcube_browser
{
$HTTP_USER_AGENT = strtolower($_SERVER['HTTP_USER_AGENT']);
- $this->ver = 0;
- $this->win = strpos($HTTP_USER_AGENT, 'win') != false;
- $this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
+ $this->ver = 0;
+ $this->win = strpos($HTTP_USER_AGENT, 'win') != false;
+ $this->mac = strpos($HTTP_USER_AGENT, 'mac') != false;
$this->linux = strpos($HTTP_USER_AGENT, 'linux') != false;
$this->unix = strpos($HTTP_USER_AGENT, 'unix') != false;
- $this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
- $this->ns4 = strpos($HTTP_USER_AGENT, 'mozilla/4') !== false && strpos($HTTP_USER_AGENT, 'msie') === false;
- $this->ns = ($this->ns4 || strpos($HTTP_USER_AGENT, 'netscape') !== false);
- $this->ie = !$this->opera && strpos($HTTP_USER_AGENT, 'compatible; msie') !== false;
- $this->khtml = strpos($HTTP_USER_AGENT, 'khtml') !== false;
- $this->mz = !$this->ie && !$this->khtml && strpos($HTTP_USER_AGENT, 'mozilla/5') !== false;
+ $this->opera = strpos($HTTP_USER_AGENT, 'opera') !== false;
+ $this->ns = strpos($HTTP_USER_AGENT, 'netscape') !== false;
$this->chrome = strpos($HTTP_USER_AGENT, 'chrome') !== false;
- $this->safari = !$this->chrome && ($this->khtml || strpos($HTTP_USER_AGENT, 'safari') !== false);
+ $this->ie = !$this->opera && (strpos($HTTP_USER_AGENT, 'compatible; msie') !== false || strpos($HTTP_USER_AGENT, 'trident/') !== false);
+ $this->safari = !$this->chrome && (strpos($HTTP_USER_AGENT, 'safari') !== false || strpos($HTTP_USER_AGENT, 'applewebkit') !== false);
+ $this->mz = !$this->ie && !$this->safari && !$this->chrome && !$this->ns && strpos($HTTP_USER_AGENT, 'mozilla') !== false;
- if ($this->ns || $this->chrome) {
- $test = preg_match('/(mozilla|chrome)\/([0-9.]+)/', $HTTP_USER_AGENT, $regs);
- $this->ver = $test ? (float)$regs[2] : 0;
+ if (preg_match('/(chrome|msie|opera|version|khtml)(\s*|\/)([0-9.]+)/', $HTTP_USER_AGENT, $regs)) {
+ $this->ver = (float) $regs[3];
}
- else if ($this->mz) {
- $test = preg_match('/rv:([0-9.]+)/', $HTTP_USER_AGENT, $regs);
- $this->ver = $test ? (float)$regs[1] : 0;
- }
- else if ($this->ie || $this->opera) {
- $test = preg_match('/(msie|opera) ([0-9.]+)/', $HTTP_USER_AGENT, $regs);
- $this->ver = $test ? (float)$regs[2] : 0;
+ else if (preg_match('/rv:([0-9.]+)/', $HTTP_USER_AGENT, $regs)) {
+ $this->ver = (float) $regs[1];
}
if (preg_match('/ ([a-z]{2})-([a-z]{2})/', $HTTP_USER_AGENT, $regs))
@@ -61,10 +53,10 @@ class rcube_browser
else
$this->lang = 'en';
- $this->dom = ($this->mz || $this->safari || ($this->ie && $this->ver>=5) || ($this->opera && $this->ver>=7));
+ $this->dom = $this->mz || $this->safari || ($this->ie && $this->ver>=5) || ($this->opera && $this->ver>=7);
$this->pngalpha = $this->mz || $this->safari || ($this->ie && $this->ver>=5.5) ||
($this->ie && $this->ver>=5 && $this->mac) || ($this->opera && $this->ver>=7) ? true : false;
- $this->imgdata = !$this->ie;
+ $this->imgdata = !$this->ie;
}
}
diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php
index 5c9e5ab39..2e03352bf 100644
--- a/program/lib/Roundcube/rcube_contacts.php
+++ b/program/lib/Roundcube/rcube_contacts.php
@@ -350,7 +350,7 @@ class rcube_contacts extends rcube_addressbook
if (in_array($col, $this->table_cols)) {
switch ($mode) {
case 1: // strict
- $where[] = '(' . $this->db->quoteIdentifier($col) . ' = ' . $this->db->quote($val)
+ $where[] = '(' . $this->db->quote_identifier($col) . ' = ' . $this->db->quote($val)
. ' OR ' . $this->db->ilike($col, $val . $AS . '%')
. ' OR ' . $this->db->ilike($col, '%' . $AS . $val . $AS . '%')
. ' OR ' . $this->db->ilike($col, '%' . $AS . $val) . ')';
@@ -390,7 +390,7 @@ class rcube_contacts extends rcube_addressbook
}
foreach (array_intersect($required, $this->table_cols) as $col) {
- $and_where[] = $this->db->quoteIdentifier($col).' <> '.$this->db->quote('');
+ $and_where[] = $this->db->quote_identifier($col).' <> '.$this->db->quote('');
}
if (!empty($where)) {
@@ -630,7 +630,7 @@ class rcube_contacts extends rcube_addressbook
$a_insert_cols = $a_insert_values = array();
foreach ($save_data as $col => $value) {
- $a_insert_cols[] = $this->db->quoteIdentifier($col);
+ $a_insert_cols[] = $this->db->quote_identifier($col);
$a_insert_values[] = $this->db->quote($value);
}
@@ -665,7 +665,7 @@ class rcube_contacts extends rcube_addressbook
$save_cols = $this->convert_save_data($save_cols, $record);
foreach ($save_cols as $col => $value) {
- $write_sql[] = sprintf("%s=%s", $this->db->quoteIdentifier($col), $this->db->quote($value));
+ $write_sql[] = sprintf("%s=%s", $this->db->quote_identifier($col), $this->db->quote($value));
}
if (!empty($write_sql)) {
diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php
index 00e6d4e20..aa385dce4 100644
--- a/program/lib/Roundcube/rcube_csv2vcard.php
+++ b/program/lib/Roundcube/rcube_csv2vcard.php
@@ -47,7 +47,7 @@ class rcube_csv2vcard
//'business_street_2' => '',
//'business_street_3' => '',
'car_phone' => 'phone:car',
- 'categories' => 'categories',
+ 'categories' => 'groups',
//'children' => '',
'company' => 'organization',
//'company_main_phone' => '',
@@ -146,6 +146,9 @@ class rcube_csv2vcard
'work_title' => 'jobtitle',
'work_zip' => 'zipcode:work',
'group' => 'groups',
+
+ // GMail
+ 'groups' => 'groups',
);
/**
@@ -427,6 +430,11 @@ class rcube_csv2vcard
$contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d'];
}
+ // categories/groups separator in vCard is ',' not ';'
+ if (!empty($contact['groups'])) {
+ $contact['groups'] = str_replace(';', ',', $contact['groups']);
+ }
+
// Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00"
foreach (array('birthday', 'anniversary') as $key) {
if (!empty($contact[$key])) {
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index aaba28172..2828f26ee 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -392,7 +392,7 @@ class rcube_db
*/
protected function _query($query, $offset, $numrows, $params)
{
- $query = trim($query);
+ $query = ltrim($query);
$this->db_connect($this->dsn_select($query), true);
@@ -405,27 +405,28 @@ class rcube_db
$query = $this->set_limit($query, $numrows, $offset);
}
- $params = (array) $params;
-
// Because in Roundcube we mostly use queries that are
// executed only once, we will not use prepared queries
$pos = 0;
$idx = 0;
- while ($pos = strpos($query, '?', $pos)) {
- if ($query[$pos+1] == '?') { // skip escaped ?
- $pos += 2;
- }
- else {
- $val = $this->quote($params[$idx++]);
- unset($params[$idx-1]);
- $query = substr_replace($query, $val, $pos, 1);
- $pos += strlen($val);
+ if (count($params)) {
+ while ($pos = strpos($query, '?', $pos)) {
+ if ($query[$pos+1] == '?') { // skip escaped '?'
+ $pos += 2;
+ }
+ else {
+ $val = $this->quote($params[$idx++]);
+ unset($params[$idx-1]);
+ $query = substr_replace($query, $val, $pos, 1);
+ $pos += strlen($val);
+ }
}
}
- // replace escaped ? back to normal
- $query = rtrim(strtr($query, array('??' => '?')), ';');
+ // replace escaped '?' back to normal, see self::quote()
+ $query = str_replace('??', '?', $query);
+ $query = rtrim($query, " \t\n\r\0\x0B;");
$this->debug($query);
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index 9faf1bbc6..fdda1d4b2 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -3848,9 +3848,12 @@ class rcube_imap extends rcube_storage
/**
* Sort folders first by default folders and then in alphabethical order
*
- * @param array $a_folders Folders list
+ * @param array $a_folders Folders list
+ * @param bool $skip_default Skip default folders handling
+ *
+ * @return array Sorted list
*/
- protected function sort_folder_list($a_folders)
+ public function sort_folder_list($a_folders, $skip_default = false)
{
$a_out = $a_defaults = $folders = array();
@@ -3862,7 +3865,7 @@ class rcube_imap extends rcube_storage
continue;
}
- if (($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
+ if (!$skip_default && ($p = array_search($folder, $this->default_folders)) !== false && !$a_defaults[$p]) {
$a_defaults[$p] = $folder;
}
else {
diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php
index 9b662a286..f24ec3ed8 100644
--- a/program/lib/Roundcube/rcube_message.php
+++ b/program/lib/Roundcube/rcube_message.php
@@ -211,16 +211,19 @@ class rcube_message
}
$level = explode('.', $part->mime_id);
+ $depth = count($level);
// Check if the part belongs to higher-level's multipart part
- // this can be alternative/related/signed/encrypted, but not mixed
+ // this can be alternative/related/signed/encrypted or mixed
while (array_pop($level) !== null) {
- if (!count($level)) {
+ $parent_depth = count($level);
+ if (!$parent_depth) {
return true;
}
$parent = $this->mime_parts[join('.', $level)];
- if (!preg_match('/^multipart\/(alternative|related|signed|encrypted)$/', $parent->mimetype)) {
+ if (!preg_match('/^multipart\/(alternative|related|signed|encrypted|mixed)$/', $parent->mimetype)
+ || ($parent->mimetype == 'multipart/mixed' && $parent_depth < $depth - 1)) {
continue 2;
}
}
@@ -529,8 +532,9 @@ class rcube_message
$part_mimetype = $mail_part->real_mimetype;
list($primary_type, $secondary_type) = explode('/', $part_mimetype);
}
- else
- $part_mimetype = $mail_part->mimetype;
+ else {
+ $part_mimetype = $part_orig_mimetype = $mail_part->mimetype;
+ }
// multipart/alternative
if ($primary_type == 'multipart') {
diff --git a/program/lib/Roundcube/rcube_plugin.php b/program/lib/Roundcube/rcube_plugin.php
index 3153a8410..aa6d8376e 100644
--- a/program/lib/Roundcube/rcube_plugin.php
+++ b/program/lib/Roundcube/rcube_plugin.php
@@ -109,7 +109,7 @@ abstract class rcube_plugin
*/
public function require_plugin($plugin_name)
{
- return $this->api->load_plugin($plugin_name);
+ return $this->api->load_plugin($plugin_name, true);
}
/**
diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php
index 2258f1486..ad012552d 100644
--- a/program/lib/Roundcube/rcube_plugin_api.php
+++ b/program/lib/Roundcube/rcube_plugin_api.php
@@ -168,10 +168,11 @@ class rcube_plugin_api
* Load the specified plugin
*
* @param string Plugin name
+ * @param boolean Force loading of the plugin even if it doesn't match the filter
*
* @return boolean True on success, false if not loaded or failure
*/
- public function load_plugin($plugin_name)
+ public function load_plugin($plugin_name, $force = false)
{
static $plugins_dir;
@@ -197,7 +198,7 @@ class rcube_plugin_api
// check inheritance...
if (is_subclass_of($plugin, 'rcube_plugin')) {
// ... task, request type and framed mode
- if ((!$plugin->task || preg_match('/^('.$plugin->task.')$/i', $this->task))
+ if (($force || !$plugin->task || preg_match('/^('.$plugin->task.')$/i', $this->task))
&& (!$plugin->noajax || (is_object($this->output) && $this->output->type == 'html'))
&& (!$plugin->noframe || empty($_REQUEST['_framed']))
) {
diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php
index 67072df41..caca262c6 100644
--- a/program/lib/Roundcube/rcube_session.php
+++ b/program/lib/Roundcube/rcube_session.php
@@ -34,6 +34,7 @@ class rcube_session
private $changed;
private $time_diff = 0;
private $reloaded = false;
+ private $appends = array();
private $unsets = array();
private $gc_handlers = array();
private $cookiename = 'roundcube_sessauth';
@@ -441,8 +442,19 @@ class rcube_session
$node = &$this->get_node(explode('.', $path), $_SESSION);
- if ($key !== null) $node[$key] = $value;
- else $node[] = $value;
+ if ($key !== null) {
+ $node[$key] = $value;
+ $path .= '.' . $key;
+ }
+ else {
+ $node[] = $value;
+ }
+
+ $this->appends[] = $path;
+
+ // when overwriting a previously unset variable
+ if ($this->unsets[$path])
+ unset($this->unsets[$path]);
}
@@ -491,13 +503,40 @@ class rcube_session
*/
public function reload()
{
+ // collect updated data from previous appends
+ $merge_data = array();
+ foreach ((array)$this->appends as $var) {
+ $path = explode('.', $var);
+ $value = $this->get_node($path, $_SESSION);
+ $k = array_pop($path);
+ $node = &$this->get_node($path, $merge_data);
+ $node[$k] = $value;
+ }
+
if ($this->key && $this->memcache)
$data = $this->mc_read($this->key);
else if ($this->key)
$data = $this->db_read($this->key);
- if ($data)
+ if ($data) {
session_decode($data);
+
+ // apply appends and unsets to reloaded data
+ $_SESSION = array_merge_recursive($_SESSION, $merge_data);
+
+ foreach ((array)$this->unsets as $var) {
+ if (isset($_SESSION[$var])) {
+ unset($_SESSION[$var]);
+ }
+ else {
+ $path = explode('.', $var);
+ $k = array_pop($path);
+ $node = &$this->get_node($path, $_SESSION);
+ unset($node[$k]);
+ }
+ }
+ }
+
}
/**
diff --git a/program/lib/Roundcube/rcube_spellchecker.php b/program/lib/Roundcube/rcube_spellchecker.php
index 3182ff378..5b77bda02 100644
--- a/program/lib/Roundcube/rcube_spellchecker.php
+++ b/program/lib/Roundcube/rcube_spellchecker.php
@@ -352,7 +352,7 @@ class rcube_spellchecker
"UPDATE ".$this->rc->db->table_name('dictionary')
." SET data = ?"
." WHERE user_id " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
- ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+ ." AND " . $this->rc->db->quote_identifier('language') . " = ?",
implode(' ', $plugin['dictionary']), $plugin['language']);
}
// don't store empty dict
@@ -360,14 +360,14 @@ class rcube_spellchecker
$this->rc->db->query(
"DELETE FROM " . $this->rc->db->table_name('dictionary')
." WHERE user_id " . ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
- ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+ ." AND " . $this->rc->db->quote_identifier('language') . " = ?",
$plugin['language']);
}
}
else if (!empty($this->dict)) {
$this->rc->db->query(
"INSERT INTO " .$this->rc->db->table_name('dictionary')
- ." (user_id, " . $this->rc->db->quoteIdentifier('language') . ", data) VALUES (?, ?, ?)",
+ ." (user_id, " . $this->rc->db->quote_identifier('language') . ", data) VALUES (?, ?, ?)",
$plugin['userid'], $plugin['language'], implode(' ', $plugin['dictionary']));
}
}
@@ -394,7 +394,7 @@ class rcube_spellchecker
$sql_result = $this->rc->db->query(
"SELECT data FROM ".$this->rc->db->table_name('dictionary')
." WHERE user_id ". ($plugin['userid'] ? "= ".$this->rc->db->quote($plugin['userid']) : "IS NULL")
- ." AND " . $this->rc->db->quoteIdentifier('language') . " = ?",
+ ." AND " . $this->rc->db->quote_identifier('language') . " = ?",
$plugin['language']);
if ($sql_arr = $this->rc->db->fetch_assoc($sql_result)) {
diff --git a/program/lib/Roundcube/rcube_user.php b/program/lib/Roundcube/rcube_user.php
index 57f63361d..1d5a90577 100644
--- a/program/lib/Roundcube/rcube_user.php
+++ b/program/lib/Roundcube/rcube_user.php
@@ -221,6 +221,14 @@ class rcube_user
return false;
}
+ /**
+ * Generate a unique hash to identify this user which
+ */
+ function get_hash()
+ {
+ $key = substr($this->rc->config->get('des_key'), 1, 4);
+ return md5($this->data['user_id'] . $key . $this->data['username'] . '@' . $this->data['mail_host']);
+ }
/**
* Get default identity of this user
@@ -257,7 +265,7 @@ class rcube_user
"SELECT * FROM ".$this->db->table_name('identities').
" WHERE del <> 1 AND user_id = ?".
($sql_add ? " ".$sql_add : "").
- " ORDER BY ".$this->db->quoteIdentifier('standard')." DESC, name ASC, identity_id ASC",
+ " ORDER BY ".$this->db->quote_identifier('standard')." DESC, name ASC, identity_id ASC",
$this->ID);
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -292,7 +300,7 @@ class rcube_user
$query_cols = $query_params = array();
foreach ((array)$data as $col => $value) {
- $query_cols[] = $this->db->quoteIdentifier($col) . ' = ?';
+ $query_cols[] = $this->db->quote_identifier($col) . ' = ?';
$query_params[] = $value;
}
$query_params[] = $iid;
@@ -328,7 +336,7 @@ class rcube_user
$insert_cols = $insert_values = array();
foreach ((array)$data as $col => $value) {
- $insert_cols[] = $this->db->quoteIdentifier($col);
+ $insert_cols[] = $this->db->quote_identifier($col);
$insert_values[] = $value;
}
$insert_cols[] = 'user_id';
@@ -393,7 +401,7 @@ class rcube_user
if ($this->ID && $iid) {
$this->db->query(
"UPDATE ".$this->db->table_name('identities').
- " SET ".$this->db->quoteIdentifier('standard')." = '0'".
+ " SET ".$this->db->quote_identifier('standard')." = '0'".
" WHERE user_id = ?".
" AND identity_id <> ?".
" AND del <> 1",
@@ -633,11 +641,11 @@ class rcube_user
$result = array();
$sql_result = $this->db->query(
- "SELECT search_id AS id, ".$this->db->quoteIdentifier('name')
+ "SELECT search_id AS id, ".$this->db->quote_identifier('name')
." FROM ".$this->db->table_name('searches')
." WHERE user_id = ?"
- ." AND ".$this->db->quoteIdentifier('type')." = ?"
- ." ORDER BY ".$this->db->quoteIdentifier('name'),
+ ." AND ".$this->db->quote_identifier('type')." = ?"
+ ." ORDER BY ".$this->db->quote_identifier('name'),
(int) $this->ID, (int) $type);
while ($sql_arr = $this->db->fetch_assoc($sql_result)) {
@@ -665,9 +673,9 @@ class rcube_user
}
$sql_result = $this->db->query(
- "SELECT ".$this->db->quoteIdentifier('name')
- .", ".$this->db->quoteIdentifier('data')
- .", ".$this->db->quoteIdentifier('type')
+ "SELECT ".$this->db->quote_identifier('name')
+ .", ".$this->db->quote_identifier('data')
+ .", ".$this->db->quote_identifier('type')
." FROM ".$this->db->table_name('searches')
." WHERE user_id = ?"
." AND search_id = ?",
@@ -722,11 +730,11 @@ class rcube_user
$insert_cols[] = 'user_id';
$insert_values[] = (int) $this->ID;
- $insert_cols[] = $this->db->quoteIdentifier('type');
+ $insert_cols[] = $this->db->quote_identifier('type');
$insert_values[] = (int) $data['type'];
- $insert_cols[] = $this->db->quoteIdentifier('name');
+ $insert_cols[] = $this->db->quote_identifier('name');
$insert_values[] = $data['name'];
- $insert_cols[] = $this->db->quoteIdentifier('data');
+ $insert_cols[] = $this->db->quote_identifier('data');
$insert_values[] = serialize($data['data']);
$sql = "INSERT INTO ".$this->db->table_name('searches')
diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php
index 5f74ccbd4..a54ee7e11 100644
--- a/program/lib/Roundcube/rcube_vcard.php
+++ b/program/lib/Roundcube/rcube_vcard.php
@@ -378,7 +378,7 @@ class rcube_vcard
default:
if ($field == 'phone' && $this->phonetypemap[$type_uc]) {
$type = $this->phonetypemap[$type_uc];
- }
+ }
if (($tag = self::$fieldmap[$field]) && (is_array($value) || strlen($value))) {
$index = count($this->raw[$tag]);
diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc
index 8f221a3a9..92ec82617 100644
--- a/program/localization/en_US/labels.inc
+++ b/program/localization/en_US/labels.inc
@@ -232,6 +232,9 @@ $labels['checkspelling'] = 'Check spelling';
$labels['resumeediting'] = 'Resume editing';
$labels['revertto'] = 'Revert to';
+$labels['restore'] = 'Restore';
+$labels['restoremessage'] = 'Restore message?';
+
$labels['responses'] = 'Responses';
$labels['insertresponse'] = 'Insert a response';
$labels['manageresponses'] = 'Manage responses';
diff --git a/program/localization/en_US/messages.inc b/program/localization/en_US/messages.inc
index 033c820d1..a36d9ab62 100644
--- a/program/localization/en_US/messages.inc
+++ b/program/localization/en_US/messages.inc
@@ -84,6 +84,7 @@ $messages['norecipientwarning'] = 'Please enter at least one recipient.';
$messages['nosubjectwarning'] = 'The "Subject" field is empty. Would you like to enter one now?';
$messages['nobodywarning'] = 'Send this message without text?';
$messages['notsentwarning'] = 'Message has not been sent. Do you want to discard your message?';
+$messages['restoresavedcomposedata'] = 'A previously composed but unsent message was found.\n\nSubject: $subject\nSaved: $date\n\nDo you want to restore this message?';
$messages['noldapserver'] = 'Please select an ldap server to search.';
$messages['nosearchname'] = 'Please enter a contact name or email address.';
$messages['notuploadedwarning'] = 'Not all attachments have been uploaded yet. Please wait or cancel the upload.';
diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc
index 646d2bcd1..f75b219ff 100644
--- a/program/steps/mail/compose.inc
+++ b/program/steps/mail/compose.inc
@@ -110,9 +110,10 @@ $OUTPUT->add_label('nosubject', 'nosenderwarning', 'norecipientwarning', 'nosubj
'nobodywarning', 'notsentwarning', 'notuploadedwarning', 'savingmessage', 'sendingmessage',
'messagesaved', 'converting', 'editorwarning', 'searching', 'uploading', 'uploadingmany',
'fileuploaderror', 'sendmessage', 'savenewresponse', 'responsename', 'responsetext', 'save',
- 'savingresponse');
+ 'savingresponse', 'restoresavedcomposedata', 'restoremessage', 'delete', 'restore', 'ignore');
$OUTPUT->set_env('compose_id', $COMPOSE['id']);
+$OUTPUT->set_env('session_id', session_id());
$OUTPUT->set_pagetitle(rcube_label('compose'));
// add config parameters to client script
@@ -239,6 +240,9 @@ if (!empty($msg_uid) && empty($COMPOSE['as_attachment']))
$COMPOSE['reply_msgid'] = '<' . $in_reply_to . '>';
$COMPOSE['references'] = $MESSAGE->headers->references;
+
+ // use message-ID as draft_id, same as in sendmail.inc
+ $OUTPUT->set_env('draft_id', trim($MESSAGE->headers->get('message-id'), '<>'));
}
}
else {
@@ -442,6 +446,11 @@ function rcmail_process_compose_params(&$COMPOSE)
}
}
+ // clean HTML message body which can be submitted by URL
+ if ($COMPOSE['param']['body']) {
+ $COMPOSE['param']['body'] = rcmail_wash_html($COMPOSE['param']['body'], array('safe' => false, 'inline_html' => true), array());
+ }
+
$RCMAIL = rcmail::get_instance();
// select folder where to save the sent message
@@ -642,7 +651,7 @@ function rcmail_prepare_message_body()
}
else if ($COMPOSE['param']['body']) {
$body = $COMPOSE['param']['body'];
- $isHtml = false;
+ $isHtml = (bool) $COMPOSE['param']['html'];
}
// forward as attachment
else if ($compose_mode == RCUBE_COMPOSE_FORWARD && $COMPOSE['as_attachment']) {
@@ -827,6 +836,9 @@ function rcmail_compose_body($attrib)
$msgtype = new html_hiddenfield(array('name' => '_is_html', 'value' => ($isHtml?"1":"0")));
$out .= $msgtype->show();
+ $framed = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
+ $out .= $framed->show();
+
// If desired, set this textarea to be editable by TinyMCE
if ($isHtml) {
$MESSAGE_BODY = htmlentities($MESSAGE_BODY, ENT_NOQUOTES, RCMAIL_CHARSET);
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 78a977b82..8164592dd 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -1342,9 +1342,9 @@ function rcmail_html4inline($body, $container_id, $body_id='', &$attributes=null
if (preg_match($regexp, $body, $m)) {
$attrs = $m[0];
// Get bgcolor, we'll set it as background-color of the message container
- if ($m[1] && preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/', $attrs, $mb)) {
+ if ($m[1] && preg_match('/bgcolor=["\']*([a-z0-9#]+)["\']*/i', $attrs, $mb)) {
$attributes['background-color'] = $mb[1];
- $attrs = preg_replace('/bgcolor=["\']*([a-z0-9#]+)["\']*/', '', $attrs);
+ $attrs = preg_replace('/bgcolor=["\']*[a-z0-9#]+["\']*/i', '', $attrs);
}
// Get background, we'll set it as background-image of the message container
if ($m[1] && preg_match('/background=["\']*([^"\'>\s]+)["\']*/', $attrs, $mb)) {
diff --git a/program/steps/mail/get.inc b/program/steps/mail/get.inc
index e0c4e2911..ae48307e3 100644
--- a/program/steps/mail/get.inc
+++ b/program/steps/mail/get.inc
@@ -84,17 +84,18 @@ else if ($_GET['_thumb']) {
// render thumbnail image if not done yet
if (!is_file($cache_file)) {
- $fp = fopen(($orig_name = $cache_basename . '.orig.' . $ext), 'w');
- $MESSAGE->get_part_content($part->mime_id, $fp);
- fclose($fp);
-
- $image = new rcube_image($orig_name);
- if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
- $mimetype = 'image/' . $imgtype;
- unlink($orig_name);
- }
- else {
- rename($orig_name, $cache_file);
+ if ($fp = fopen(($orig_name = $cache_basename . '.orig.' . $ext), 'w')) {
+ $MESSAGE->get_part_content($part->mime_id, $fp);
+ fclose($fp);
+
+ $image = new rcube_image($orig_name);
+ if ($imgtype = $image->resize($thumbnail_size, $cache_file, true)) {
+ $mimetype = 'image/' . $imgtype;
+ unlink($orig_name);
+ }
+ else {
+ rename($orig_name, $cache_file);
+ }
}
}
diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc
index 52b02ecff..ea5eaaed1 100644
--- a/program/steps/mail/sendmail.inc
+++ b/program/steps/mail/sendmail.inc
@@ -855,6 +855,7 @@ else {
$folders[] = $COMPOSE['mailbox'];
rcmail_compose_cleanup($COMPOSE_ID);
+ $OUTPUT->command('remove_compose_data', $COMPOSE_ID);
if ($store_folder && !$saved)
$OUTPUT->command('sent_successfully', 'error', rcube_label('errorsavingsent'), $folders);
diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc
index f19e2177b..7f2a10ebc 100644
--- a/program/steps/settings/edit_folder.inc
+++ b/program/steps/settings/edit_folder.inc
@@ -28,11 +28,11 @@ function rcmail_folder_form($attrib)
$storage = $RCMAIL->get_storage();
// edited folder name (empty in create-folder mode)
- $mbox = trim(get_input_value('_mbox', RCUBE_INPUT_GPC, true));
+ $mbox = get_input_value('_mbox', RCUBE_INPUT_GPC, true);
$mbox_imap = rcube_charset_convert($mbox, RCMAIL_CHARSET, 'UTF7-IMAP');
// predefined path for new folder
- $parent = trim(get_input_value('_path', RCUBE_INPUT_GPC, true));
+ $parent = get_input_value('_path', RCUBE_INPUT_GPC, true);
$parent_imap = rcube_charset_convert($parent, RCMAIL_CHARSET, 'UTF7-IMAP');
$threading_supported = $storage->get_capability('THREAD');
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 64af18d62..44482e938 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -109,7 +109,7 @@ else if ($RCMAIL->action == 'delete-folder')
else if ($RCMAIL->action == 'rename-folder')
{
$name_utf8 = trim(get_input_value('_folder_newname', RCUBE_INPUT_POST, true));
- $oldname_utf8 = trim(get_input_value('_folder_oldname', RCUBE_INPUT_POST, true));
+ $oldname_utf8 = get_input_value('_folder_oldname', RCUBE_INPUT_POST, true);
if (strlen($name_utf8) && strlen($oldname_utf8)) {
$name = rcube_charset_convert($name_utf8, RCMAIL_CHARSET, 'UTF7-IMAP');
@@ -167,7 +167,7 @@ else if ($RCMAIL->action == 'purge')
// get mailbox size
else if ($RCMAIL->action == 'folder-size')
{
- $name = trim(get_input_value('_mbox', RCUBE_INPUT_POST, true));
+ $name = get_input_value('_mbox', RCUBE_INPUT_POST, true);
$size = $STORAGE->folder_size($name);
diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc
index c922aca08..81744d904 100644
--- a/program/steps/settings/func.inc
+++ b/program/steps/settings/func.inc
@@ -1180,12 +1180,29 @@ function rcmail_user_prefs($current = null)
$data = $RCMAIL->plugins->exec_hook('preferences_list',
array('section' => $sect['id'], 'blocks' => $blocks, 'current' => $current));
+ $advanced_prefs = $config['advanced_prefs'];
+
// create output
- foreach ($data['blocks'] as $block) {
+ foreach ($data['blocks'] as $key => $block) {
if (!empty($block['content']) || !empty($block['options'])) {
$found = true;
- break;
}
+ // move some options to the 'advanced' block as configured by admin
+ if ($key != 'advanced') {
+ foreach ($advanced_prefs as $opt) {
+ if ($block['options'][$opt]) {
+ $data['blocks']['advanced']['options'][$opt] = $block['options'][$opt];
+ unset($data['blocks'][$key]['options'][$opt]);
+ }
+ }
+ }
+ }
+
+ // move 'advanced' block to the end of the list
+ if (!empty($data['blocks']['advanced'])) {
+ $adv = $data['blocks']['advanced'];
+ unset($data['blocks']['advanced']);
+ $data['blocks']['advanced'] = $adv;
}
if (!$found)
diff --git a/program/steps/settings/save_folder.inc b/program/steps/settings/save_folder.inc
index 877b0fbbe..efb096d57 100644
--- a/program/steps/settings/save_folder.inc
+++ b/program/steps/settings/save_folder.inc
@@ -5,7 +5,7 @@
| program/steps/settings/save_folder.inc |
| |
| This file is part of the Roundcube Webmail client |
- | Copyright (C) 2005-2012, The Roundcube Dev Team |
+ | Copyright (C) 2005-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
@@ -26,8 +26,8 @@ $STORAGE = $RCMAIL->get_storage();
$name = trim(get_input_value('_name', RCUBE_INPUT_POST, true));
-$old = trim(get_input_value('_mbox', RCUBE_INPUT_POST, true));
-$path = trim(get_input_value('_parent', RCUBE_INPUT_POST, true));
+$old = get_input_value('_mbox', RCUBE_INPUT_POST, true);
+$path = get_input_value('_parent', RCUBE_INPUT_POST, true);
$name_imap = rcube_charset_convert($name, RCMAIL_CHARSET, 'UTF7-IMAP');
$old_imap = rcube_charset_convert($old, RCMAIL_CHARSET, 'UTF7-IMAP');
diff --git a/program/steps/utils/error.inc b/program/steps/utils/error.inc
index 9fb71c528..a891d42f8 100644
--- a/program/steps/utils/error.inc
+++ b/program/steps/utils/error.inc
@@ -110,6 +110,7 @@ EOF;
if ($rcmail->output && $rcmail->output->template_exists('error')) {
$rcmail->output->reset();
+ $rcmail->output->set_env('server_error', $ERROR_CODE);
$rcmail->output->send('error');
}
diff --git a/public_html/.htaccess b/public_html/.htaccess
new file mode 120000
index 000000000..94546dc49
--- /dev/null
+++ b/public_html/.htaccess
@@ -0,0 +1 @@
+../.htaccess \ No newline at end of file
diff --git a/public_html/index.php b/public_html/index.php
new file mode 100644
index 000000000..cec0dca07
--- /dev/null
+++ b/public_html/index.php
@@ -0,0 +1,27 @@
+<?php
+
+/*
+ +-----------------------------------------------------------------------+
+ | Roundcube Webmail IMAP Client |
+ | Version 1.0-git |
+ | |
+ | Copyright (C) 2005-2013, The Roundcube Dev Team |
+ | |
+ | Licensed under the GNU General Public License version 3 or |
+ | any later version with exceptions for skins & plugins. |
+ | See the README file for a full license statement. |
+ | |
+ | PURPOSE: |
+ | This is the public entry point for all HTTP requests to the |
+ | Roundcue webmail application. |
+ +-----------------------------------------------------------------------+
+ | Author: Thomas Bruederli <roundcube@gmail.com> |
+ | Author: Aleksander Machniak <alec@alec.pl> |
+ +-----------------------------------------------------------------------+
+*/
+
+define('INSTALL_PATH', realpath(__DIR__ . '/..') . '/');
+
+// include index.php from application root directory
+include INSTALL_PATH . 'index.php';
+
diff --git a/public_html/plugins b/public_html/plugins
new file mode 120000
index 000000000..7655edcc0
--- /dev/null
+++ b/public_html/plugins
@@ -0,0 +1 @@
+../plugins \ No newline at end of file
diff --git a/public_html/program/js b/public_html/program/js
new file mode 120000
index 000000000..2c7564380
--- /dev/null
+++ b/public_html/program/js
@@ -0,0 +1 @@
+../../program/js \ No newline at end of file
diff --git a/public_html/program/resources b/public_html/program/resources
new file mode 120000
index 000000000..a1164a03c
--- /dev/null
+++ b/public_html/program/resources
@@ -0,0 +1 @@
+../../program/resources \ No newline at end of file
diff --git a/public_html/robots.txt b/public_html/robots.txt
new file mode 120000
index 000000000..123b16649
--- /dev/null
+++ b/public_html/robots.txt
@@ -0,0 +1 @@
+../robots.txt \ No newline at end of file
diff --git a/public_html/skins b/public_html/skins
new file mode 120000
index 000000000..249223789
--- /dev/null
+++ b/public_html/skins
@@ -0,0 +1 @@
+../skins \ No newline at end of file
diff --git a/skins/classic/functions.js b/skins/classic/functions.js
index 8d81c3ad2..2b7886d38 100644
--- a/skins/classic/functions.js
+++ b/skins/classic/functions.js
@@ -984,7 +984,7 @@ function rcube_init_mail_ui()
else if (rcmail.env.action == 'show' || rcmail.env.action == 'preview')
// add menu link for each attachment
$('#attachment-list > li[id^="attach"]').each(function() {
- $(this).append($('<a class="drop">').click(function() { rcmail_ui.show_attachmentmenu(this); }));
+ $(this).append($('<a class="drop"></a>').click(function() { rcmail_ui.show_attachmentmenu(this); }));
});
}
else if (rcmail.env.task == 'addressbook') {
diff --git a/skins/classic/mail.css b/skins/classic/mail.css
index 23350dda4..2b09ba269 100644
--- a/skins/classic/mail.css
+++ b/skins/classic/mail.css
@@ -937,7 +937,7 @@ table.messagelist.fixedcopy
.messagelist tr td.date
{
- width: 118px;
+ width: 135px;
padding: 0 2px;
}
diff --git a/skins/larry/addressbook.css b/skins/larry/addressbook.css
index a70b1ba69..8681cda70 100644
--- a/skins/larry/addressbook.css
+++ b/skins/larry/addressbook.css
@@ -200,7 +200,7 @@
top: 0;
left: 0;
right: 0;
- bottom: 28px;
+ bottom: 0px;
border: 0;
border-radius: 4px;
}
@@ -393,7 +393,7 @@ a.deletebutton {
#import-box {
position: absolute;
- bottom: 28px;
+ bottom: 0px;
top: 34px;
left: 0;
right: 0;
diff --git a/skins/larry/ie7hacks.css b/skins/larry/ie7hacks.css
index fc4713361..60adff7a1 100644
--- a/skins/larry/ie7hacks.css
+++ b/skins/larry/ie7hacks.css
@@ -43,7 +43,8 @@ a.deletebutton,
.attachmentslist li a.cancelupload,
#contacts-table td.action a,
.previewheader .iconlink,
-.minimal #taskbar .button-inner {
+.minimal #taskbar .button-inner,
+#preferences-details fieldset.advanced .advanced-toggle {
/* workaround for text-indent which also offsets the background image */
text-indent: 0;
font-size: 0;
diff --git a/skins/larry/iehacks.css b/skins/larry/iehacks.css
index 960ce7648..7c1585107 100644
--- a/skins/larry/iehacks.css
+++ b/skins/larry/iehacks.css
@@ -56,6 +56,10 @@ input.button:active {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#eaeaea', endColorstr='#c8c8c8', GradientType=0);
}
+#messagestack div {
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e0404040', endColorstr='#e0303030', GradientType=0);
+}
+
.ui-dialog.popupmessage .ui-dialog-titlebar {
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#e3e3e3', endColorstr='#cfcfcf', GradientType=0);
}
diff --git a/skins/larry/images/ajaxloader_dark.gif b/skins/larry/images/ajaxloader_dark.gif
new file mode 100644
index 000000000..f1cce35ac
--- /dev/null
+++ b/skins/larry/images/ajaxloader_dark.gif
Binary files differ
diff --git a/skins/larry/images/filetypes.png b/skins/larry/images/filetypes.png
index 53b2eee9a..09772660b 100644
--- a/skins/larry/images/filetypes.png
+++ b/skins/larry/images/filetypes.png
Binary files differ
diff --git a/skins/larry/images/listicons.png b/skins/larry/images/listicons.png
index 2527fe10d..8a17cc5bd 100644
--- a/skins/larry/images/listicons.png
+++ b/skins/larry/images/listicons.png
Binary files differ
diff --git a/skins/larry/images/messages_dark.png b/skins/larry/images/messages_dark.png
new file mode 100644
index 000000000..d7c932502
--- /dev/null
+++ b/skins/larry/images/messages_dark.png
Binary files differ
diff --git a/skins/larry/includes/footer.html b/skins/larry/includes/footer.html
index a4fa69296..5cf9d1ec5 100644
--- a/skins/larry/includes/footer.html
+++ b/skins/larry/includes/footer.html
@@ -1,3 +1,5 @@
+<roundcube:object name="message" id="messagestack" condition="env:task != 'login'" />
+
<script type="text/javascript">
// UI startup
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index f3b95b850..90b3511d4 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -37,19 +37,16 @@
top: 42px;
left: 0;
width: 100%;
- bottom: 28px;
-}
-
-#mailview-top.fullheight {
- border-radius: 4px 4px 0 0;
+ bottom: 0px;
}
#mailview-bottom {
+ display: none;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
- height: 27px;
+ height: 0;
border-radius: 4px;
border-top: none;
}
@@ -102,10 +99,6 @@ html>/**/body #messagelist {
border-radius: 0 0 4px 4px;
}
-#mailview-top.fullheight #messagelistfooter {
- border-radius: 0;
-}
-
#messagelistfooter.rightalign {
text-align: right;
}
@@ -513,7 +506,7 @@ table.messagelist.fixedcopy {
}
.messagelist tr td.date {
- width: 135px;
+ width: 155px;
}
.messagelist tr.message {
@@ -752,7 +745,7 @@ table.messagelist.fixedcopy {
top: 0;
left: 0;
width: 100%;
- bottom: 28px;
+ bottom: 0px;
}
#messagecontframe {
@@ -765,7 +758,7 @@ table.messagelist.fixedcopy {
top: 110px;
left: 0;
width: 100%;
- bottom: 27px;
+ bottom: 1px;
overflow: auto;
-webkit-overflow-scrolling: touch;
}
@@ -1367,9 +1360,7 @@ div.message-partheaders .headers-table td.header {
top: 42px;
left: 0;
width: 100%;
- bottom: 28px;
- border-radius: 4px 4px 0 0;
- border-bottom: none;
+ bottom: 0px;
overflow: hidden;
}
@@ -1490,6 +1481,7 @@ div.message-partheaders .headers-table td.header {
left: 0;
right: 260px;
bottom: 0;
+ border-radius: 0 0 0 4px;
}
#composebodycontainer.buttons {
@@ -1503,7 +1495,7 @@ div.message-partheaders .headers-table td.header {
bottom: 0;
width: 99%;
border: 0;
- border-radius: 0;
+ border-radius: 0 0 0 4px;
padding: 8px 0 8px 8px;
resize: none;
font-family: monospace;
diff --git a/skins/larry/settings.css b/skins/larry/settings.css
index a769ac412..af667b9e7 100644
--- a/skins/larry/settings.css
+++ b/skins/larry/settings.css
@@ -2,7 +2,7 @@
* Roundcube webmail styles for the Settings section
*
* Copyright (c) 2012, The Roundcube Dev Team
- * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com
+ * Screendesign by FLINT / Büro für Gestaltung, bueroflint.com
*
* The contents are subject to the Creative Commons Attribution-ShareAlike
* License. It is allowed to copy, distribute, transmit and to adapt the work
@@ -61,11 +61,21 @@
#preferences-details fieldset.advanced .advanced-toggle {
position: absolute;
- top: 2px;
+ display: block;
+ top: 0px;
right: 6px;
text-decoration: none;
color: #666;
font-size: 11px;
+ width: 20px;
+ height: 18px;
+ background: url('images/listicons.png') 0 -1157px no-repeat;
+ text-indent: 1000px;
+ overflow: hidden;
+}
+
+#preferences-details fieldset.advanced .collapsed .advanced-toggle {
+ background-position: -24px -1137px;
}
#sections-table tbody td.section,
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index b47edec34..810299735 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -509,6 +509,7 @@ a.iconlink.upload {
}
#message.statusbar {
+ display: none;
position: absolute;
bottom: 0;
left: 0;
@@ -528,6 +529,93 @@ a.iconlink.upload {
text-overflow: ellipsis;
}
+#messagestack {
+ position: absolute;
+ bottom: 14px;
+ right: 5px;
+ z-index: 50000;
+ width: auto;
+ height: auto;
+ max-height: 85%;
+ overflow-y: auto;
+ padding: 2px;
+}
+
+#messagestack div {
+ display: block;
+ position: relative;
+ width: 280px;
+ height: auto;
+ min-height: 16px;
+ margin: 3px 2px 5px 2px;
+ padding: 8px 10px 7px 30px;
+ cursor: default;
+ font-size: 12px;
+ font-weight: bold;
+ border-radius: 4px;
+ border: 1px solid #444;
+ color: #ebebeb;
+ text-shadow: 0 1px 1px #000;
+
+ background: rgba(64,64,64,0.9);
+ background: -moz-linear-gradient(top, rgba(64,64,64,0.9) 0%, rgba(48,48,48,0.9) 100%);
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(64,64,64,0.9)), color-stop(100%,rgba(48,48,48,0.9)));
+ background: -webkit-linear-gradient(top, rgba(64,64,64,0.9) 0%, rgba(48,48,48,0.9) 100%);
+ background: -o-linear-gradient(top, rgba(64,64,64,0.9) 0%, rgba(48,48,48,0.9) 100%);
+ background: -ms-linear-gradient(top, rgba(64,64,64,0.9) 0%, rgba(48,48,48,0.9) 100%);
+ background: linear-gradient(to bottom, rgba(64,64,64,0.9) 0%, rgba(48,48,48,0.9) 100%);
+
+ -moz-box-shadow: 0 1px 4px 0 rgba(50,50,50,0.8), inset 0px 1px 0 0px #888;
+ -webkit-box-shadow: 0 1px 4px 0 rgba(50,50,50,0.8), inset 0px 1px 0 0px #888;
+ -o-box-shadow: 0 1px 4px 0 rgba(50,50,50,0.8), inset 0px 1px 0 0px #888;
+ box-shadow: 0 1px 4px 0 rgba(50,50,50,0.8), inset 0px 1px 0 0px #888;
+}
+
+#messagestack div:after {
+ content: "";
+ position: absolute;
+ display: block;
+ top: 0;
+ left: 4px;
+ width: 20px;
+ height: 24px;
+ background: url(images/messages_dark.png) 0 6px no-repeat;
+}
+
+#messagestack div.error {
+ color: #ff615d;
+}
+
+#messagestack div.error:after {
+ background-position: 0 -55px;
+}
+
+#messagestack div.warning {
+ color: #f4bf0e;
+}
+
+#messagestack div.warning:after {
+ background-position: 0 -84px;
+}
+
+#messagestack div.confirmation {
+ color: #00e05a;
+}
+
+#messagestack div.confirmation:after {
+ background-position: 0 -25px;
+}
+
+#messagestack div.loading {
+ color: #ddd;
+}
+
+#messagestack div.loading:after {
+ top: 4px;
+ left: 6px;
+ background: url(images/ajaxloader_dark.gif) 0 4px no-repeat;
+}
+
.ui-dialog.error .ui-dialog-title,
.ui-dialog.warning .ui-dialog-title,
.ui-dialog.confirmation .ui-dialog-title {
@@ -1241,7 +1329,6 @@ table.records-table {
display: table;
width: 100%;
table-layout: fixed;
- border-collapse: collapse;
border-spacing: 0;
border: 1px solid #bbd3da;
}
@@ -1413,7 +1500,7 @@ body.iframe .footerleft.floating:before,
top: 34px;
left: 0;
right: 0;
- bottom: 28px;
+ bottom: 0px;
overflow: auto;
}
@@ -1422,7 +1509,7 @@ body.iframe .footerleft.floating:before,
top: 0;
left: 0;
right: 0;
- bottom: 28px;
+ bottom: 0px;
}
.footerleft {
@@ -1619,11 +1706,13 @@ ul.proplist li {
min-height: 40px;
padding: 5px 25px;
text-align: center;
+ font-size: 1.1em;
}
#login-form #message div {
display: inline-block;
padding-right: 0;
+ font-size: 12px;
}
#bottomline {
@@ -2331,6 +2420,18 @@ ul.toolbarmenu li span.conversation {
background-position: 0 -494px;
}
+.attachmentslist li.ppt,
+.attachmentslist li.pptx,
+.attachmentslist li.ppsx,
+.attachmentslist li.vnd.mspowerpoint {
+ background-position: 0 -520px;
+}
+
+.attachmentslist li.odp,
+.attachmentslist li.otp {
+ background-position: 0 -546px;
+}
+
.attachmentslist li a,
#compose-attachments ul li {
display: block;
diff --git a/skins/larry/svggradients.css b/skins/larry/svggradients.css
index c40d44f4b..2596f2bd2 100644
--- a/skins/larry/svggradients.css
+++ b/skins/larry/svggradients.css
@@ -56,6 +56,10 @@ input.button:active {
background-image: url(svggradient.php?c=eaeaea;c8c8c8);
}
+#messagestack div {
+ background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iIzQwNDA0MCIgc3RvcC1vcGFjaXR5PSIwLjg4Ii8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMzMDMwMzAiIHN0b3Atb3BhY2l0eT0iMC44OCIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
+}
+
.ui-dialog.popupmessage .ui-dialog-titlebar {
background-image: url(svggradient.php?c=e3e3e3;cfcfcf);
}
diff --git a/skins/larry/templates/addressbook.html b/skins/larry/templates/addressbook.html
index b33ebd999..9f83853b2 100644
--- a/skins/larry/templates/addressbook.html
+++ b/skins/larry/templates/addressbook.html
@@ -71,7 +71,6 @@
<div class="iframebox">
<roundcube:object name="addressframe" id="contact-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
diff --git a/skins/larry/templates/compose.html b/skins/larry/templates/compose.html
index 5a6285c68..11662d185 100644
--- a/skins/larry/templates/compose.html
+++ b/skins/larry/templates/compose.html
@@ -179,10 +179,6 @@
</form>
-<div id="mailview-bottom" class="uibox">
- <roundcube:object name="message" id="message" class="statusbar" />
-</div>
-
</div><!-- end mailview-right -->
</div><!-- end mainscreen -->
diff --git a/skins/larry/templates/folders.html b/skins/larry/templates/folders.html
index 988ff952c..56396bf1d 100644
--- a/skins/larry/templates/folders.html
+++ b/skins/larry/templates/folders.html
@@ -31,7 +31,6 @@
<div class="iframebox">
<roundcube:object name="folderframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/templates/identities.html b/skins/larry/templates/identities.html
index d9270b68a..e3d2cc842 100644
--- a/skins/larry/templates/identities.html
+++ b/skins/larry/templates/identities.html
@@ -28,7 +28,6 @@
<div class="iframebox">
<roundcube:object name="identityframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/templates/importcontacts.html b/skins/larry/templates/importcontacts.html
index 69b138b9a..d7c690a6a 100644
--- a/skins/larry/templates/importcontacts.html
+++ b/skins/larry/templates/importcontacts.html
@@ -24,9 +24,6 @@
<roundcube:object name="importnav" class="button" />
</p>
</div>
-
-<roundcube:object name="message" id="message" class="statusbar" />
-
</div>
<roundcube:include file="/includes/footer.html" />
diff --git a/skins/larry/templates/login.html b/skins/larry/templates/login.html
index 13e919ad3..8da941189 100644
--- a/skins/larry/templates/login.html
+++ b/skins/larry/templates/login.html
@@ -39,11 +39,13 @@
<roundcube:object name="preloader" images="
/images/ajaxloader.gif
+ /images/ajaxloader_dark.gif
/images/buttons.png
/images/addcontact.png
/images/filetypes.png
/images/listicons.png
/images/messages.png
+ /images/messages_dark.png
/images/quota.png
/images/selector.png
/images/splitter.png
diff --git a/skins/larry/templates/mail.html b/skins/larry/templates/mail.html
index f2c52c820..2e7c0c105 100644
--- a/skins/larry/templates/mail.html
+++ b/skins/larry/templates/mail.html
@@ -6,7 +6,7 @@
<style type="text/css">
<roundcube:if condition="config:preview_pane == true" />
#mailview-top { height: <roundcube:exp expression="!empty(cookie:mailviewsplitter) ? cookie:mailviewsplitter-48 : 276" />px; }
- #mailview-bottom { top: <roundcube:exp expression="!empty(cookie:mailviewsplitter) ? cookie:mailviewsplitter+6 : 330" />px; height: auto; }
+ #mailview-bottom { top: <roundcube:exp expression="!empty(cookie:mailviewsplitter) ? cookie:mailviewsplitter+6 : 330" />px; height: auto; display: block; }
#mailpreviewframe { display: block; }
<roundcube:endif />
</style>
@@ -116,8 +116,6 @@
<roundcube:object name="messagecontentframe" id="messagecontframe" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
-<roundcube:object name="message" id="message" class="statusbar" />
-
</div><!-- end mailview-bottom -->
</div><!-- end mailview-right -->
diff --git a/skins/larry/templates/message.html b/skins/larry/templates/message.html
index 6937b00af..e63705f76 100644
--- a/skins/larry/templates/message.html
+++ b/skins/larry/templates/message.html
@@ -84,8 +84,6 @@
</div>
</div>
-<roundcube:object name="message" id="message" class="statusbar" />
-
</div><!-- end mailview-right -->
</div><!-- end mainscreen -->
diff --git a/skins/larry/templates/messageerror.html b/skins/larry/templates/messageerror.html
index a735d47f2..dbe373a12 100644
--- a/skins/larry/templates/messageerror.html
+++ b/skins/larry/templates/messageerror.html
@@ -38,8 +38,6 @@
<div id="messagecontent" class="watermark"></div>
-<roundcube:object name="message" id="message" class="statusbar" />
-
</div><!-- end mailview-right -->
</div><!-- end mainscreen -->
diff --git a/skins/larry/templates/messagepart.html b/skins/larry/templates/messagepart.html
index d0e3a808d..0ec935873 100644
--- a/skins/larry/templates/messagepart.html
+++ b/skins/larry/templates/messagepart.html
@@ -27,7 +27,6 @@
<div class="iframebox">
<roundcube:object name="messagePartFrame" id="messagepartframe" frameborder="0" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/templates/plugin.html b/skins/larry/templates/plugin.html
index 341f604ac..5b86563ab 100644
--- a/skins/larry/templates/plugin.html
+++ b/skins/larry/templates/plugin.html
@@ -16,7 +16,6 @@
<div id="pluginbody" class="uibox contentbox">
<roundcube:object name="plugin.body" />
-<roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/templates/responses.html b/skins/larry/templates/responses.html
index fb40048c8..8e6884539 100644
--- a/skins/larry/templates/responses.html
+++ b/skins/larry/templates/responses.html
@@ -28,7 +28,6 @@
<div class="iframebox">
<roundcube:object name="responseframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/templates/settings.html b/skins/larry/templates/settings.html
index 427e0a4f5..08df7686e 100644
--- a/skins/larry/templates/settings.html
+++ b/skins/larry/templates/settings.html
@@ -24,7 +24,6 @@
<div class="iframebox">
<roundcube:object name="prefsframe" id="preferences-frame" style="width:100%; height:100%" frameborder="0" src="/watermark.html" />
</div>
- <roundcube:object name="message" id="message" class="statusbar" />
</div>
</div>
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index 0fd0241f7..75dcba8ec 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -106,14 +106,17 @@ function rcube_mail_ui()
// add menu link for each attachment
$('#attachment-list > li').each(function() {
- $(this).append($('<a class="drop">').click(function() { attachmentmenu(this); }));
+ $(this).append($('<a class="drop"></a>').click(function() { attachmentmenu(this); }));
});
}
else if (rcmail.env.action == 'compose') {
- rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); });
+ rcmail.addEventListener('aftertoggle-editor', function(e){
+ window.setTimeout(function(){ layout_composeview() }, 200);
+ if (e && e.mode)
+ $("select[name='editorSelector']").val(e.mode);
+ });
rcmail.addEventListener('aftersend-attachment', show_uploadform);
rcmail.addEventListener('add-recipient', function(p){ show_header_row(p.field, true); });
- layout_composeview();
// Show input elements with non-empty value
var f, v, field, fields = ['cc', 'bcc', 'replyto', 'followupto'];
@@ -133,6 +136,10 @@ function rcube_mail_ui()
return false;
}).css('cursor', 'pointer');
+ // adjust hight when textarea starts to scroll
+ $("textarea[name='_to'], textarea[name='_cc'], textarea[name='_bcc']").change(function(e){ adjust_compose_editfields(this); }).change();
+ rcmail.addEventListener('autocomplete_insert', function(p){ adjust_compose_editfields(p.field); });
+
// toggle compose options if opened in new window and they were visible before
var opener_rc = rcmail.opener();
if (opener_rc && opener_rc.env.action == 'compose' && $('#composeoptionstoggle', opener.document).hasClass('remove'))
@@ -366,6 +373,10 @@ function rcube_mail_ui()
*/
function message_displayed(p)
{
+ var siblings = $(p.object).siblings('div');
+ if (siblings.length)
+ $(p.object).insertBefore(siblings.first());
+
// show a popup dialog on errors
if (p.type == 'error' && rcmail.env.task != 'login') {
if (me.message_timer) {
@@ -392,12 +403,12 @@ function rcube_mail_ui()
me.messagedialog.dialog('destroy').hide();
},
position: ['center', pos.top],
- hide: { effect:'drop', direction:'down' },
+ hide: { effect:'drop', direction:'right' },
width: 420,
minHeight: 90
}).show();
- me.message_timer = window.setTimeout(function(){ me.messagedialog.dialog('close'); }, Math.max(2000, p.timeout / 2));
+ me.message_timer = window.setTimeout(function(){ me.messagedialog.dialog('close'); }, Math.max(3000, p.timeout / 2));
}
}
@@ -428,6 +439,16 @@ function rcube_mail_ui()
// STUB
}
+ function adjust_compose_editfields(elem)
+ {
+ if (elem.nodeName == 'TEXTAREA') {
+ var $elem = $(elem), line_height = 14, // hard-coded because some browsers only provide the outer height in elem.clientHeight
+ content_height = elem.scrollHeight,
+ rows = elem.value.length > 80 && content_height > line_height*1.5 ? 2 : 1;
+ $elem.css('height', (line_height*rows) + 'px');
+ layout_composeview();
+ }
+ }
function layout_composeview()
{
@@ -592,7 +613,7 @@ function rcube_mail_ui()
if (visible) {
$('#mailview-top').removeClass('fullheight').css({ bottom:'auto' });
- $('#mailview-bottom').css({ height:'auto' });
+ $('#mailview-bottom').css({ height:'auto' }).show();
rcmail.env.contentframe = 'messagecontframe';
if (uid = rcmail.message_list.get_single_selection())
@@ -610,8 +631,8 @@ function rcube_mail_ui()
rcmail.env.contentframe = null;
rcmail.show_contentframe(false);
- $('#mailview-top').addClass('fullheight').css({ height:'auto', bottom:'28px' });
- $('#mailview-bottom').css({ top:'auto', height:'26px' });
+ $('#mailview-top').addClass('fullheight').css({ height:'auto', bottom:'0px' });
+ $('#mailview-bottom').css({ top:'auto', height:'0px' }).hide();
if (mailviewsplit.handle)
mailviewsplit.handle.hide();
diff --git a/temp/.gitignore b/temp/.gitignore
new file mode 100644
index 000000000..f59ec20aa
--- /dev/null
+++ b/temp/.gitignore
@@ -0,0 +1 @@
+* \ No newline at end of file
diff --git a/temp/.htaccess b/temp/.htaccess
deleted file mode 100644
index 8e6a345dc..000000000
--- a/temp/.htaccess
+++ /dev/null
@@ -1,2 +0,0 @@
-Order allow,deny
-Deny from all \ No newline at end of file
diff --git a/tests/Framework/Browser.php b/tests/Framework/Browser.php
index 832d4bf14..a042572a8 100644
--- a/tests/Framework/Browser.php
+++ b/tests/Framework/Browser.php
@@ -21,7 +21,7 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
/**
* @dataProvider browsers
*/
- function test_browser($useragent, $opera, $chrome, $ie, $ns, $ns4, $khtml, $safari, $mz)
+ function test_browser($useragent, $opera, $chrome, $ie, $ns, $safari, $mz)
{
$object = $this->getBrowser($useragent);
@@ -30,8 +30,6 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
$this->assertEquals($chrome, $object->chrome, 'Check for Chrome failed');
$this->assertEquals($ie, $object->ie, 'Check for IE failed');
$this->assertEquals($ns, $object->ns, 'Check for NS failed');
- $this->assertEquals($ns4, $object->ns4, 'Check for NS4 failed');
- $this->assertEquals($khtml, $object->khtml, 'Check for khtml failed');
$this->assertEquals($safari, $object->safari, 'Check for Safari failed');
$this->assertEquals($mz, $object->mz, 'Check for MZ failed');
}
@@ -132,7 +130,7 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
function browsers()
{
- return $this->extractDataSet(array('isOpera','isChrome','isIE','isNS','isNS4','isKHTML','isSafari','isMZ'));
+ return $this->extractDataSet(array('isOpera','isChrome','isIE','isNS','isSafari','isMZ'));
}
function useragents()
@@ -149,8 +147,6 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
'isChrome' => false, //isChrome
'isIE' => false, //isIE
'isNS' => false, //isNS
- 'isNS4' => false, //isNS4
- 'isKHTML' => false, //isKHTML
'isSafari' => false, //isSafari
'isMZ' => true, //isMZ
'lang' => 'en-US', //lang
@@ -169,8 +165,6 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
'isChrome' => false, //isChrome
'isIE' => false, //isIE
'isNS' => false, //isNS
- 'isNS4' => false, //isNS4
- 'isKHTML' => false, //isKHTML
'isSafari' => false, //isSafari
'isMZ' => true, //isMZ
'lang' => 'en-US', //lang
@@ -181,7 +175,7 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
'Chrome Mac' => array(
'useragent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3',
- 'version' => '5', //Version
+ 'version' => '6', //Version
'isWin' => false, //isWindows
'isLinux' => false,
'isMac' => true, //isMac
@@ -190,8 +184,6 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
'isChrome' => true, //isChrome
'isIE' => false, //isIE
'isNS' => false, //isNS
- 'isNS4' => false, //isNS4
- 'isKHTML' => true, //isKHTML
'isSafari' => false, //isSafari
'isMZ' => false, //isMZ
'lang' => 'en-US', //lang
@@ -199,6 +191,25 @@ class Framework_Browser extends PHPUnit_Framework_TestCase
'canPNGALPHA' => false, //canPNGALPHA
'canIMGDATA' => true, //canIMGDATA
),
+
+ 'IE 11' => array(
+ 'useragent' => 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko',
+ 'version' => '11.0', //Version
+ 'isWin' => true, //isWindows
+ 'isLinux' => false,
+ 'isMac' => false, //isMac
+ 'isUnix' => false, //isUnix
+ 'isOpera' => false, //isOpera
+ 'isChrome' => false, //isChrome
+ 'isIE' => true, //isIE
+ 'isNS' => false, //isNS
+ 'isSafari' => false, //isSafari
+ 'isMZ' => false, //isMZ
+ 'lang' => '', //lang
+ 'hasDOM' => true, //hasDOM
+ 'canPNGALPHA' => true, //canPNGALPHA
+ 'canIMGDATA' => false, //canIMGDATA
+ ),
);
}