defa
2012-09-05 921408eba600a7dc51271c35480e9114cac5ecec
Merge https://github.com/roundcube/roundcubemail into crypt_hash_branch
12 files deleted
15 files added
36 files modified
1311 ■■■■ changed files
CHANGELOG 8 ●●●●● patch | view | raw | blame | history
plugins/acl/acl.php 3 ●●●● patch | view | raw | blame | history
plugins/acl/skins/larry/acl.css 4 ●●●● patch | view | raw | blame | history
plugins/acl/skins/larry/templates/table.html 8 ●●●● patch | view | raw | blame | history
plugins/managesieve/tests/Makefile 7 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/Parser.php 54 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/Tokenizer.php 33 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser.phpt 120 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_body.phpt 49 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_imapflags.phpt 28 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_include.phpt 30 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_kep14.phpt 19 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_prefix.phpt 25 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_relational.phpt 25 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_vacation.phpt 39 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parser_variables.phpt 39 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/parset_subaddress.phpt 38 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser 52 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser.out 52 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_body 17 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_imapflags 7 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_include 7 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_kep14 2 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_kep14.out 3 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_prefix 5 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_relational 6 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_subaddress 11 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_vacation 12 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/src/parser_variables 12 ●●●●● patch | view | raw | blame | history
plugins/managesieve/tests/tokenize.phpt 66 ●●●●● patch | view | raw | blame | history
plugins/password/drivers/virtualmin.php 4 ●●●● patch | view | raw | blame | history
program/include/rcube_charset.php 6 ●●●●● patch | view | raw | blame | history
program/include/rcube_imap.php 4 ●●●● patch | view | raw | blame | history
program/include/rcube_imap_generic.php 5 ●●●●● patch | view | raw | blame | history
program/include/rcube_message.php 26 ●●●● patch | view | raw | blame | history
program/include/rcube_output_html.php 31 ●●●●● patch | view | raw | blame | history
program/include/rcube_shared.inc 2 ●●●●● patch | view | raw | blame | history
program/include/rcube_storage.php 2 ●●● patch | view | raw | blame | history
program/js/app.js 95 ●●●●● patch | view | raw | blame | history
program/js/list.js 4 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 13 ●●●● patch | view | raw | blame | history
program/steps/mail/headers.inc 3 ●●●● patch | view | raw | blame | history
program/steps/settings/folders.inc 4 ●●● patch | view | raw | blame | history
program/steps/settings/save_folder.inc 5 ●●●● patch | view | raw | blame | history
skins/classic/templates/message.html 15 ●●●● patch | view | raw | blame | history
skins/classic/templates/messageerror.html 15 ●●●● patch | view | raw | blame | history
skins/larry/addressbook.css 1 ●●●● patch | view | raw | blame | history
skins/larry/ie7hacks.css 6 ●●●● patch | view | raw | blame | history
skins/larry/iehacks.css 2 ●●● patch | view | raw | blame | history
skins/larry/images/contactpic_32px.png patch | view | raw | blame | history
skins/larry/images/contactpic_48px.png patch | view | raw | blame | history
skins/larry/mail.css 90 ●●●●● patch | view | raw | blame | history
skins/larry/styles.css 6 ●●●●● patch | view | raw | blame | history
skins/larry/svggradient.php 2 ●●●●● patch | view | raw | blame | history
skins/larry/svggradients.css 2 ●●● patch | view | raw | blame | history
skins/larry/templates/message.html 63 ●●●●● patch | view | raw | blame | history
skins/larry/templates/messageerror.html 10 ●●●●● patch | view | raw | blame | history
skins/larry/templates/messagepreview.html 5 ●●●●● patch | view | raw | blame | history
skins/larry/ui.js 56 ●●●● patch | view | raw | blame | history
tests/Framework/Shared.php 43 ●●●●● patch | view | raw | blame | history
tests/HtmlToText.php 4 ●●●● patch | view | raw | blame | history
tests/MailFunc.php 2 ●●● patch | view | raw | blame | history
tests/phpunit.xml 4 ●●●● patch | view | raw | blame | history
CHANGELOG
@@ -1,6 +1,14 @@
CHANGELOG Roundcube Webmail
===========================
- Fix focus issue in IE when selecting message row (#1488620)
- Remove (too big) min-width on mail screen
- Add full headers view in message preview window (#1488538)
- Fix message display page issues - unified with message preview (#1488590, #1488642)
- Fix displaying all headers when they contain malformed characters (#1488666)
- Fix decoding of HTML messages with UTF-16 charset specified (#1488654)
- Fix quota capability detection so it can be overwritten by a plugin (#1488655)
- Added template object 'frame'
- Fix identity selection on reply (#1488101)
- Add option to enable HTML editor on forwarding (#1488517)
- Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149)
plugins/acl/acl.php
@@ -233,8 +233,7 @@
        // Advanced rights
        $attrib['id'] = 'advancedrights';
        foreach ($supported as $val) {
            $id = "acl$val";
        foreach ($supported as $idx => $val) {
            $ul .= html::tag('li', null,
                $input->show('', array(
                    'name' => "acl[$val]", 'value' => $val, 'id' => $id))
plugins/acl/skins/larry/acl.css
@@ -123,3 +123,7 @@
{
  margin-left: 0.5em;
}
ul.toolbarmenu li span.delete {
  background-position: 0 -1509px;
}
plugins/acl/skins/larry/templates/table.html
@@ -3,14 +3,14 @@
    <roundcube:object name="acltable" id="acltable" class="records-table" />
</div>
<div id="acllist-footer" class="boxfooter">
    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu');return false" innerClass="inner" content="&#9881;" />
    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu', undefined, {above:1});return false" innerClass="inner" content="&#9881;" />
</div>
</div>
<div id="aclmenu" class="popupmenu">
    <ul class="toolbarmenu selectable">
        <li><roundcube:button command="acl-edit" label="edit" classAct="active" /></li>
        <li><roundcube:button command="acl-delete" label="delete" classAct="active" /></li>
    <ul class="toolbarmenu selectable iconized">
        <li><roundcube:button command="acl-edit" label="edit" class="icon" classAct="icon active" innerclass="icon edit" /></li>
        <li><roundcube:button command="acl-delete" label="delete" class="icon" classAct="icon active" innerclass="icon delete" /></li>
        <roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" />
            <li><roundcube:button name="acl-switch" id="acl-switch" label="acl.advanced" onclick="rcmail.command('acl-mode-switch')" class="active" /></li>
        <roundcube:endif />
plugins/managesieve/tests/Makefile
File was deleted
plugins/managesieve/tests/Parser.php
New file
@@ -0,0 +1,54 @@
<?php
class Parser extends PHPUnit_Framework_TestCase
{
    function setUp()
    {
        include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php';
    }
    /**
     * Sieve script parsing
     *
     * @dataProvider data_parser
     */
    function test_parser($input, $output, $message)
    {
        $script = new rcube_sieve_script($input);
        $result = $script->as_text();
        $this->assertEquals(trim($result), trim($output), $message);
    }
    /**
     * Data provider for test_parser()
     */
    function data_parser()
    {
        $dir_path = realpath(dirname(__FILE__) . '/src');
        $dir      = opendir($dir_path);
        $result   = array();
        while ($file = readdir($dir)) {
            if (preg_match('/^[a-z0-9_]+$/', $file)) {
                $input = file_get_contents($dir_path . '/' . $file);
                if (file_exists($dir_path . '/' . $file . '.out')) {
                    $output = file_get_contents($dir_path . '/' . $file . '.out');
                }
                else {
                    $output = $input;
                }
                $result[] = array(
                    'input'   => $input,
                    'output'  => $output,
                    'message' => "Error in parsing '$file' file",
                );
            }
        }
        return $result;
    }
}
plugins/managesieve/tests/Tokenizer.php
New file
@@ -0,0 +1,33 @@
<?php
class Tokenizer extends PHPUnit_Framework_TestCase
{
    function setUp()
    {
        include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php';
    }
    function data_tokenizer()
    {
        return array(
            array(1, "text: #test\nThis is test ; message;\nMulti line\n.\n;\n", '"This is test ; message;\nMulti line"'),
            array(0, '["test1","test2"]', '[["test1","test2"]]'),
            array(1, '["test"]', '["test"]'),
            array(1, '"te\\"st"', '"te\\"st"'),
            array(0, 'test #comment', '["test"]'),
            array(0, "text:\ntest\n.\ntext:\ntest\n.\n", '["test","test"]'),
            array(1, '"\\a\\\\\\"a"', '"a\\\\\\"a"'),
        );
    }
    /**
     * @dataProvider data_tokenizer
     */
    function test_tokenizer($num, $input, $output)
    {
        $res = json_encode(rcube_sieve_script::tokenize($input, $num));
        $this->assertEquals(trim($res), trim($output));
    }
}
plugins/managesieve/tests/parser.phpt
File was deleted
plugins/managesieve/tests/parser_body.phpt
File was deleted
plugins/managesieve/tests/parser_imapflags.phpt
File was deleted
plugins/managesieve/tests/parser_include.phpt
File was deleted
plugins/managesieve/tests/parser_kep14.phpt
File was deleted
plugins/managesieve/tests/parser_prefix.phpt
File was deleted
plugins/managesieve/tests/parser_relational.phpt
File was deleted
plugins/managesieve/tests/parser_vacation.phpt
File was deleted
plugins/managesieve/tests/parser_variables.phpt
File was deleted
plugins/managesieve/tests/parset_subaddress.phpt
File was deleted
plugins/managesieve/tests/src/parser
New file
@@ -0,0 +1,52 @@
require ["fileinto","reject","envelope"];
# rule:[spam]
if anyof (header :contains "X-DSPAM-Result" "Spam")
{
    fileinto "Spam";
    stop;
}
# rule:[test1]
if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld")
{
    discard;
    stop;
}
# rule:[test2]
if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
{
    fileinto "test";
    stop;
}
# rule:[comments]
if anyof (true) /* comment
 * "comment" #comment */ {
    /* comment */ stop;
# comment
}
# rule:[reject]
if size :over 5000K {
    reject "Message over 5MB size limit. Please contact me before sending this.";
}
# rule:[false]
if false # size :over 5000K
{
    stop; /* rule disabled */
}
# rule:[true]
if true
{
    stop;
}
fileinto "Test";
# rule:[address test]
if address :all :is "From" "nagios@domain.tld"
{
    fileinto "domain.tld";
    stop;
}
# rule:[envelope test]
if envelope :domain :is "From" "domain.tld"
{
    fileinto "domain.tld";
    stop;
}
plugins/managesieve/tests/src/parser.out
New file
@@ -0,0 +1,52 @@
require ["fileinto","reject","envelope"];
# rule:[spam]
if header :contains "X-DSPAM-Result" "Spam"
{
    fileinto "Spam";
    stop;
}
# rule:[test1]
if header :contains ["From","To"] "test@domain.tld"
{
    discard;
    stop;
}
# rule:[test2]
if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]")
{
    fileinto "test";
    stop;
}
# rule:[comments]
if true
{
    stop;
}
# rule:[reject]
if size :over 5000K
{
    reject "Message over 5MB size limit. Please contact me before sending this.";
}
# rule:[false]
if false # size :over 5000K
{
    stop;
}
# rule:[true]
if true
{
    stop;
}
fileinto "Test";
# rule:[address test]
if address :all :is "From" "nagios@domain.tld"
{
    fileinto "domain.tld";
    stop;
}
# rule:[envelope test]
if envelope :domain :is "From" "domain.tld"
{
    fileinto "domain.tld";
    stop;
}
plugins/managesieve/tests/src/parser_body
New file
@@ -0,0 +1,17 @@
require ["body","fileinto"];
if body :raw :contains "MAKE MONEY FAST"
{
    stop;
}
if body :content "text" :contains ["missile","coordinates"]
{
    fileinto "secrets";
}
if body :content "audio/mp3" :contains ""
{
    fileinto "jukebox";
}
if body :text :contains "project schedule"
{
    fileinto "project/schedule";
}
plugins/managesieve/tests/src/parser_imapflags
New file
@@ -0,0 +1,7 @@
require ["imap4flags"];
# rule:[imapflags]
if header :matches "Subject" "^Test$"
{
    setflag "\\Seen";
    addflag ["\\Answered","\\Deleted"];
}
plugins/managesieve/tests/src/parser_include
New file
@@ -0,0 +1,7 @@
require ["include"];
include "script.sieve";
# rule:[two]
if true
{
    include :optional "second.sieve";
}
plugins/managesieve/tests/src/parser_kep14
New file
@@ -0,0 +1,2 @@
# EDITOR Roundcube
# EDITOR_VERSION 123
plugins/managesieve/tests/src/parser_kep14.out
New file
@@ -0,0 +1,3 @@
require ["variables"];
set "EDITOR" "Roundcube";
set "EDITOR_VERSION" "123";
plugins/managesieve/tests/src/parser_prefix
New file
@@ -0,0 +1,5 @@
# this is a comment
# and the second line
require ["variables"];
set "b" "c";
plugins/managesieve/tests/src/parser_relational
New file
@@ -0,0 +1,6 @@
require ["relational","comparator-i;ascii-numeric"];
# rule:[redirect]
if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
{
    redirect "test@test.tld";
}
plugins/managesieve/tests/src/parser_subaddress
New file
@@ -0,0 +1,11 @@
require ["envelope","subaddress","fileinto"];
if envelope :user "To" "postmaster"
{
    fileinto "postmaster";
    stop;
}
if envelope :detail :is "To" "mta-filters"
{
    fileinto "mta-filters";
    stop;
}
plugins/managesieve/tests/src/parser_vacation
New file
@@ -0,0 +1,12 @@
require ["vacation"];
# rule:[test-vacation]
if header :contains "Subject" "vacation"
{
    vacation :days 1 text:
# test
test test /* test */
test
.
;
    stop;
}
plugins/managesieve/tests/src/parser_variables
New file
@@ -0,0 +1,12 @@
require ["variables"];
set "honorific" "Mr";
set "vacation" text:
Dear ${HONORIFIC} ${last_name},
I am out, please leave a message after the meep.
.
;
set :length "b" "${a}";
set :lower "b" "${a}";
set :upperfirst "b" "${a}";
set :upperfirst :lower "b" "${a}";
set :quotewildcard "b" "Rock*";
plugins/managesieve/tests/tokenize.phpt
File was deleted
plugins/password/drivers/virtualmin.php
@@ -48,6 +48,10 @@
            $pieces = explode("_", $username);
            $domain = $pieces[0];
            break;
        case 8: // domain taken from alias, username left as it was
            $email = $rcmail->user->data['alias'];
            $domain = substr(strrchr($email, "@"), 1);
            break;
        default: // username@domain
            $domain = substr(strrchr($username, "@"), 1);
        }
program/include/rcube_charset.php
@@ -181,6 +181,12 @@
        $to   = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse_charset($to);
        $from = self::parse_charset($from);
        // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654)
        // In that case we can just skip the conversion (use UTF-8)
        if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) {
            $from = 'UTF-8';
        }
        if ($from == $to || empty($str) || empty($from)) {
            return $str;
        }
program/include/rcube_imap.php
@@ -2222,6 +2222,10 @@
            $folder = $this->folder;
        }
        if (!$this->check_connection()) {
            return false;
        }
        // make sure folder exists
        if ($this->folder_exists($folder)) {
            if ($is_file) {
program/include/rcube_imap_generic.php
@@ -2538,7 +2538,7 @@
    {
        unset($this->data['APPENDUID']);
        if (!$mailbox) {
        if ($mailbox === null || $mailbox === '') {
            return false;
        }
@@ -2603,7 +2603,7 @@
    {
        unset($this->data['APPENDUID']);
        if (!$mailbox) {
        if ($mailbox === null || $mailbox === '') {
            return false;
        }
@@ -2612,6 +2612,7 @@
        if (file_exists(realpath($path))) {
            $in_fp = fopen($path, 'r');
        }
        if (!$in_fp) {
            $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading");
            return false;
program/include/rcube_message.php
@@ -52,7 +52,8 @@
    private $opt = array();
    private $parse_alternative = false;
    public $uid = null;
    public $uid;
    public $folder;
    public $headers;
    public $parts = array();
    public $mime_parts = array();
@@ -68,16 +69,21 @@
     *
     * Provide a uid, and parse message structure.
     *
     * @param string $uid The message UID.
     * @param string $uid    The message UID.
     * @param string $folder Folder name
     *
     * @see self::$app, self::$storage, self::$opt, self::$parts
     */
    function __construct($uid)
    function __construct($uid, $folder = null)
    {
        $this->uid  = $uid;
        $this->app  = rcube::get_instance();
        $this->storage = $this->app->get_storage();
        $this->folder  = strlen($folder) ? $folder : $this->storage->get_folder();
        $this->storage->set_options(array('all_headers' => true));
        // Set current folder
        $this->storage->set_folder($this->folder);
        $this->headers = $this->storage->get_message($uid);
@@ -179,10 +185,12 @@
                }
                return $fp ? true : $part->body;
            }
            // get from IMAP
            $this->storage->set_folder($this->folder);
            return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv);
        } else
            return null;
        }
    }
@@ -637,8 +645,10 @@
    function tnef_decode(&$part)
    {
        // @TODO: attachment may be huge, hadle it via file
        if (!isset($part->body))
        if (!isset($part->body)) {
            $this->storage->set_folder($this->folder);
            $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
        }
        $parts = array();
        $tnef = new tnef_decoder;
@@ -673,8 +683,10 @@
    function uu_decode(&$part)
    {
        // @TODO: messages may be huge, hadle body via file
        if (!isset($part->body))
        if (!isset($part->body)) {
            $this->storage->set_folder($this->folder);
            $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
        }
        $parts = array();
        // FIXME: line length is max.65?
program/include/rcube_output_html.php
@@ -400,7 +400,7 @@
                'line' => __LINE__,
                'file' => __FILE__,
                'message' => 'Error loading template for '.$realname
                ), true, true);
                ), true, $write);
            return false;
        }
@@ -696,6 +696,11 @@
                if ($attrib['name'] || $attrib['command']) {
                    return $this->button($attrib);
                }
                break;
            // frame
            case 'frame':
                return $this->frame($attrib);
                break;
            // show a label
@@ -1275,6 +1280,30 @@
    }
    /**
     * Returns iframe object, registers some related env variables
     *
     * @param array $attrib HTML attributes
     *
     * @return string IFRAME element
     */
    public function frame($attrib)
    {
        if (!$attrib['id']) {
            $attrib['id'] = 'rcmframe';
        }
        if (!$attrib['name']) {
            $attrib['name'] = $attrib['id'];
        }
        $this->set_env('contentframe', $attrib['id']);
        $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif');
        return html::iframe($attrib);
    }
    /*  ************* common functions delivering gui objects **************  */
program/include/rcube_shared.inc
@@ -422,7 +422,6 @@
{
    $filename = preg_replace(
        array(
            '/MDB2_(.+)/',
            '/Mail_(.+)/',
            '/Net_(.+)/',
            '/Auth_(.+)/',
@@ -430,7 +429,6 @@
            '/^utf8$/',
        ),
        array(
            'Mail/\\1',
            'Mail/\\1',
            'Net/\\1',
            'Auth/\\1',
program/include/rcube_storage.php
@@ -195,7 +195,7 @@
     */
    public function set_folder($folder)
    {
        if ($this->folder == $folder) {
        if ($this->folder === $folder) {
            return;
        }
program/js/app.js
@@ -1538,14 +1538,17 @@
    if (list.multi_selecting || !this.env.contentframe)
      return;
    if (list.get_single_selection() && window.frames && window.frames[this.env.contentframe]) {
      if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)>=0) {
        if (this.preview_timer)
          clearTimeout(this.preview_timer);
        if (this.preview_read_timer)
          clearTimeout(this.preview_read_timer);
        this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200);
      }
    if (list.get_single_selection())
      return;
    var win = this.get_frame_window(this.env.contentframe);
    if (win && win.location.href.indexOf(this.env.blankpage)>=0) {
      if (this.preview_timer)
        clearTimeout(this.preview_timer);
      if (this.preview_read_timer)
        clearTimeout(this.preview_read_timer);
      this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200);
    }
  };
@@ -1910,12 +1913,12 @@
    if (!id)
      return;
    var target = window,
    var win, target = window,
      action = preview ? 'preview': 'show',
      url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox);
    if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      target = window.frames[this.env.contentframe];
    if (preview && (win = this.get_frame_window(this.env.contentframe))) {
      target = win;
      url += '&_framed=1';
    }
@@ -1952,18 +1955,35 @@
  this.show_contentframe = function(show)
  {
    var frm, win;
    if (this.env.contentframe && (frm = $('#'+this.env.contentframe)) && frm.length) {
      if (!show && (win = window.frames[this.env.contentframe])) {
    var frame, win, name = this.env.contentframe;
    if (name && (frame = this.get_frame_element(name))) {
      if (!show && (win = this.get_frame_window(name))) {
        if (win.location && win.location.href.indexOf(this.env.blankpage)<0)
          win.location.href = this.env.blankpage;
      }
      else if (!bw.safari && !bw.konq)
        frm[show ? 'show' : 'hide']();
      }
        $(frame)[show ? 'show' : 'hide']();
    }
    if (!show && this.busy)
      this.set_busy(false, null, this.env.frame_lock);
  };
  this.get_frame_element = function(id)
  {
    var frame;
    if (id && (frame = document.getElementById(id)))
      return frame;
  };
  this.get_frame_window = function(id)
  {
    var frame = this.get_frame_element(id);
    if (frame && frame.name && window.frames)
      return window.frames[frame.name];
  };
  this.lock_frame = function()
@@ -2009,7 +2029,7 @@
  // list messages of a specific mailbox
  this.list_mailbox = function(mbox, page, sort, url)
  {
    var target = window;
    var win, target = window;
    if (typeof url != 'object')
      url = {};
@@ -2048,8 +2068,8 @@
      return;
    }
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      target = window.frames[this.env.contentframe];
    if (win = this.get_frame_window(this.env.contentframe)) {
      target = win;
      url._framed = 1;
    }
@@ -4015,7 +4035,7 @@
  this.list_contacts = function(src, group, page)
  {
    var folder, url = {},
    var win, folder, url = {},
      target = window;
    if (!src)
@@ -4047,8 +4067,8 @@
      return;
    }
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      target = window.frames[this.env.contentframe];
    if (win = this.get_frame_window(this.env.contentframe)) {
      target = win;
      url._framed = 1;
    }
@@ -4104,11 +4124,11 @@
  // load contact record
  this.load_contact = function(cid, action, framed)
  {
    var url = {}, target = window;
    var win, url = {}, target = window;
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
    if (win = this.get_frame_window(this.env.contentframe)) {
      url._framed = 1;
      target = window.frames[this.env.contentframe];
      target = win;
      this.show_contentframe(true);
      // load dummy content
@@ -4726,11 +4746,11 @@
  // load advanced search page
  this.advanced_search = function()
  {
    var url = {_form: 1, _action: 'search'}, target = window;
    var win, url = {_form: 1, _action: 'search'}, target = window;
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
    if (win = this.get_frame_window(this.env.contentframe)) {
      url._framed = 1;
      target = window.frames[this.env.contentframe];
      target = win;
      this.contact_list.clear_selection();
    }
@@ -4852,13 +4872,13 @@
  // preferences section select and load options frame
  this.section_select = function(list)
  {
    var id = list.get_single_selection(), target = window,
    var win, id = list.get_single_selection(), target = window,
      url = {_action: 'edit-prefs', _section: id};
    if (id) {
      if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      if (win = this.get_frame_window(this.env.contentframe)) {
        url._framed = 1;
        target = window.frames[this.env.contentframe];
        target = win;
      }
      this.location_href(url, target, true);
    }
@@ -4881,13 +4901,12 @@
    if (action == 'edit-identity' && (!id || id == this.env.iid))
      return false;
    var target = window,
    var win, target = window,
      url = {_action: action, _iid: id};
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
    if (win = this.get_frame_window(this.env.contentframe)) {
      url._framed = 1;
      target = window.frames[this.env.contentframe];
      document.getElementById(this.env.contentframe).style.visibility = 'inherit';
      target = win;
    }
    if (action && (id || action == 'add-identity')) {
@@ -5263,14 +5282,14 @@
  // when user select a folder in manager
  this.show_folder = function(folder, path, force)
  {
    var target = window,
    var win, target = window,
      url = '&_action=edit-folder&_mbox='+urlencode(folder);
    if (path)
      url += '&_path='+urlencode(path);
    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
      target = window.frames[this.env.contentframe];
    if (win = this.get_frame_window(this.env.contentframe)) {
      target = win;
      url += '&_framed=1';
    }
program/js/list.js
@@ -231,8 +231,8 @@
    }
  }
  // Un-focus already focused elements
  $(document.activeElement).blur();
  // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
  $(':focus:not(body)').blur();
  $('iframe').each(function() { this.blur(); });
  if (e || (e = window.event))
program/steps/mail/func.inc
@@ -90,11 +90,13 @@
  // set current mailbox and some other vars in client environment
  $OUTPUT->set_env('mailbox', $mbox_name);
  $OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize());
  $OUTPUT->set_env('quota', $RCMAIL->storage->get_capability('QUOTA'));
  $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
  $OUTPUT->set_env('threading', $threading);
  $OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD'));
  $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
  if ($RCMAIL->storage->get_capability('QUOTA')) {
    $OUTPUT->set_env('quota', true);
  }
  if ($CONFIG['delete_junk'])
    $OUTPUT->set_env('delete_junk', true);
@@ -1053,12 +1055,17 @@
  global $OUTPUT;
  $html = html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), ''));
  $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), '');
  if (!get_boolean($attrib['no-switch'])) {
    $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), '');
  }
  unset($attrib['no-switch']);
  $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
  $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
  return html::div($attrib, $html);
  return count($attrib) > 1 ? html::div($attrib, $html) : $html;
}
program/steps/mail/headers.inc
@@ -24,7 +24,8 @@
    $source = $RCMAIL->storage->get_raw_headers($uid);
    if ($source !== false) {
        $source = htmlspecialchars(trim($source));
        $source = trim(rcube_charset::clean($source));
        $source = htmlspecialchars($source);
        $source = preg_replace(
            array(
                '/\n[\t\s]+/',
program/steps/settings/folders.inc
@@ -411,8 +411,10 @@
$OUTPUT->set_pagetitle(rcube_label('folders'));
$OUTPUT->include_script('list.js');
$OUTPUT->set_env('quota', $STORAGE->get_capability('QUOTA'));
$OUTPUT->set_env('prefix_ns', $STORAGE->get_namespace('prefix'));
if ($STORAGE->get_capability('QUOTA')) {
    $OUTPUT->set_env('quota', true);
}
// add some labels to client
$OUTPUT->add_label('deletefolderconfirm', 'purgefolderconfirm', 'folderdeleting',
program/steps/settings/save_folder.inc
@@ -80,7 +80,10 @@
    }
}
if (!$error) {
if ($error) {
    $OUTPUT->command('display_message', $error, 'error');
}
else {
    $folder['name']     = $name_imap;
    $folder['oldname']  = $old_imap;
    $folder['class']    = '';
skins/classic/templates/message.html
@@ -23,11 +23,9 @@
<div id="mailboxlist-container">
<div id="mailboxlist-title" class="boxtitle"><roundcube:label name="mailboxlist" /></div>
<div class="boxlistcontent">
<roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" />
    <roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" />
</div>
<div class="boxfooter">
  <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " />
</div>
<div class="boxfooter"></div>
</div>
</div>
@@ -56,15 +54,6 @@
    var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165});
    rcmail.add_onload('mailviewsplitv.init()');
</script>
<div id="mailboxoptionsmenu" class="popupmenu">
  <ul>
    <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
    <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
    <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
    <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
  </ul>
</div>
</body>
</html>
skins/classic/templates/messageerror.html
@@ -42,11 +42,9 @@
<div id="mailboxlist-container">
<div class="boxtitle"><roundcube:label name="mailboxlist" /></div>
<div class="boxlistcontent">
<roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" />
    <roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" />
</div>
<div class="boxfooter">
  <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " />
</div>
<div class="boxfooter"></div>
</div>
</div>
@@ -62,15 +60,6 @@
    var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165});
    rcmail.add_onload('mailviewsplitv.init()');
