Thomas Bruederli
2012-10-17 271efe53e084779a8141228c29b5819d1acd2762
Add user settings to open message view and compose form in new windows. This natevely implements the compose_newwindow plugin functionslity and more
18 files modified
275 ■■■■ changed files
config/main.inc.php.dist 6 ●●●●● patch | view | raw | blame | history
program/include/rcube_message.php 9 ●●●●● patch | view | raw | blame | history
program/include/rcube_output_html.php 11 ●●●●● patch | view | raw | blame | history
program/js/app.js 146 ●●●● patch | view | raw | blame | history
program/localization/en_US/labels.inc 2 ●●●●● patch | view | raw | blame | history
program/steps/addressbook/func.inc 1 ●●●● patch | view | raw | blame | history
program/steps/addressbook/mailto.inc 2 ●●● patch | view | raw | blame | history
program/steps/mail/compose.inc 19 ●●●● patch | view | raw | blame | history
program/steps/mail/func.inc 17 ●●●● patch | view | raw | blame | history
program/steps/mail/show.inc 1 ●●●● patch | view | raw | blame | history
program/steps/settings/func.inc 24 ●●●●● patch | view | raw | blame | history
program/steps/settings/save_prefs.inc 2 ●●●●● patch | view | raw | blame | history
skins/larry/includes/header.html 10 ●●●● patch | view | raw | blame | history
skins/larry/mail.css 4 ●●●● patch | view | raw | blame | history
skins/larry/styles.css 4 ●●●● patch | view | raw | blame | history
skins/larry/templates/compose.html 5 ●●●●● patch | view | raw | blame | history
skins/larry/templates/message.html 10 ●●●●● patch | view | raw | blame | history
skins/larry/templates/messagepreview.html 2 ●●● patch | view | raw | blame | history
config/main.inc.php.dist
@@ -724,6 +724,12 @@
// 2 - Always show inline images
$rcmail_config['show_images'] = 0;
// open messages in new window
$rcmail_config['message_extwin'] = false;
// open message compose form in new window
$rcmail_config['compose_extwin'] = false;
// compose html formatted messages by default
// 0 - never, 1 - always, 2 - on reply to HTML message, 3 - on forward or reply to HTML message
$rcmail_config['htmleditor'] = 0;
program/include/rcube_message.php
@@ -210,15 +210,16 @@
                if (!$recursive) {
                    $level = explode('.', $part->mime_id);
                    // Level too high
                    if (count($level) > 2) {
                    // Skip if level too deep or part has a file name
                    if (count($level) > 2 || $part->filename) {
                        continue;
                    }
                    // HTML part can be on the lower level, if not...
                    if (count($level) > 1) {
                        // It can be an alternative or related message part
                        $parent = $this->mime_parts[0];
                        array_pop($level);
                        $parent = $this->mime_parts[join('.', $level)];
                        // ... parent isn't multipart/alternative or related
                        if ($parent->mimetype != 'multipart/alternative' && $parent->mimetype != 'multipart/related') {
                            continue;
                        }
program/include/rcube_output_html.php
@@ -77,6 +77,9 @@
        $this->set_skin($skin);
        $this->set_env('skin', $skin);
        if (!empty($_REQUEST['_extwin']))
          $this->set_env('extwin', 1);
        // add common javascripts
        $this->add_script('var '.rcmail::JS_OBJECT_NAME.' = new rcube_webmail();', 'head_top');
@@ -274,6 +277,8 @@
     */
    public function redirect($p = array(), $delay = 1)
    {
        if ($this->env['extwin'])
            $p['extwin'] = 1;
        $location = $this->app->url($p);
        header('Location: ' . $location);
        exit;
@@ -974,7 +979,7 @@
            else if (in_array($attrib['command'], $a_static_commands)) {
                $attrib['href'] = $this->app->url(array('action' => $attrib['command']));
            }
            else if ($attrib['command'] == 'permaurl' && !empty($this->env['permaurl'])) {
            else if (($attrib['command'] == 'permaurl' || $attrib['command'] == 'extwin') && !empty($this->env['permaurl'])) {
              $attrib['href'] = $this->env['permaurl'];
            }
        }
@@ -1319,6 +1324,10 @@
        $hiddenfield = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
        $hidden = $hiddenfield->show();
      }
      if ($this->env['extwin']) {
        $hiddenfield = new html_hiddenfield(array('name' => '_extwin', 'value' => '1'));
        $hidden = $hiddenfield->show();
      }
      if (!$content)
        $attrib['noclose'] = true;
program/js/app.js
@@ -176,10 +176,10 @@
    }
    // enable general commands
    this.enable_command('logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', 'about', 'switch-task', true);
    this.enable_command('close', 'logout', 'mail', 'addressbook', 'settings', 'save-pref', 'compose', 'undo', 'about', 'switch-task', true);
    if (this.env.permaurl)
      this.enable_command('permaurl', true);
      this.enable_command('permaurl', 'extwin', true);
    switch (this.task) {
@@ -249,7 +249,7 @@
          }
        }
        else if (this.env.action == 'compose') {
          this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor', 'list-adresses'];
          this.env.compose_commands = ['send-attachment', 'remove-attachment', 'send', 'cancel', 'toggle-editor', 'list-adresses', 'extwin'];
          if (this.env.drafts_mailbox)
            this.env.compose_commands.push('savedraft')
@@ -570,6 +570,19 @@
          parent.location.href = this.env.permaurl;
        break;
      case 'extwin':
        if (this.env.action == 'compose') {
          var prevstate = this.env.compose_extwin;
          $("input[name='_action']", this.gui_objects.messageform).val('compose');
          this.gui_objects.messageform.action = this.url('mail/compose', { _id: this.env.compose_id, _extwin: 1 });
          this.gui_objects.messageform.target = this.open_window('about:blank', 1150, 900);
          this.gui_objects.messageform.submit();
        }
        else {
          this.open_window(this.env.permaurl, 1000, 1200);
        }
        break;
      case 'menu-open':
      case 'menu-save':
        this.triggerEvent(command, {props:props});
@@ -582,10 +595,18 @@
        }
        break;
      case 'close':
        if (this.env.extwin)
          window.close();
        break;
      case 'list':
        if (props && props != '')
          this.reset_qsearch();
        if (this.task == 'mail') {
        if (this.env.action == 'compose' && this.env.extwin) {
          window.close();
        }
        else if (this.task == 'mail') {
          this.list_mailbox(props);
          this.set_button_titles();
        }
@@ -641,7 +662,7 @@
          uid = this.get_single_uid();
          if (uid && (!this.env.uid || uid != this.env.uid)) {
            if (this.env.mailbox == this.env.drafts_mailbox)
              this.goto_url('compose', { _draft_uid: uid, _mbox: this.env.mailbox }, true);
              this.open_compose_step({ _draft_uid: uid, _mbox: this.env.mailbox });
            else
              this.show_message(uid);
          }
@@ -670,7 +691,7 @@
        else if (this.task == 'mail' && (cid = this.get_single_uid())) {
          url = { _mbox: this.env.mailbox };
          url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = cid;
          this.goto_url('compose', url, true);
          this.open_compose_step(url);
        }
        break;
@@ -863,47 +884,46 @@
        break;
      case 'compose':
        url = this.url('mail/compose');
        url = {};
        if (this.task == 'mail') {
          url += '&_mbox='+urlencode(this.env.mailbox);
          url._mbox = this.env.mailbox;
          if (props)
             url += '&_to='+urlencode(props);
             url._to = props;
          // also send search request so we can go back to search result after message is sent
          if (this.env.search_request)
            url += '&_search='+this.env.search_request;
            url._search = this.env.search_request;
        }
        // modify url if we're in addressbook
        else if (this.task == 'addressbook') {
          // switch to mail compose step directly
          if (props && props.indexOf('@') > 0) {
            url = this.get_task_url('mail', url);
            this.redirect(url + '&_to='+urlencode(props));
            url._to = props;
          }
          else {
            // use contact_id passed as command parameter
            var n, len, a_cids = [];
            if (props)
              a_cids.push(props);
            // get selected contacts
            else if (this.contact_list) {
              var selection = this.contact_list.get_selection();
              for (n=0, len=selection.length; n<len; n++)
                a_cids.push(selection[n]);
            }
            if (a_cids.length)
              this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source, }, true);
            else if (this.env.group)
              this.http_post('mailto', { _gid: this.env.group, _source: this.env.source }, true);
            break;
          }
          // use contact_id passed as command parameter
          var n, len, a_cids = [];
          if (props)
            a_cids.push(props);
          // get selected contacts
          else if (this.contact_list) {
            var selection = this.contact_list.get_selection();
            for (n=0, len=selection.length; n<len; n++)
              a_cids.push(selection[n]);
          }
          if (a_cids.length)
            this.http_post('mailto', { _cid: a_cids.join(','), _source: this.env.source}, true);
          else if (this.env.group)
            this.http_post('mailto', { _gid: this.env.group, _source: this.env.source}, true);
          break;
        }
        else if (props)
          url += '&_to='+urlencode(props);
          url._to = props;
        this.redirect(url);
        this.open_compose_step(url);
        break;
      case 'spellcheck':
