diff options
-rw-r--r-- | .gitignore | 7 | ||||
-rw-r--r-- | CHANGELOG | 2 | ||||
-rw-r--r-- | composer.json-dist | 33 | ||||
-rw-r--r-- | config/main.inc.php.dist | 10 | ||||
-rw-r--r-- | program/include/rcube_imap.php | 5 | ||||
-rw-r--r-- | program/include/rcube_output_html.php | 183 | ||||
-rw-r--r-- | program/js/app.js | 19 | ||||
-rw-r--r-- | program/steps/mail/compose.inc | 5 | ||||
-rw-r--r-- | program/steps/mail/func.inc | 16 | ||||
-rw-r--r-- | program/steps/mail/list.inc | 36 | ||||
-rw-r--r-- | program/steps/mail/sendmail.inc | 11 | ||||
-rw-r--r-- | program/steps/settings/func.inc | 3 | ||||
-rw-r--r-- | skins/classic/templates/compose.html | 4 | ||||
-rw-r--r-- | skins/larry/mail.css | 5 | ||||
-rw-r--r-- | skins/larry/templates/compose.html | 2 |
15 files changed, 235 insertions, 106 deletions
diff --git a/.gitignore b/.gitignore index f55131f8c..c410c9b8b 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,9 @@ logs/* temp/* config/* -plugins/*/config.inc.php
\ No newline at end of file +plugins/*/config.inc.php + +# composer-related +composer.phar +composer.lock +vendor @@ -1,6 +1,8 @@ CHANGELOG Roundcube Webmail =========================== +- Add option to disable saving sent mail in Sent folder - no_save_sent_messages (#1488686) +- Fix handling dont_override with message_sort_col and message_sort_order settings (#1488760) - Fix HTML part detection in messages with attachments (#1488769) - Fix bug where wrong words were highlighted on spell-before-send check - Fix handling of URLs with asterisk characters (#1488759) diff --git a/composer.json-dist b/composer.json-dist new file mode 100644 index 000000000..104b572c4 --- /dev/null +++ b/composer.json-dist @@ -0,0 +1,33 @@ +{ + "name": "roundcube/roundcubemail", + "description": "The Roundcube Webmail suite", + "license": "GPL-3.0", + "repositories": [ + { + "packagist": false + }, + { + "type": "pear", + "url": "http://pear.php.net/" + }, + { + "type": "package", + "package": { + "name": "Net_SMTP", + "version": "dev-master", + "source": { + "url": "http://github.com/pear/Net_SMTP", + "type": "git", + "reference": "master" + } + } + } + ], + "require" : { + "pear-pear/Mail_Mime": ">=1.8.1", + "pear-pear/Mail_mimeDecode": ">=1.5.5", + "Net_SMTP": "dev-master", + "pear-pear/Net_IDNA2": ">=0.1.1", + "pear-pear/Auth_SASL": ">=1.0.6" + } +} diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 770d9f4d8..2ee4ccd27 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -182,6 +182,9 @@ $rcmail_config['smtp_timeout'] = 0; // ONLY ENABLE IT IF YOU'RE REALLY SURE WHAT YOU'RE DOING! $rcmail_config['enable_installer'] = false; +// don't allow these settings to be overriden by the user +$rcmail_config['dont_override'] = array(); + // provide an URL where a user can get support for this Roundcube installation // PLEASE DO NOT LINK TO THE ROUNDCUBE.NET WEBSITE HERE! $rcmail_config['support_url'] = ''; @@ -349,9 +352,6 @@ $rcmail_config['line_length'] = 72; // send plaintext messages as format=flowed $rcmail_config['send_format_flowed'] = true; -// don't allow these settings to be overriden by the user -$rcmail_config['dont_override'] = array(); - // Set identities access level: // 0 - many identities with possibility to edit all params // 1 - many identities with possibility to edit all params but not email address @@ -379,6 +379,10 @@ $rcmail_config['contact_photo_size'] = 160; // Enable DNS checking for e-mail address validation $rcmail_config['email_dns_check'] = false; +// Disables saving sent messages in Sent folder (like gmail) (Default: false) +// Note: useful when SMTP server stores sent mail in user mailbox +$rcmail_config['no_save_sent_messages'] = false; + // ---------------------------------- // PLUGINS // ---------------------------------- diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 1e6cf360b..a89fd164d 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -4119,6 +4119,11 @@ class rcube_imap extends rcube_storage return $this->delete_folder($folder); } + function clear_mailbox($folder = null) + { + return $this->clear_folder($folder); + } + public function mailbox_exists($folder, $subscription=false) { return $this->folder_exists($folder, $subscription); diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 6138e2a30..2e3cd506d 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -33,7 +33,7 @@ class rcube_output_html extends rcube_output protected $js_env = array(); protected $js_labels = array(); protected $js_commands = array(); - protected $plugin_skin_path; + protected $skin_paths = array(); protected $template_name; protected $scripts_path = ''; protected $script_files = array(); @@ -161,9 +161,27 @@ class rcube_output_html extends rcube_output $this->config->set('skin_path', $skin_path); + // register skin path(s) + $this->skin_paths = array(); + $this->load_skin($skin_path); + return $valid; } + /** + * Helper method to recursively read skin meta files and register search paths + */ + private function load_skin($skin_path) + { + $this->skin_paths[] = $skin_path; + + // read meta file and check for dependecies + $meta = @json_decode(@file_get_contents($skin_path.'/meta.json'), true); + if ($meta['extends'] && is_dir('skins/' . $meta['extends'])) { + $this->load_skin('skins/' . $meta['extends']); + } + } + /** * Check if a specific template exists @@ -173,8 +191,14 @@ class rcube_output_html extends rcube_output */ public function template_exists($name) { - $filename = $this->config->get('skin_path') . '/templates/' . $name . '.html'; - return (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name])); + $found = false; + foreach ($this->skin_paths as $skin_path) { + $filename = $skin_path . '/templates/' . $name . '.html'; + $found = (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name])); + if ($found) + break; + } + return $found; } @@ -359,41 +383,60 @@ class rcube_output_html extends rcube_output */ function parse($name = 'main', $exit = true, $write = true) { - $skin_path = $this->config->get('skin_path'); $plugin = false; $realname = $name; - $temp = explode('.', $name, 2); - - $this->plugin_skin_path = null; - $this->template_name = $realname; + $this->template_name = $realname; + $temp = explode('.', $name, 2); if (count($temp) > 1) { $plugin = $temp[0]; $name = $temp[1]; $skin_dir = $plugin . '/skins/' . $this->config->get('skin'); - $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir; - // fallback to default skin - if (!is_dir($skin_path)) { + // apply skin search escalation list to plugin directory + $plugin_skin_paths = array(); + foreach ($this->skin_paths as $skin_path) { + $plugin_skin_paths[] = $this->app->plugins->dir . $plugin . '/' . $skin_path; + } + + // add fallback to default skin + if (is_dir($this->app->plugins->dir . $plugin . '/skins/default')) { $skin_dir = $plugin . '/skins/default'; - $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir; + $plugin_skin_paths[] = $this->app->plugins->dir . $skin_dir; } + + // add plugin skin paths to search list + $this->skin_paths = array_merge($plugin_skin_paths, $this->skin_paths); } - $path = "$skin_path/templates/$name.html"; + // find skin template + $path = false; + foreach ($this->skin_paths as $skin_path) { + $path = "$skin_path/templates/$name.html"; - if (!is_readable($path) && $this->deprecated_templates[$realname]) { - $path = "$skin_path/templates/".$this->deprecated_templates[$realname].".html"; - if (is_readable($path)) - rcube::raise_error(array('code' => 502, 'type' => 'php', + // fallback to deprecated template names + if (!is_readable($path) && $this->deprecated_templates[$realname]) { + $path = "$skin_path/templates/" . $this->deprecated_templates[$realname] . ".html"; + rcube::raise_error(array( + 'code' => 502, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Using deprecated template '".$this->deprecated_templates[$realname] - ."' in $skin_path/templates. Please rename to '".$realname."'"), - true, false); + 'message' => "Using deprecated template '" . $this->deprecated_templates[$realname] + . "' in $skin_path/templates. Please rename to '$realname'"), + true, false); + } + + if (is_readable($path)) { + $this->config->set('skin_path', $skin_path); + $this->base_path = $skin_path; + break; + } + else { + $path = false; + } } // read template file - if (($templ = @file_get_contents($path)) === false) { + if (!$path || ($templ = @file_get_contents($path)) === false) { rcube::raise_error(array( 'code' => 501, 'type' => 'php', @@ -421,7 +464,7 @@ class rcube_output_html extends rcube_output $output = $hook['content']; unset($hook['content']); - $output = $this->parse_with_globals($output); + $output = $this->parse_with_globals($this->fix_paths($output)); // make sure all <form> tags have a valid request token $output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output); @@ -493,7 +536,7 @@ class rcube_output_html extends rcube_output public function abs_url($str) { if ($str[0] == '/') - return $this->config->get('skin_path') . $str; + return $this->base_path . $str; else return $str; } @@ -527,7 +570,7 @@ class rcube_output_html extends rcube_output { $GLOBALS['__version'] = html::quote(RCMAIL_VERSION); $GLOBALS['__comm_path'] = html::quote($this->app->comm_path); - $GLOBALS['__skin_path'] = html::quote($this->config->get('skin_path')); + $GLOBALS['__skin_path'] = html::quote($this->base_path); return preg_replace_callback('/\$(__[a-z0-9_\-]+)/', array($this, 'globals_callback'), $input); @@ -544,6 +587,43 @@ class rcube_output_html extends rcube_output /** + * Correct absolute paths in images and other tags + * add timestamp to .js and .css filename + */ + protected function fix_paths($output) + { + return preg_replace_callback( + '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', + array($this, 'file_callback'), $output); + } + + + /** + * Callback function for preg_replace_callback in write() + * + * @return string Parsed string + */ + protected function file_callback($matches) + { + $file = $matches[3]; + + // correct absolute paths + if ($file[0] == '/') { + $file = $this->base_path . $file; + } + + // add file modification timestamp + if (preg_match('/\.(js|css)$/', $file)) { + if ($fs = @filemtime($file)) { + $file .= '?s=' . $fs; + } + } + + return $matches[1] . '=' . $matches[2] . $file . $matches[4]; + } + + + /** * Public wrapper to dipp into template parsing. * * @param string $input @@ -705,6 +785,9 @@ class rcube_output_html extends rcube_output // show a label case 'label': + if ($attrib['expression']) + $attrib['name'] = eval("return " . $this->parse_expression($attrib['expression']) .";"); + if ($attrib['name'] || $attrib['command']) { // @FIXME: 'noshow' is useless, remove? if ($attrib['noshow']) { @@ -736,8 +819,17 @@ class rcube_output_html extends rcube_output // include a file case 'include': - if (!$this->plugin_skin_path || !is_file($path = realpath($this->plugin_skin_path . $attrib['file']))) - $path = realpath(($attrib['skin_path'] ? $attrib['skin_path'] : $this->config->get('skin_path')).$attrib['file']); + $old_base_path = $this->base_path; + $skin_paths = $this->skin_paths; + if ($attrib['skin_path']) + array_unshift($skin_paths, $attrib['skin_path']); + foreach ($skin_paths as $skin_path) { + $path = realpath($skin_path . $attrib['file']); + if (is_file($path)) { + $this->base_path = $skin_path; + break; + } + } if (is_readable($path)) { if ($this->config->get('skin_include_php')) { @@ -747,14 +839,16 @@ class rcube_output_html extends rcube_output $incl = file_get_contents($path); } $incl = $this->parse_conditions($incl); - return $this->parse_xml($incl); + $incl = $this->parse_xml($incl); + $incl = $this->fix_paths($incl); + $this->base_path = $old_base_path; + return $incl; } break; case 'plugin.include': $hook = $this->app->plugins->exec_hook("template_plugin_include", $attrib); return $hook['content']; - break; // define a container block case 'container': @@ -1234,14 +1328,6 @@ class rcube_output_html extends rcube_output $output = substr_replace($output, $css, $pos, 0); } - $this->base_path = $base_path; - - // correct absolute paths in images and other tags - // add timestamp to .js and .css filename - $output = preg_replace_callback( - '!(src|href|background)=(["\']?)([a-z0-9/_.-]+)(["\'\s>])!i', - array($this, 'file_callback'), $output); - // trigger hook with final HTML content to be sent $hook = $this->app->plugins->exec_hook("send_page", array('content' => $output)); if (!$hook['abort']) { @@ -1256,31 +1342,6 @@ class rcube_output_html extends rcube_output /** - * Callback function for preg_replace_callback in write() - * - * @return string Parsed string - */ - protected function file_callback($matches) - { - $file = $matches[3]; - - // correct absolute paths - if ($file[0] == '/') { - $file = $this->base_path . $file; - } - - // add file modification timestamp - if (preg_match('/\.(js|css)$/', $file)) { - if ($fs = @filemtime($file)) { - $file .= '?s=' . $fs; - } - } - - return $matches[1] . '=' . $matches[2] . $file . $matches[4]; - } - - - /** * Returns iframe object, registers some related env variables * * @param array $attrib HTML attributes diff --git a/program/js/app.js b/program/js/app.js index 06eb9295c..8296400ba 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -594,12 +594,11 @@ function rcube_webmail() break; case 'sort': - var sort_order, sort_col = props; + var sort_order = this.env.sort_order, + sort_col = !this.env.disabled_sort_col ? props : this.env.sort_col; - if (this.env.sort_col==sort_col) - sort_order = this.env.sort_order=='ASC' ? 'DESC' : 'ASC'; - else - sort_order = 'ASC'; + if (!this.env.disabled_sort_order) + sort_order = this.env.sort_col == sort_col && sort_order == 'ASC' ? 'DESC' : 'ASC'; // set table header and update env this.set_list_sorting(sort_col, sort_order); @@ -5733,13 +5732,11 @@ function rcube_webmail() col = this.env.coltypes[n]; if ((cell = thead.rows[0].cells[n]) && (col == 'from' || col == 'to' || col == 'fromto')) { cell.id = 'rcm'+col; + $('span,a', cell).text(this.get_label(col == 'fromto' ? smart_col : col)); // if we have links for sorting, it's a bit more complicated... - if (cell.firstChild && cell.firstChild.tagName.toLowerCase()=='a') { - cell = cell.firstChild; - cell.onclick = function(){ return rcmail.command('sort', this.__col, this); }; - cell.__col = col; - } - cell.innerHTML = this.get_label(col == 'fromto' ? smart_col : col); + $('a', cell).click(function(){ + return rcmail.command('sort', this.id.replace(/^rcm/, ''), this); + }); } } } diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 691eca2db..7ac9a8d39 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -1528,6 +1528,11 @@ function rcmail_check_sent_folder($folder, $create=false) { global $RCMAIL; + // we'll not save the message, so it doesn't matter + if ($RCMAIL->config->get('no_save_sent_messages')) { + return true; + } + if ($RCMAIL->storage->folder_exists($folder, true)) { return true; } diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 39bccac16..4302bf9af 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -381,6 +381,8 @@ function rcmail_js_message_list($a_headers, $insert_top=FALSE, $a_show_cols=null */ function rcmail_message_list_head($attrib, $a_show_cols) { + global $RCMAIL; + $skin_path = $_SESSION['skin_path']; $image_tag = html::img(array('src' => "%s%s", 'alt' => "%s")); @@ -388,8 +390,18 @@ function rcmail_message_list_head($attrib, $a_show_cols) $sort_col = $_SESSION['sort_col']; $sort_order = $_SESSION['sort_order']; + $dont_override = (array)$RCMAIL->config->get('dont_override'); + $disabled_sort = in_array('message_sort_col', $dont_override); + $disabled_order = in_array('message_sort_order', $dont_override); + + $RCMAIL->output->set_env('disabled_sort_col', $disabled_sort); + $RCMAIL->output->set_env('disabled_sort_order', $disabled_order); + // define sortable columns - $a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc'); + if ($disabled_sort) + $a_sort_cols = $sort_col && !$disabled_order ? array($sort_col) : array(); + else + $a_sort_cols = array('subject', 'date', 'from', 'to', 'fromto', 'size', 'cc'); if (!empty($attrib['optionsmenuicon'])) { $onclick = 'return ' . JS_OBJECT_NAME . ".command('menu-open', 'messagelistmenu')"; @@ -439,7 +451,7 @@ function rcmail_message_list_head($attrib, $a_show_cols) else if ($col_name[0] != '<') $col_name = '<span class="' . $col .'">' . $col_name . '</span>'; - $sort_class = $col == $sort_col ? " sorted$sort_order" : ''; + $sort_class = $col == $sort_col && !$disabled_order ? " sorted$sort_order" : ''; $class_name = $col.$sort_class; // put it all together diff --git a/program/steps/mail/list.inc b/program/steps/mail/list.inc index b5d579d7d..b433f81fc 100644 --- a/program/steps/mail/list.inc +++ b/program/steps/mail/list.inc @@ -23,27 +23,33 @@ if (!$OUTPUT->ajax_call) { return; } +$save_arr = array(); +$dont_override = (array) $RCMAIL->config->get('dont_override'); + // is there a sort type for this request? -if ($sort = get_input_value('_sort', RCUBE_INPUT_GET)) -{ +if ($sort = get_input_value('_sort', RCUBE_INPUT_GET)) { // yes, so set the sort vars list($sort_col, $sort_order) = explode('_', $sort); // set session vars for sort (so next page and task switch know how to sort) - $save_arr = array(); - $_SESSION['sort_col'] = $save_arr['message_sort_col'] = $sort_col; - $_SESSION['sort_order'] = $save_arr['message_sort_order'] = $sort_order; + if (!in_array('message_sort_col', $dont_override)) { + $_SESSION['sort_col'] = $save_arr['message_sort_col'] = $sort_col; + } + if (!in_array('message_sort_order', $dont_override)) { + $_SESSION['sort_order'] = $save_arr['message_sort_order'] = $sort_order; + } } // is there a set of columns for this request? -if ($cols = get_input_value('_cols', RCUBE_INPUT_GET)) -{ - $save_arr = array(); - $save_arr['list_cols'] = explode(',', $cols); +if ($cols = get_input_value('_cols', RCUBE_INPUT_GET)) { + if (!in_array('list_cols', $dont_override)) { + $save_arr['list_cols'] = explode(',', $cols); + } } -if ($save_arr) +if (!empty($save_arr)) { $RCMAIL->user->save_prefs($save_arr); +} $mbox_name = $RCMAIL->storage->get_folder(); $threading = (bool) $RCMAIL->storage->get_threading(); @@ -52,8 +58,7 @@ $threading = (bool) $RCMAIL->storage->get_threading(); $RCMAIL->storage->folder_sync($mbox_name); // initialize searching result if search_filter is used -if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') -{ +if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') { $search_request = md5($mbox_name.$_SESSION['search_filter']); $RCMAIL->storage->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET, rcmail_sort_column()); $_SESSION['search'] = $RCMAIL->storage->get_search_set(); @@ -76,7 +81,6 @@ else if (empty($_REQUEST['_search']) && isset($_SESSION['search'])) { $RCMAIL->session->remove('search'); } - // empty result? we'll skip UNSEEN counting in rcmail_send_unread_count() if (empty($search_request) && empty($a_headers)) { $unseen = 0; @@ -96,10 +100,10 @@ $OUTPUT->command('set_mailboxname', rcmail_get_mailbox_name_text()); // add message rows rcmail_js_message_list($a_headers, FALSE, $cols); -if (isset($a_headers) && count($a_headers)) -{ - if ($search_request) +if (isset($a_headers) && count($a_headers)) { + if ($search_request) { $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $count)); + } } else { // handle IMAP errors (e.g. #1486905) diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 9fe510794..4fac872d1 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -49,7 +49,7 @@ if (!$savedraft) { if(!empty($CONFIG['sendmail_delay'])) { $wait_sec = time() - intval($CONFIG['sendmail_delay']) - intval($CONFIG['last_message_time']); - if($wait_sec < 0) { + if ($wait_sec < 0) { $OUTPUT->show_message('senttooquickly', 'error', array('sec' => $wait_sec * -1)); $OUTPUT->send('iframe'); } @@ -676,19 +676,18 @@ if (!$savedraft) $smtp_error, $mailbody_file, $smtp_opts); // return to compose page if sending failed - if (!$sent) - { + if (!$sent) { // remove temp file if ($mailbody_file) { unlink($mailbody_file); - } + } if ($smtp_error) $OUTPUT->show_message($smtp_error['label'], 'error', $smtp_error['vars']); else $OUTPUT->show_message('sendingfailed', 'error'); $OUTPUT->send('iframe'); - } + } // save message sent time if (!empty($CONFIG['sendmail_delay'])) @@ -706,7 +705,7 @@ if (!$savedraft) // Determine which folder to save message if ($savedraft) $store_target = $CONFIG['drafts_mbox']; -else +else if (!$RCMAIL->config->get('no_save_sent_messages')) $store_target = isset($_POST['_store_target']) ? get_input_value('_store_target', RCUBE_INPUT_POST) : $CONFIG['sent_mbox']; if ($store_target) { diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index c60d17f30..ba42e81f7 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -198,9 +198,6 @@ function rcmail_user_prefs($current=null) $select_timezone->add('(GMT ' . $offset . ') ' . strtr($tzs, '_', ' '), $tzs); } - if (is_numeric($config['timezone'])) - timezone_name_from_abbr("", $config['timezone'] * 3600, 0); - $blocks['main']['options']['timezone'] = array( 'title' => html::label($field_id, Q(rcube_label('timezone'))), 'content' => $select_timezone->show((string)$config['timezone']), diff --git a/skins/classic/templates/compose.html b/skins/classic/templates/compose.html index 1e1403e18..23998ee0f 100644 --- a/skins/classic/templates/compose.html +++ b/skins/classic/templates/compose.html @@ -143,10 +143,10 @@ </tr><tr> <td><label for="rcmcomposepriority"><roundcube:label name="priority" />:</label></td> <td><roundcube:object name="prioritySelector" form="form" id="rcmcomposepriority" /></td> - </tr><tr> + </tr><roundcube:if condition="!config:no_save_sent_messages" /><tr> <td><label><roundcube:label name="savesentmessagein" />:</label></td> <td><roundcube:object name="storetarget" maxlength="30" /></td> - </tr> + </tr><roundcube:endif /> </table> </div> diff --git a/skins/larry/mail.css b/skins/larry/mail.css index 95d76c596..76ca4edfa 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -415,12 +415,15 @@ a.iconbutton.threadmode.selected { border-left: 0; } - #messagelist tr td.size { width: 60px; text-align: right; } +#messagelist thead tr td.size { + text-align: left; +} + #messagelist tr td.fromto, #messagelist tr td.from, #messagelist tr td.to, diff --git a/skins/larry/templates/compose.html b/skins/larry/templates/compose.html index d29c1bd2a..ef49ece90 100644 --- a/skins/larry/templates/compose.html +++ b/skins/larry/templates/compose.html @@ -140,9 +140,11 @@ <span class="composeoption"> <label><roundcube:object name="dsnCheckBox" form="form" id="rcmcomposedsn" /> <roundcube:label name="dsn" /></label> </span> + <roundcube:if condition="!config:no_save_sent_messages" /> <span class="composeoption"> <label><roundcube:label name="savesentmessagein" /> <roundcube:object name="storetarget" maxlength="30" style="max-width:12em" /></label> </span> + <roundcube:endif /> <roundcube:container name="composeoptions" id="composeoptions" /> </div> </div> |