</script>
<div id="mailboxoptionsmenu" class="popupmenu">
  <ul>
    <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
    <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
    <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
    <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
  </ul>
</div>
</body>
<roundcube:endif />
skins/larry/addressbook.css
@@ -34,7 +34,6 @@
    position: absolute;
    top: -6px;
    left: 0;
    right: 260px;
    height: 40px;
    white-space: nowrap;
    z-index: 10;
skins/larry/ie7hacks.css
@@ -29,7 +29,7 @@
.boxfooter .listbutton .inner,
.attachmentslist li a.delete,
.attachmentslist li a.cancelupload,
#messagepreviewheader .iconlink {
#messageheader .iconlink {
    /* workaround for text-indent which also offsets the background image */
    text-indent: 0;
    font-size: 0;
@@ -45,7 +45,7 @@
.pagenav a.button,
.pagenav a.button span.inner,
#messagepreviewheader .iconlink,
#messageheader .iconlink,
#uploadform a.iconlink {
    display: inline;
}
@@ -67,7 +67,7 @@
    text-align: left;
}
#messagepreviewheader .iconlink {
#messageheader .iconlink {
    color: #fff;
    height: 14px;
}
skins/larry/iehacks.css
@@ -143,7 +143,7 @@
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#005d76', endColorstr='#004558', GradientType=0);
}
#messageheader, #partheader, #composeheaders {
#partheader, #composeheaders {
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e9e9e9', GradientType=0);
}
skins/larry/images/contactpic_32px.png