@@ -978,7 +998,7 @@
          else if (command == 'reply-list')
            url._all = 'list';
          this.goto_url('compose', url, true);
          this.open_compose_step(url);
        }
        break;
@@ -988,7 +1008,7 @@
          url = { _forward_uid: uid, _mbox: this.env.mailbox };
          if (command == 'forward-attachment' || (!props && this.env.forward_attachment))
            url._attachment = 1;
          this.goto_url('compose', url, true);
          this.open_compose_step(url);
        }
        break;
@@ -1562,7 +1582,7 @@
    var uid = list.get_single_selection();
    if (uid && this.env.mailbox == this.env.drafts_mailbox)
      this.goto_url('compose', { _draft_uid: uid, _mbox: this.env.mailbox }, true);
      this.open_compose_step({ _draft_uid: uid, _mbox: this.env.mailbox });
    else if (uid)
      this.show_message(uid, false, false);
  };
@@ -1642,6 +1662,21 @@
    }
    return allow ? (copy ? 2 : 1) : 0;
  };
  this.open_window = function(url, width, height)
  {
    var w = Math.min(width, screen.width - 10),
      h = Math.min(height, screen.height - 100),
      l = (screen.width - w) / 2 + (screen.left || 0),
      t = Math.max(0, (screen.height - h) / 2 + (screen.top || 0) - 20);
    var wname = 'rcmextwin' + new Date().getTime(),
      extwin = window.open(url + '&_extwin=1', wname, 'width='+w+',height='+h+',top='+t+',left='+l);
    extwin.moveTo(l,t);
    window.setTimeout(function(){ extwin.focus(); }, 10);
    return wname;
  };
@@ -1907,7 +1942,7 @@
      this.list_mailbox('', '', sort_col+'_'+sort_order, post_data);
  };
  // when user doble-clicks on a row
  // when user double-clicks on a row
  this.show_message = function(id, safe, preview)
  {
    if (!id)
@@ -1932,10 +1967,17 @@
    // add browser capabilities, so we can properly handle attachments
    url += '&_caps='+urlencode(this.browser_capabilities());
    if (preview && String(target.location.href).indexOf(url) >= 0)
    if (this.env.extwin)
      url += '&_extwin=1';
    if (preview && String(target.location.href).indexOf(url) >= 0) {
      this.show_contentframe(true);
    }
    else {
      this.location_href(this.env.comm_path+url, target, true);
      if (!preview && this.env.message_extwin && !this.env.extwin)
        this.open_window(this.env.comm_path+url, 1000, 1200);
      else
        this.location_href(this.env.comm_path+url, target, true);
      // mark as read and change mbox unread counter
      if (preview && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread && this.env.preview_pane_mark_read >= 0) {
@@ -2963,6 +3005,17 @@
  /*********        message compose methods        *********/
  /*********************************************************/
  this.open_compose_step = function(p)
  {
    var url = this.url('mail/compose', p);
    // open new compose window
    if (this.env.compose_extwin)
      this.open_window(url, 1150, 900);
    else
      this.redirect(url);
  };
  // init message compose form: set focus and eventhandlers
  this.init_messageform = function()
  {
@@ -2976,6 +3029,11 @@
      html_mode = $("input[name='_is_html']").val() == '1',
      ac_fields = ['cc', 'bcc', 'replyto', 'followupto'],
      ac_props;
    // copy contents from opener (after opening in a new window)
    if (window.opener && opener.rcmail && opener.rcmail.env.action == 'compose') {
      //opener.history.back();
    }
    // configure parallel autocompletion
    if (this.env.autocomplete_threads > 0) {
@@ -3631,8 +3689,16 @@
  this.sent_successfully = function(type, msg)
  {
    this.display_message(msg, type);
    // before redirect we need to wait some time for Chrome (#1486177)
    setTimeout(function(){ ref.list_mailbox(); }, 500);
    if (this.env.extwin && window.opener && opener.rcmail) {
      this.lock_form(this.gui_objects.messageform);
      opener.rcmail.display_message(msg, type);
      setTimeout(function(){ window.close() }, 1000);
    }
    else {
      // before redirect we need to wait some time for Chrome (#1486177)
      setTimeout(function(){ ref.list_mailbox(); }, 500);
    }
  };
program/localization/en_US/labels.inc
@@ -383,6 +383,8 @@
$labels['pagesize']  = 'Rows per page';
$labels['signature'] = 'Signature';
$labels['dstactive']  = 'Daylight saving time';
$labels['showinextwin'] = 'Open message in a new window';
$labels['composeextwin'] = 'Compose in a new window';
$labels['htmleditor'] = 'Compose HTML messages';
$labels['htmlonreply'] = 'on reply to HTML message';
$labels['htmlonreplyandforward'] = 'on forward or reply to HTML message';
program/steps/addressbook/func.inc
@@ -87,6 +87,7 @@
    $OUTPUT->set_env('search_mods', $search_mods);
    $OUTPUT->set_env('address_sources', $js_list);
    $OUTPUT->set_env('writable_source', $writeable);
    $OUTPUT->set_env('compose_extwin', $RCMAIL->config->get('compose_extwin',false));
    $OUTPUT->set_pagetitle(rcube_label('addressbook'));
    $_SESSION['addressbooks_count'] = $count;
program/steps/addressbook/mailto.inc
@@ -68,7 +68,7 @@
    $mailto_str = join(', ', $mailto);
    $mailto_id = substr(md5($mailto_str), 0, 16);
    $_SESSION['mailto'][$mailto_id] = urlencode($mailto_str);
    $OUTPUT->redirect(array('task' => 'mail', '_action' => 'compose', '_mailto' => $mailto_id));
    $OUTPUT->command('open_compose_step', array('_mailto' => $mailto_id));
}
else {
    $OUTPUT->show_message('nocontactsfound', 'warning');
program/steps/mail/compose.inc
@@ -130,6 +130,7 @@
    'fileuploaderror', 'sendmessage');
$OUTPUT->set_env('compose_id', $COMPOSE['id']);
$OUTPUT->set_pagetitle(rcube_label('compose'));
// add config parameters to client script
if (!empty($CONFIG['drafts_mbox'])) {
@@ -606,7 +607,10 @@
  $html_editor = intval($RCMAIL->config->get('htmleditor'));
  if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
  if (isset($_POST['_is_html'])) {
    $useHtml = !empty($_POST['_is_html']);
  }
  else if ($compose_mode == RCUBE_COMPOSE_DRAFT || $compose_mode == RCUBE_COMPOSE_EDIT) {
    $useHtml = $MESSAGE->has_html_part(false);
  }
  else if ($compose_mode == RCUBE_COMPOSE_REPLY) {
@@ -1445,7 +1449,9 @@
  $attrib['value'] = '1';
  $checkbox = new html_checkbox($attrib);
  if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
  if (isset($_POST['_receipt']))
    $mdn_default = $_POST['_receipt'];
  else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT)))
    $mdn_default = (bool) $MESSAGE->headers->mdn_to;
  else
    $mdn_default = $RCMAIL->config->get('mdn_default');
@@ -1472,8 +1478,13 @@
  $attrib['value'] = '1';
  $checkbox = new html_checkbox($attrib);
  if (isset($_POST['_dsn']))
    $dsn_value = $_POST['_dsn'];
  else
    $dsn_value = $RCMAIL->config->get('dsn_default');
  $out = $form_start ? "$form_start\n" : '';
  $out .= $checkbox->show($RCMAIL->config->get('dsn_default'));
  $out .= $checkbox->show($dsn_value);
  $out .= $form_end ? "\n$form_end" : '';
  return $out;
@@ -1520,7 +1531,7 @@
    'folder_filter' => 'mail',
    'folder_rights' => 'w',
  )));
  return $select->show($COMPOSE['param']['sent_mbox'], $attrib);
  return $select->show(isset($_POST['_store_target']) ? $_POST['_store_target'] : $COMPOSE['param']['sent_mbox'], $attrib);
}
program/steps/mail/func.inc
@@ -101,18 +101,11 @@
    $OUTPUT->set_env('quota', true);
  }
  if ($CONFIG['delete_junk'])
    $OUTPUT->set_env('delete_junk', true);
  if ($CONFIG['flag_for_deletion'])
    $OUTPUT->set_env('flag_for_deletion', true);
  if ($CONFIG['read_when_deleted'])
    $OUTPUT->set_env('read_when_deleted', true);
  if ($CONFIG['skip_deleted'])
    $OUTPUT->set_env('skip_deleted', true);
  if ($CONFIG['display_next'])
    $OUTPUT->set_env('display_next', true);
  if ($CONFIG['forward_attachment'])
    $OUTPUT->set_env('forward_attachment', true);
  foreach (array('delete_junk','flag_for_deletion','read_when_deleted','skip_deleted','display_next','message_extwin','compose_extwin','forward_attachment') as $prop) {
    if ($CONFIG[$prop])
      $OUTPUT->set_env($prop, true);
  }
  if ($CONFIG['trash_mbox'])
    $OUTPUT->set_env('trash_mailbox', $CONFIG['trash_mbox']);
  if ($CONFIG['drafts_mbox'])