skins/larry/images/contactpic_48px.png
skins/larry/mail.css
@@ -38,16 +38,16 @@
    bottom: 28px;
}
#mailview-top.fullheight {
    border-radius: 4px 4px 0 0;
}
#mailview-bottom {
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 26px;
}
#mailview-top.fullheight {
    border-radius: 4px 4px 0 0;
}
#folderlist-header {
@@ -341,7 +341,6 @@
#messagetoolbar {
    position: absolute;
    top: -6px;
    right: 390px;
    left: 0;
    height: 40px;
    white-space: nowrap;
@@ -362,7 +361,7 @@
    position: absolute;
    right: 0;
    top: 0;
    width: 240px;
    width: 400px;
}
#mailpreviewtoggle {
@@ -383,11 +382,7 @@
/*** message list ***/
#messagelist thead td:first-child {
    border-radius: 4px 0 0 0;
}
#messagelist thead td:last-child {
    border-radius: 0 4px 0 0;
    border-radius: 4px 0 0 0; /* for Chrome */
}
#messagelist tr td.attachment,
@@ -680,15 +675,14 @@
#messagecontent {
    position: absolute;
    top: 140px;
    top: 0;
    left: 0;
    width: 100%;
    bottom: 0;
    bottom: 28px;
    overflow: auto;
    border-radius: 4px 4px 0 0;
}
#messageheader,
#partheader,
#composeheaders {
    position: relative;