program/steps/mail/show.inc
@@ -59,6 +59,7 @@
  $OUTPUT->set_env('permaurl', rcmail_url('show', array('_uid' => $MESSAGE->uid, '_mbox' => $mbox_name)));
  $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
  $OUTPUT->set_env('mailbox', $mbox_name);
  $OUTPUT->set_env('compose_extwin', $RCMAIL->config->get('compose_extwin',false));
  // mimetypes supported by the browser (default settings)
  $mimetypes = $RCMAIL->config->get('client_mimetypes', 'text/plain,text/html,text/xml,image/jpeg,image/gif,image/png,image/bmp,image/tiff,application/x-javascript,application/pdf,application/x-shockwave-flash');
program/steps/settings/func.inc
@@ -129,8 +129,8 @@
  $sections['general'] = array('id' => 'general', 'section' => rcube_label('uisettings'));
  $sections['mailbox'] = array('id' => 'mailbox', 'section' => rcube_label('mailboxview'));
  $sections['compose'] = array('id' => 'compose', 'section' => rcube_label('messagescomposition'));
  $sections['mailview'] = array('id' => 'mailview','section' => rcube_label('messagesdisplaying'));
  $sections['compose'] = array('id' => 'compose', 'section' => rcube_label('messagescomposition'));
  $sections['addressbook'] = array('id' => 'addressbook','section' => rcube_label('addressbook'));
  $sections['folders'] = array('id' => 'folders', 'section' => rcube_label('specialfolders'));
  $sections['server'] = array('id' => 'server',  'section' => rcube_label('serversettings'));