@@ -712,7 +706,7 @@
h3.subject {
    font-size: 14px;
    margin: 0 8em 0 0;
    margin: 0 13em 0 0;
    padding: 8px 8px 4px 8px;
    white-space: nowrap;
    overflow: hidden;
@@ -787,6 +781,7 @@
    background: -ms-linear-gradient(left, #fbfbfb 0, #e9e9e9 100%);
    background: linear-gradient(left, #fbfbfb 0, #e9e9e9 100%);
    border-right: 1px solid #dfdfdf;
    border-radius: 3px 0 0 0; /* for Opera */
}
#previewheaderstoggle .iconlink {
@@ -801,28 +796,29 @@
#previewheaderstoggle.remove .iconlink {
    top: auto;
    bottom: 5px;
    bottom: 15px;
    background-position: -5px -242px;
}
div.more-headers {
    cursor: pointer;
    height: 10px;
    background: url(images/buttons.png) center -1619px no-repeat;
#previewheaderstoggle .iconlink.allheaders {
    display: none;
}
div.hide-headers {
    background-position: center -1629px;
#previewheaderstoggle.remove .iconlink.allheaders {
    top: auto;
    bottom: 2px;
    display: inline-block;
    background-position: -27px -242px;
}
#all-headers {
    position: relative;
    margin: 0 10px;
    margin: 2px 0;
    padding: 0;
    height: 180px;
    border: 1px solid #bbb;
    background-color: #f0f0f0;
    overflow: hidden;
    border-radius: 4px;
    background: #fff;
}
#headers-source {
@@ -832,25 +828,30 @@
    left: 0;
    right: 0;
    bottom: 0;
    padding: 2px 5px;
    padding: 2px;
    overflow: auto;
    text-align: left;
    color: #333;
    color: #666;
}
#messagepreviewheader {
#messageheader {
    position: relative;
    height: auto;
    margin: 0 8px 0 0;
    padding: 0 0 6px 72px;
    padding: 0 0 0 72px;
    border-bottom: 2px solid #f0f0f0;
}
#messagepreviewheader h3.subject {
#messagecontent #messageheader {
    padding: 0 0 0 90px;
    min-height: 68px;
}
#messageheader h3.subject {
    padding: 8px 8px 2px 0;
}
#messagepreviewheader #contactphoto {
#messageheader #contactphoto {
    display: block;
    position: absolute;
    top: 11px;
@@ -862,52 +863,40 @@
    border-radius: 3px;
}
#messagepreviewheader #contactphoto img {
#messageheader #contactphoto img {
    width: 32px;
    height: auto;
    border-radius: 3px;
}
#messageheader #contactphoto {
    display: block;
    position: absolute;
    top: 40px;
    right: 10px;
#messagecontent #messageheader #contactphoto {
    top: 11px;
    left: 31px;
    width: 48px;
    height: 48px;
    overflow: hidden;
    background: url(images/contactpic_48px.png) center center no-repeat #fff;
    border-radius: 4px;
}
#messageheader #contactphoto img {
#messagecontent #messageheader #contactphoto img {
    width: 48px;
    height: auto;
    border-radius: 4px;
}
#messagepreviewheader #countcontrols,
#messageheader #countcontrols {
    position: absolute;
    top: 8px;
    right: 8px;
    width: 20em;
    right: 0;
    text-align: right;
    white-space: nowrap;
}
#messageheader .pagenav .countdisplay {
    min-width: 0;
    padding-right: 0.5em;
    white-space: nowrap;
}
#messagecontent .leftcol,
#messagepreview .leftcol {
    margin-right: 252px;
    overflow-x: auto;
}
#messagecontent .rightcol,
#messagepreview .rightcol {
    float: right;