@@ -414,6 +414,17 @@
      'main' => array('name' => Q(rcube_label('mainoptions'))),
    );
    // show checkbox to open message view in new window
    if (!isset($no_override['message_extwin'])) {
      $field_id = 'rcmfd_message_extwin';
      $input_msgextwin = new html_checkbox(array('name' => '_message_extwin', 'id' => $field_id, 'value' => 1));
      $blocks['main']['options']['message_extwin'] = array(
        'title' => html::label($field_id, Q(rcube_label('showinextwin'))),
        'content' => $input_msgextwin->show($config['message_extwin']?1:0),
      );
    }
    // show checkbox for HTML/plaintext messages
    if (!isset($no_override['prefer_html'])) {
      $field_id = 'rcmfd_htmlmsg';
@@ -483,6 +494,17 @@
      'sig'        => array('name' => Q(rcube_label('signatureoptions'))),
    );
    // show checkbox to compose messages in a new window
    if (!isset($no_override['compose_extwin'])) {
      $field_id = 'rcmfdcompose_extwin';
      $input_compextwin = new html_checkbox(array('name' => '_compose_extwin', 'id' => $field_id, 'value' => 1));
      $blocks['main']['options']['compose_extwin'] = array(
        'title' => html::label($field_id, Q(rcube_label('composeextwin'))),
        'content' => $input_compextwin->show($config['compose_extwin']?1:0),
      );
    }
    if (!isset($no_override['htmleditor'])) {
      $field_id = 'rcmfd_htmleditor';
      $select_htmleditor = new html_select(array('name' => '_htmleditor', 'id' => $field_id));
program/steps/settings/save_prefs.inc
@@ -59,6 +59,7 @@
  case 'mailview':
    $a_user_prefs = array(
      'message_extwin'  => intval($_POST['_message_extwin']),
      'prefer_html'     => isset($_POST['_prefer_html']) ? TRUE : FALSE,
      'inline_images'   => isset($_POST['_inline_images']) ? TRUE : FALSE,
      'show_images'     => isset($_POST['_show_images']) ? intval($_POST['_show_images']) : 0,
@@ -70,6 +71,7 @@
  case 'compose':
    $a_user_prefs = array(
      'compose_extwin'     => intval($_POST['_compose_extwin']),
      'htmleditor'         => intval($_POST['_htmleditor']),
      'draft_autosave'     => isset($_POST['_draft_autosave']) ? intval($_POST['_draft_autosave']) : 0,
      'mime_param_folding' => isset($_POST['_mime_param_folding']) ? intval($_POST['_mime_param_folding']) : 0,
skins/larry/includes/header.html
@@ -7,11 +7,16 @@
        <roundcube:endif />
    </div>
    <div class="topright">
    <span class="username"><roundcube:object name="username" /></span>
    <roundcube:button command="logout" label="logout" class="button-logout" />
    <roundcube:if condition="!env:extwin" />
        <span class="username"><roundcube:object name="username" /></span>
        <roundcube:button command="logout" label="logout" class="button-logout" />
    <roundcube:else />
        <roundcube:button command="close" label="close" class="closelink" />
    <roundcube:endif />
    </div>
</div>
<roundcube:if condition="!env:extwin" />
<div id="topnav">
    <div id="taskbar" class="topright">
    <roundcube:button command="mail" label="mail" class="button-mail" classSel="button-mail button-selected" innerClass="button-inner" />
@@ -21,6 +26,7 @@
    </div>
    <roundcube:object name="logo" src="/images/roundcube_logo.png" id="toplogo" border="0" alt="Logo" onclick="rcmail.command('switch-task','mail');return false;" />
</div>
<roundcube:endif />
<br style="clear:both" />
</div>
skins/larry/mail.css
@@ -30,6 +30,10 @@
    z-index: 3;
}
#mailview-right.fullwidth {
    left: 0;
}
#mailview-top {
    position: absolute;
    top: 42px;
skins/larry/styles.css
@@ -637,6 +637,10 @@
    bottom: 20px;
}
.extwin #mainscreen {
    top: 40px;
}
#mainscreen.offset {
    top: 130px;
}
skins/larry/templates/compose.html
@@ -7,7 +7,7 @@
<link rel="stylesheet" type="text/css" href="/googiespell.css" />
<roundcube:endif />
</head>
<body>
<roundcube:if condition="env:extwin" /><body class="extwin"><roundcube:else /><body><roundcube:endif />
<div class="minwidth">
<roundcube:include file="/includes/header.html" />
@@ -108,10 +108,11 @@
</tbody>
</table>
<div id="composebuttons" class="formbuttons">
<div id="composebuttons" class="pagenav formbuttons">
    <roundcube:button type="input" command="send" class="button mainaction" label="sendmessage" tabindex="11" />
    <roundcube:button type="input" command="savedraft" class="button" label="savemessage" tabindex="12" />
    <roundcube:button type="input" command="list" class="button" label="cancel" tabindex="13" />
    <roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="inner" title="openinextwin" content="[]" condition="!env:extwin" />