/*
@@ -921,6 +910,7 @@
    min-height: 200px;
    background: #f0f0f0;
    padding: 8px;
    border-radius: 4px;
}
#messagebody {
skins/larry/styles.css
@@ -647,6 +647,7 @@
.uibox {
    border: 1px solid #a3a3a3;
    border-radius: 4px;
    overflow: hidden;
    box-shadow: 0 0 2px #999;
    -o-box-shadow: 0 0 2px #999;
    -webkit-box-shadow: 0 0 2px #999;
@@ -660,7 +661,7 @@
    left: 0;
    bottom: 0;
    width: 100%;
    min-width: 1150px;
    min-width: 1024px;
}
.scroller {
@@ -698,7 +699,8 @@
    left: 0;
    width: 100%;
    bottom: 0;
    overflow: auto;
    overflow-x: hidden;
    overflow-y: auto;
}
.listbox .scroller.withfooter {
skins/larry/svggradient.php
@@ -11,6 +11,8 @@
 * See http://creativecommons.org/licenses/by-sa/3.0/ for details.
 */
ini_set('error_reporting', E_ALL &~ (E_NOTICE | E_STRICT));
header('Content-Type: image/svg+xml');
header("Expires: ".gmdate("D, d M Y H:i:s", time()+864000)." GMT");
header("Cache-Control: max-age=864000");
skins/larry/svggradients.css
@@ -133,7 +133,7 @@
    background-image: url(svggradient.php?c=005d76;004558);
}
#messageheader, #partheader, #composeheaders {
#partheader, #composeheaders {
    background-image: url(svggradient.php?c=ffffff;e9e9e9);
}
skins/larry/templates/message.html
@@ -24,20 +24,38 @@
<!-- folders list -->
<div id="mailboxcontainer" class="uibox listbox">
<div class="scroller">
<roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" />
</div>
    <div class="scroller">
        <roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" />
    </div>
</div>
</div>
</div><!-- end mailview-left -->
<div id="mailview-right">
<div id="mailview-right" class="uibox" style="top: 42px">
<div id="mailview-top">
<div id="messageheader" class="uibox">
<h2 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h2>
<roundcube:object name="messageHeaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject" />
<roundcube:object name="messageFullHeaders" id="full-headers" />
<div id="messagecontent">
<div id="messageheader">
<h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></span></a>
<div id="contactphoto"><roundcube:object name="contactphoto" /></div>
<table class="headers-table" id="preview-shortheaders"><tbody><tr>
<roundcube:if condition="env:mailbox == config:drafts_mbox || env:mailbox == config:sent_mbox">
    <td class="header-title"><roundcube:label name="to" /></td>
    <td class="header from"><roundcube:object name="messageHeaders" valueOf="to" addicon="/images/addcontact.png" /></td>
<roundcube:else />
    <td class="header-title"><roundcube:label name="from" /></td>
    <td class="header from"><roundcube:object name="messageHeaders" valueOf="from" addicon="/images/addcontact.png" /></td>
<roundcube:endif />
    <td class="header-title"><roundcube:label name="date" /></td>
    <td class="header from"><roundcube:object name="messageHeaders" valueOf="date" /></td>
</tr></tbody></table>
<roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" />
<roundcube:object name="messageFullHeaders" no-switch="true" />
<!-- record navigation -->
<div id="countcontrols" class="pagenav">
@@ -46,24 +64,21 @@
    <roundcube:button command="nextmessage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextmessage" content="&amp;gt;" />
</div>
<div id="contactphoto"><roundcube:object name="contactphoto" /></div>
</div><!-- end messageheader -->
<div id="messagepreview">
    <div class="rightcol">
        <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
    </div>
    <div class="leftcol">
        <roundcube:object name="messageObjects" id="message-objects" />
        <roundcube:object name="messageBody" id="messagebody" />
    </div>
</div>
<div id="messagecontent" class="uibox">
<div class="rightcol">
<roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
</div>
<div class="leftcol">
<roundcube:object name="messageObjects" id="message-objects" />
<roundcube:object name="messageBody" id="messagebody" />
</div>
</div>
</div><!-- end messagecontent -->
</div><!-- end mailview-top -->
<div id="mailview-bottom" class="uibox">
<roundcube:object name="message" id="message" class="statusbar" />
</div>
</div><!-- end mailview-right -->
skins/larry/templates/messageerror.html
@@ -27,8 +27,6 @@
</div>
<div id="mailview-right">
<!-- toolbar -->
<div id="messagetoolbar" class="fullwidth">
    <div id="mailtoolbar" class="toolbar">
@@ -36,11 +34,11 @@
    </div>
</div>
<div id="mailview-top" class="uibox watermark"></div>
<div id="mailview-right" class="uibox" style="top: 42px">
<div id="mailview-bottom" class="uibox">
    <roundcube:object name="message" id="message" class="statusbar" />
</div>
<div id="messagecontent" class="watermark"></div>
<roundcube:object name="message" id="message" class="statusbar" />
</div><!-- end mailview-right -->
skins/larry/templates/messagepreview.html
@@ -6,10 +6,10 @@
</head>
<body class="iframe fullheight">
<div id="messagepreviewheader">
<div id="messageheader">
<h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span></a>
<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></a>
<div id="contactphoto"><roundcube:object name="contactphoto" /></div>
<table class="headers-table" id="preview-shortheaders"><tbody><tr>
@@ -25,6 +25,7 @@
</tr></tbody></table>
<roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" />
<roundcube:object name="messageFullHeaders" no-switch="true" />
<!-- record navigation -->
<div id="countcontrols" class="pagenav">
skins/larry/ui.js
@@ -74,9 +74,8 @@
      if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
        layout_messageview();
        rcmail.addEventListener('aftershow-headers', function() { layout_messageview(); });
        rcmail.addEventListener('afterhide-headers', function() { layout_messageview(); });
        $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false });
        $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false; });
        $('#headerstoggleall').click(function(e){ toggle_all_headers(this); return false; });
      }
      else if (rcmail.env.action == 'compose') {
        rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); });