</div>
</div>
skins/larry/templates/message.html
@@ -4,7 +4,7 @@
<title><roundcube:object name="pagetitle" /></title>
<roundcube:include file="/includes/links.html" />
</head>
<body class="noscroll">
<roundcube:if condition="env:extwin" /><body class="noscroll extwin"><roundcube:else /><body class="noscroll"><roundcube:endif />
<roundcube:include file="/includes/header.html" />
@@ -12,13 +12,17 @@
<!-- toolbar -->
<div id="messagetoolbar" class="toolbar fullwidth">
<roundcube:if condition="!env:extwin" />
    <roundcube:button command="list" type="link" class="button back disabled" classAct="button back" classSel="button back pressed" label="back" />
    <span class="spacer"></span>
<roundcube:endif />
    <roundcube:include file="/includes/mailtoolbar.html" />
    <div class="toolbarselect">
        <roundcube:object name="mailboxlist" type="select" noSelection="moveto" maxlength="25" onchange="rcmail.command('moveto', this.options[this.selectedIndex].value)" class="mailboxlist decorated" folder_filter="mail" />
    </div>
</div>
<roundcube:if condition="!env:extwin" />
<div id="mailview-left">
@@ -32,6 +36,10 @@
</div>
<div id="mailview-right" class="offset uibox">
<roundcube:else />
<div id="mailview-right" class="offset fullwidth uibox">
<roundcube:endif />
<div id="messageheader">
<span id="previewheaderstoggle"></span>
skins/larry/templates/messagepreview.html
@@ -36,7 +36,7 @@
    <roundcube:button command="forward" type="link" class="button forward" classSel="button forward pressed" innerClass="inner" title="forwardmessage" content="-&gt;" />
    &nbsp;
<roundcube:endif />
    <roundcube:button command="permaurl" type="link" class="button extwin" classSel="button extwin pressed" innerClass="inner" title="openinextwin" content="[]" target="_blank" />
    <roundcube:button command="extwin" type="link" class="button extwin" classSel="button extwin pressed" innerClass="inner" title="openinextwin" content="[]" />
</div>
</div>