@@ -162,6 +161,12 @@
        new rcube_scroller('#directorylist-content', '#directorylist-header', '#directorylist-footer');
      }
    }
    // set min-width to show all toolbar buttons
    var screen = $('.minwidth');
    if (screen.length) {
      screen.css('min-width', $('.toolbar').width() + $('#quicksearchbar').parent().width() + 20);
    }
    // turn a group of fieldsets into tabs
@@ -254,11 +259,11 @@
   */
  function resize()
  {
    if (rcmail.env.task == 'mail' && (rcmail.env.action == 'show' || rcmail.env.action == 'preview')) {
      layout_messageview();
    }
    if (rcmail.env.task == 'mail' && rcmail.env.action == 'compose') {
      layout_composeview();
    if (rcmail.env.task == 'mail') {
      if (rcmail.env.action == 'show' || rcmail.env.action == 'preview')
        layout_messageview();
      else if (rcmail.env.action == 'compose')
        layout_composeview();
    }
    // make iframe footer buttons float if scrolling is active
@@ -267,13 +272,13 @@
        body = $(document.body),
        floating = footer.hasClass('floating'),
        overflow = body.outerHeight(true) > $(window).height();
      if (overflow != floating) {
        var action = overflow ? 'addClass' : 'removeClass';
        footer[action]('floating');
        body[action]('floatingbuttons');
      }
    })
    });
  }
  /**
@@ -315,7 +320,6 @@
   */
  function layout_messageview()
  {
    $('#messagecontent').css('top', ($('#messageheader').outerHeight() + 10) + 'px');
    $('#message-objects div a').addClass('button');
    if (!$('#attachment-list li').length) {
@@ -508,13 +512,31 @@
  {
    $('#preview-shortheaders').toggle();
    var full = $('#preview-allheaders').toggle(),
      button = $('a#previewheaderstoggle');
      button = $('#previewheaderstoggle');
    if (!$('#headerstoggleall').length)
      $('#all-headers').toggle();
    // add toggle button to full headers table
    if (full.is(':visible'))
      button.attr('href', '#hide').removeClass('add').addClass('remove')
    else
      button.attr('href', '#details').removeClass('remove').addClass('add')
    if (full.is(':visible')) {
      button.attr('href', '#hide').removeClass('add').addClass('remove');
    }
    else {
      button.attr('href', '#details').removeClass('remove').addClass('add');
    }
  }
  /**
   * Show/hide all message headers
   */
  function toggle_all_headers(button)
  {
    rcmail.command('show-headers', '', button);
    $(button).remove();
    $('#previewheaderstoggle span').css({bottom: '5px'});
    return false;
  }
@@ -847,6 +869,8 @@
      // Select/unselect tab
      $('#tab'+idx).toggleClass('selected', idx==index);
    });
    resize();
  }
  /**
tests/Framework/Shared.php
@@ -158,4 +158,47 @@
        $this->assertEquals($input_str, $result_str, "Invalid array_keys_recursive() result");
    }
    /**
     * rcube_shared.inc: format_email()
     */
    function test_format_email()
    {
        $data = array(
            ''                 => '',
            'test'             => 'test',
            'test@test.tld'    => 'test@test.tld',
            'test@[127.0.0.1]' => 'test@[127.0.0.1]',
            'TEST@TEST.TLD'    => 'TEST@test.tld',
        );
        foreach ($data as $value => $expected) {
            $result = format_email($value);
            $this->assertEquals($expected, $result, "Invalid format_email() result for $value");
        }
    }
    /**
     * rcube_shared.inc: format_email_recipient()
     */
    function test_format_email_recipient()
    {
        $data = array(
            ''                          => array(''),
            'test'                      => array('test'),
            'test@test.tld'             => array('test@test.tld'),
            'test@[127.0.0.1]'          => array('test@[127.0.0.1]'),
            'TEST@TEST.TLD'             => array('TEST@TEST.TLD'),
            'TEST <test@test.tld>'      => array('test@test.tld', 'TEST'),
            '"TEST\"" <test@test.tld>'  => array('test@test.tld', 'TEST"'),
        );
        foreach ($data as $expected => $value) {
            $result = format_email_recipient($value[0], $value[1]);
            $this->assertEquals($expected, $result, "Invalid format_email_recipient()");
        }
    }
}
tests/HtmlToText.php
@@ -8,7 +8,7 @@
class HtmlToText extends PHPUnit_Framework_TestCase
{
    function data()
    function data_html2text()
    {
        return array(
            0 => array(
@@ -45,7 +45,7 @@
    }
    /**
     * @dataProvider data
     * @dataProvider data_html2text
     */
    function test_html2text($title, $in, $out)
    {
tests/MailFunc.php
@@ -8,7 +8,7 @@
class MailFunc extends PHPUnit_Framework_TestCase
{
    function __construct()
    function setUp()
    {
        // simulate environment to successfully include func.inc
        $GLOBALS['RCMAIL'] = $RCMAIL = rcmail::get_instance();
tests/phpunit.xml
@@ -29,5 +29,9 @@
            <file>HtmlToText.php</file>
            <file>MailFunc.php</file>
        </testsuite>
        <testsuite name="managesieve">
            <file>./../plugins/managesieve/tests/Parser.php</file>
            <file>./../plugins/managesieve/tests/Tokenizer.php</file>
        </testsuite>
    </testsuites>
</phpunit>