From 30b30226e6569f13e444cdcb513cd2bfc24318d7 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Thu, 04 Nov 2010 10:03:26 -0400
Subject: [PATCH] - Add possibility to force mailbox selection. There're situations where we're invoking   STATUS (for all messages count) and SELECT later for other operations. If we   call SELECT first, the STATUS will be not needed.

---
 program/include/rcube_template.php |  546 ++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 349 insertions(+), 197 deletions(-)

diff --git a/program/include/rcube_template.php b/program/include/rcube_template.php
index 61e4975..a30c4f3 100755
--- a/program/include/rcube_template.php
+++ b/program/include/rcube_template.php
@@ -4,8 +4,8 @@
  +-----------------------------------------------------------------------+
  | program/include/rcube_template.php                                    |
  |                                                                       |
- | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2006-2008, RoundCube Dev. - Switzerland                 |
+ | This file is part of the Roundcube Webmail client                     |
+ | Copyright (C) 2006-2010, Roundcube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -30,15 +30,17 @@
  */
 class rcube_template extends rcube_html_page
 {
-    var $app;
-    var $config;
-    var $framed = false;
-    var $pagetitle = '';
-    var $env = array();
-    var $js_env = array();
-    var $js_commands = array();
-    var $object_handlers = array();
+    private $app;
+    private $config;
+    private $pagetitle = '';
+    private $message = null;
+    private $js_env = array();
+    private $js_commands = array();
+    private $object_handlers = array();
 
+    public $browser;
+    public $framed = false;
+    public $env = array();
     public $type = 'html';
     public $ajax_call = false;
 
@@ -54,9 +56,11 @@
 
         $this->app = rcmail::get_instance();
         $this->config = $this->app->config->all();
-        
+        $this->browser = new rcube_browser();
+
         //$this->framed = $framed;
         $this->set_env('task', $task);
+        $this->set_env('request_token', $this->app->get_request_token());
 
         // load the correct skin (in case user-defined)
         $this->set_skin($this->config['skin']);
@@ -65,17 +69,19 @@
         $javascript = 'var '.JS_OBJECT_NAME.' = new rcube_webmail();';
 
         // don't wait for page onload. Call init at the bottom of the page (delayed)
-        $javascript_foot = "if (window.call_init)\n call_init('".JS_OBJECT_NAME."');";
+        $javascript_foot = '$(document).ready(function(){ '.JS_OBJECT_NAME.'.init(); });';
 
         $this->add_script($javascript, 'head_top');
         $this->add_script($javascript_foot, 'foot');
         $this->scripts_path = 'program/js/';
+        $this->include_script('jquery-1.4.min.js');
         $this->include_script('common.js');
         $this->include_script('app.js');
 
         // register common UI objects
         $this->add_handlers(array(
             'loginform'       => array($this, 'login_form'),
+            'preloader'       => array($this, 'preloader'),
             'username'        => array($this, 'current_username'),
             'message'         => array($this, 'message_container'),
             'charsetselector' => array($this, 'charset_selector'),
@@ -123,7 +129,7 @@
         else {
             $title = ucfirst($this->env['task']);
         }
-        
+
         return $title;
     }
 
@@ -133,13 +139,21 @@
      */
     public function set_skin($skin)
     {
-        if (!empty($skin) && is_dir('skins/'.$skin) && is_readable('skins/'.$skin))
+        $valid = false;
+
+        if (!empty($skin) && is_dir('skins/'.$skin) && is_readable('skins/'.$skin)) {
             $skin_path = 'skins/'.$skin;
-        else
+            $valid = true;
+        }
+        else {
             $skin_path = $this->config['skin_path'] ? $this->config['skin_path'] : 'skins/default';
+            $valid = !$skin;
+        }
 
         $this->app->config->set('skin_path', $skin_path);
         $this->config['skin_path'] = $skin_path;
+
+        return $valid;
     }
 
     /**
@@ -198,7 +212,9 @@
      */
     public function command()
     {
-        $this->js_commands[] = func_get_args();
+        $cmd = func_get_args();
+        if (strpos($cmd[0], 'plugin.') === false)
+          $this->js_commands[] = $cmd;
     }
 
 
@@ -207,8 +223,11 @@
      */
     public function add_label()
     {
-        $arg_list = func_get_args();
-        foreach ($arg_list as $i => $name) {
+        $args = func_get_args();
+        if (count($args) == 1 && is_array($args[0]))
+          $args = $args[0];
+
+        foreach ($args as $name) {
             $this->command('add_label', $name, rcube_label($name));
         }
     }
@@ -220,14 +239,18 @@
      * @param string Message to display
      * @param string Message type [notice|confirm|error]
      * @param array Key-value pairs to be replaced in localized text
+     * @param boolean Override last set message
      * @uses self::command()
      */
-    public function show_message($message, $type='notice', $vars=NULL)
+    public function show_message($message, $type='notice', $vars=null, $override=true)
     {
-        $this->command(
-            'display_message',
-            rcube_label(array('name' => $message, 'vars' => $vars)),
-            $type);
+        if ($override || !$this->message) {
+            $this->message = $message;
+            $this->command(
+                'display_message',
+                rcube_label(array('name' => $message, 'vars' => $vars)),
+                $type);
+        }
     }
 
 
@@ -275,12 +298,23 @@
     public function send($templ = null, $exit = true)
     {
         if ($templ != 'iframe') {
+            // prevent from endless loops
+            if ($exit != 'recur' && $this->app->plugins->is_processing('render_page')) {
+                raise_error(array('code' => 505, 'type' => 'php',
+                  'file' => __FILE__, 'line' => __LINE__,
+                  'message' => 'Recursion alert: ignoring output->send()'), true, false);
+                return;
+            }
             $this->parse($templ, false);
         }
         else {
             $this->framed = $templ == 'iframe' ? true : $this->framed;
             $this->write();
         }
+
+        // set output asap
+        ob_flush();
+        flush();
 
         if ($exit) {
             exit;
@@ -297,13 +331,21 @@
     public function write($template = '')
     {
         // unlock interface after iframe load
+        $unlock = preg_replace('/[^a-z0-9]/i', '', $_GET['_unlock']);
         if ($this->framed) {
-            array_unshift($this->js_commands, array('set_busy', false));
+            array_unshift($this->js_commands, array('set_busy', false, null, $unlock));
+        }
+        else if ($unlock) {
+            array_unshift($this->js_commands, array('hide_message', $unlock));
         }
         // write all env variables to client
         $js = $this->framed ? "if(window.parent) {\n" : '';
         $js .= $this->get_js_commands() . ($this->framed ? ' }' : '');
         $this->add_script($js, 'head_top');
+
+        // make sure all <form> tags have a valid request token
+        $template = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $template);
+        $this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer);
 
         // call super method
         parent::write($template, $this->config['skin_path']);
@@ -322,6 +364,20 @@
     private function parse($name = 'main', $exit = true)
     {
         $skin_path = $this->config['skin_path'];
+        $plugin = false;
+
+        $temp = explode(".", $name, 2);
+        if (count($temp) > 1) {
+            $plugin = $temp[0];
+            $name = $temp[1];
+            $skin_dir = $plugin . '/skins/' . $this->config['skin'];
+            $skin_path = $this->app->plugins->dir . $skin_dir;
+            if (!is_dir($skin_path)) {  // fallback to default skin
+                $skin_dir = $plugin . '/skins/default';
+                $skin_path = $this->app->plugins->dir . $skin_dir;
+            }
+        }
+
         $path = "$skin_path/templates/$name.html";
 
         // read template file
@@ -336,19 +392,29 @@
             return false;
         }
 
+        // replace all path references to plugins/... with the configured plugins dir
+        // and /this/ to the current plugin skin directory
+        if ($plugin) {
+            $templ = preg_replace(array('/\bplugins\//', '/(["\']?)\/this\//'), array($this->app->plugins->url, '\\1'.$this->app->plugins->url.$skin_dir.'/'), $templ);
+        }
+
         // parse for specialtags
         $output = $this->parse_conditions($templ);
         $output = $this->parse_xml($output);
 
+        // trigger generic hook where plugins can put additional content to the page
+        $hook = $this->app->plugins->exec_hook("render_page", array('template' => $name, 'content' => $output));
+
         // add debug console
         if ($this->config['debug_level'] & 8) {
-            $this->add_footer('<div style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;opacity:0.8;filter:alpha(opacity=80);z-index:9000">
-                <a href="#toggle" onclick="con=document.getElementById(\'dbgconsole\');con.style.display=(con.style.display==\'none\'?\'block\':\'none\');return false">console</a>
-                <form action="/" name="debugform" style="display:inline"><textarea name="console" id="dbgconsole" rows="20" cols="40" wrap="off" style="display:none;width:400px;border:none;font-size:x-small"></textarea></form></div>'
+            $this->add_footer('<div id="console" style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;z-index:9000;">
+                <a href="#toggle" onclick="con=$(\'#dbgconsole\');con[con.is(\':visible\')?\'hide\':\'show\']();return false">console</a>
+                <textarea name="console" id="dbgconsole" rows="20" cols="40" wrap="off" style="display:none;width:400px;border:none;font-size:10px" spellcheck="false"></textarea></div>'
             );
         }
-        $output = $this->parse_with_globals($output);
-        $this->write(trim($output), $skin_path);
+
+        $output = $this->parse_with_globals($hook['content']);
+        $this->write(trim($output));
         if ($exit) {
             exit;
         }
@@ -374,12 +440,12 @@
             $parent = $this->framed || preg_match('/^parent\./', $method);
             $out .= sprintf(
                 "%s.%s(%s);\n",
-            ($parent ? 'parent.' : '') . JS_OBJECT_NAME,
-            preg_replace('/^parent\./', '', $method),
-            implode(',', $args)
+                ($parent ? 'if(window.parent && parent.'.JS_OBJECT_NAME.') parent.' : '') . JS_OBJECT_NAME,
+                preg_replace('/^parent\./', '', $method),
+                implode(',', $args)
             );
         }
-        
+
         return $out;
     }
 
@@ -391,7 +457,10 @@
      */
     public function abs_url($str)
     {
-        return preg_replace('/^\//', $this->config['skin_path'].'/', $str);
+        if ($str[0] == '/')
+            return $this->config['skin_path'] . $str;
+        else
+            return $str;
     }
 
 
@@ -403,8 +472,18 @@
      */
     private function parse_with_globals($input)
     {
+        $GLOBALS['__version'] = Q(RCMAIL_VERSION);
         $GLOBALS['__comm_path'] = Q($this->app->comm_path);
-        return preg_replace('/\$(__[a-z0-9_\-]+)/e', '$GLOBALS["\\1"]', $input);
+        return preg_replace_callback('/\$(__[a-z0-9_\-]+)/',
+	    array($this, 'globals_callback'), $input);
+    }
+
+    /**
+     * Callback funtion for preg_replace_callback() in parse_with_globals()
+     */
+    private function globals_callback($matches)
+    {
+        return $GLOBALS[$matches[1]];
     }
 
     /**
@@ -468,7 +547,24 @@
      */
     private function check_condition($condition)
     {
-            return eval("return (".$this->parse_expression($condition).");");
+        return eval("return (".$this->parse_expression($condition).");");
+    }
+
+
+    /**
+     * Inserts hidden field with CSRF-prevention-token into POST forms
+     */
+    private function alter_form_tag($matches)
+    {
+        $out = $matches[0];
+        $attrib  = parse_attrib_string($matches[1]);
+
+        if (strtolower($attrib['method']) == 'post') {
+            $hidden = new html_hiddenfield(array('name' => '_token', 'value' => $this->app->get_request_token()));
+            $out .= "\n" . $hidden->show();
+        }
+
+        return $out;
     }
 
 
@@ -476,7 +572,7 @@
      * Parses expression and replaces variables
      *
      * @param  string Expression statement
-     * @return string Expression statement
+     * @return string Expression value
      */
     private function parse_expression($expression)
     {
@@ -486,14 +582,16 @@
                 '/config:([a-z0-9_]+)(:([a-z0-9_]+))?/i',
                 '/env:([a-z0-9_]+)/i',
                 '/request:([a-z0-9_]+)/i',
-                '/cookie:([a-z0-9_]+)/i'
+                '/cookie:([a-z0-9_]+)/i',
+                '/browser:([a-z0-9_]+)/i'
             ),
             array(
                 "\$_SESSION['\\1']",
                 "\$this->app->config->get('\\1',get_boolean('\\3'))",
                 "\$this->env['\\1']",
                 "get_input_value('\\1', RCUBE_INPUT_GPC)",
-                "\$_COOKIE['\\1']"
+                "\$_COOKIE['\\1']",
+                "\$this->browser->{'\\1'}"
             ),
             $expression);
     }
@@ -510,37 +608,21 @@
      */
     private function parse_xml($input)
     {
-        return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command_callback'), $input);
+        return preg_replace_callback('/<roundcube:([-_a-z]+)\s+([^>]+)>/Ui', array($this, 'xml_command'), $input);
     }
 
 
     /**
-     * This is a callback function for preg_replace_callback (see #1485286)
-     * It's only purpose is to reconfigure parameters for xml_command, so that the signature isn't disturbed
-     */
-    private function xml_command_callback($matches)
-    {
-        $str_attrib = isset($matches[2]) ? $matches[2] : '';
-        $add_attrib = isset($matches[3]) ? $matches[3] : array();
-
-        $command = $matches[1];
-        //matches[0] is the entire matched portion of the string
-
-        return $this->xml_command($command, $str_attrib, $add_attrib);
-    }
-
-
-    /**
-     * Convert a xml command tag into real content
+     * Callback function for parsing an xml command tag
+     * and turn it into real html content
      *
-     * @param  string Tag command: object,button,label, etc.
-     * @param  string Attribute string
+     * @param  array Matches array of preg_replace_callback
      * @return string Tag/Object content
      */
-    private function xml_command($command, $str_attrib, $add_attrib = array())
+    private function xml_command($matches)
     {
-        $command = strtolower($command);
-        $attrib  = parse_attrib_string($str_attrib) + $add_attrib;
+        $command = strtolower($matches[1]);
+        $attrib  = parse_attrib_string($matches[2]);
 
         // empty output if required condition is not met
         if (!empty($attrib['condition']) && !$this->check_condition($attrib['condition'])) {
@@ -571,69 +653,73 @@
                         $incl = $this->include_php($path);
                     }
                     else {
-		        $incl = file_get_contents($path);
-		    }
+                      $incl = file_get_contents($path);
+                    }
+                    $incl = $this->parse_conditions($incl);
                     return $this->parse_xml($incl);
                 }
                 break;
 
             case 'plugin.include':
-                //rcube::tfk_debug(var_export($this->config['skin_path'], true));
-                $path = realpath($this->config['skin_path'].$attrib['file']);
-                if (!$path) {
-                    //rcube::tfk_debug("Does not exist:");
-                    //rcube::tfk_debug($this->config['skin_path']);
-                    //rcube::tfk_debug($attrib['file']);
-                    //rcube::tfk_debug($path);
-                }
-                $incl = file_get_contents($path);
-                if ($incl) {
-                    return $this->parse_xml($incl);
+                $hook = $this->app->plugins->exec_hook("template_plugin_include", $attrib);
+                return $hook['content'];
+                break;
+
+            // define a container block
+            case 'container':
+                if ($attrib['name'] && $attrib['id']) {
+                    $this->command('gui_container', $attrib['name'], $attrib['id']);
+                    // let plugins insert some content here
+                    $hook = $this->app->plugins->exec_hook("template_container", $attrib);
+                    return $hook['content'];
                 }
                 break;
 
             // return code for a specific application object
             case 'object':
                 $object = strtolower($attrib['name']);
+                $content = '';
 
                 // we are calling a class/method
                 if (($handler = $this->object_handlers[$object]) && is_array($handler)) {
                     if ((is_object($handler[0]) && method_exists($handler[0], $handler[1])) ||
                     (is_string($handler[0]) && class_exists($handler[0])))
-                    return call_user_func($handler, $attrib);
+                    $content = call_user_func($handler, $attrib);
                 }
+                // execute object handler function
                 else if (function_exists($handler)) {
-                    // execute object handler function
-                    return call_user_func($handler, $attrib);
+                    $content = call_user_func($handler, $attrib);
                 }
-
-                if ($object=='productname') {
-                    $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'RoundCube Webmail';
-                    return Q($name);
+                else if ($object == 'productname') {
+                    $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'Roundcube Webmail';
+                    $content = Q($name);
                 }
-                if ($object=='version') {
+                else if ($object == 'version') {
                     $ver = (string)RCMAIL_VERSION;
                     if (is_file(INSTALL_PATH . '.svn/entries')) {
                         if (preg_match('/Revision:\s(\d+)/', @shell_exec('svn info'), $regs))
                           $ver .= ' [SVN r'.$regs[1].']';
                     }
-                    return $ver;
+                    $content = Q($ver);
                 }
-                if ($object=='steptitle') {
-                  return Q($this->get_pagetitle());
+                else if ($object == 'steptitle') {
+                  $content = Q($this->get_pagetitle());
                 }
-                if ($object=='pagetitle') {
+                else if ($object == 'pagetitle') {
                     $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : '';
                     $title .= $this->get_pagetitle();
-                    return Q($title);
+                    $content = Q($title);
                 }
-                break;
+
+                // exec plugin hooks for this template object
+                $hook = $this->app->plugins->exec_hook("template_object_$object", $attrib + array('content' => $content));
+                return $hook['content'];
 
             // return code for a specified eval expression
             case 'exp':
-        	$value = $this->parse_expression($attrib['expression']);
+                $value = $this->parse_expression($attrib['expression']);
                 return eval("return Q($value);");
-            
+
             // return variable
             case 'var':
                 $var = explode(':', $attrib['name']);
@@ -658,6 +744,9 @@
                         break;
                     case 'cookie':
                         $value = htmlspecialchars($_COOKIE[$name]);
+                        break;
+                    case 'browser':
+                        $value = $this->browser->{$name};
                         break;
                 }
 
@@ -697,17 +786,14 @@
      */
     public function button($attrib)
     {
-        static $sa_buttons = array();
         static $s_button_count = 100;
 
         // these commands can be called directly via url
-        $a_static_commands = array('compose', 'list');
+        $a_static_commands = array('compose', 'list', 'preferences', 'folders', 'identities');
 
         if (!($attrib['command'] || $attrib['name'])) {
             return '';
         }
-
-        $browser   = new rcube_browser();
 
         // try to find out the button type
         if ($attrib['type']) {
@@ -716,48 +802,33 @@
         else {
             $attrib['type'] = ($attrib['image'] || $attrib['imagepas'] || $attrib['imageact']) ? 'image' : 'link';
         }
+
         $command = $attrib['command'];
 
-        // take the button from the stack
-        if ($attrib['name'] && $sa_buttons[$attrib['name']]) {
-            $attrib = $sa_buttons[$attrib['name']];
-        }
-        else if($attrib['image'] || $attrib['imageact'] || $attrib['imagepas'] || $attrib['class']) {
-            // add button to button stack
-            if (!$attrib['name']) {
-                $attrib['name'] = $command;
-            }
-            if (!$attrib['image']) {
-                $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
-            }
-            $sa_buttons[$attrib['name']] = $attrib;
-        }
-        else if ($command && $sa_buttons[$command]) {
-            // get saved button for this command/name
-            $attrib = $sa_buttons[$command];
+        if ($attrib['task'])
+          $command = $attrib['task'] . '.' . $command;
+
+        if (!$attrib['image']) {
+            $attrib['image'] = $attrib['imagepas'] ? $attrib['imagepas'] : $attrib['imageact'];
         }
 
-        // set border to 0 because of the link arround the button
-        if ($attrib['type']=='image' && !isset($attrib['border'])) {
-            $attrib['border'] = 0;
-        }
         if (!$attrib['id']) {
             $attrib['id'] =  sprintf('rcmbtn%d', $s_button_count++);
         }
         // get localized text for labels and titles
         if ($attrib['title']) {
-            $attrib['title'] = Q(rcube_label($attrib['title']));
+            $attrib['title'] = Q(rcube_label($attrib['title'], $attrib['domain']));
         }
         if ($attrib['label']) {
-            $attrib['label'] = Q(rcube_label($attrib['label']));
+            $attrib['label'] = Q(rcube_label($attrib['label'], $attrib['domain']));
         }
         if ($attrib['alt']) {
-            $attrib['alt'] = Q(rcube_label($attrib['alt']));
+            $attrib['alt'] = Q(rcube_label($attrib['alt'], $attrib['domain']));
         }
+
         // set title to alt attribute for IE browsers
-        if ($browser->ie && $attrib['title'] && !$attrib['alt']) {
-            $attrib['alt'] = $attrib['title'];
-            unset($attrib['title']);
+        if ($this->browser->ie && !$attrib['title'] && $attrib['alt']) {
+            $attrib['title'] = $attrib['alt'];
         }
 
         // add empty alt attribute for XHTML compatibility
@@ -782,11 +853,14 @@
             if (in_array($attrib['command'], rcmail::$main_tasks)) {
                 $attrib['href'] = rcmail_url(null, null, $attrib['command']);
             }
+            else if ($attrib['task'] && in_array($attrib['task'], rcmail::$main_tasks)) {
+                $attrib['href'] = rcmail_url($attrib['command'], null, $attrib['task']);
+            }
             else if (in_array($attrib['command'], $a_static_commands)) {
                 $attrib['href'] = rcmail_url($attrib['command']);
             }
             else if ($attrib['command'] == 'permaurl' && !empty($this->env['permaurl'])) {
-                $attrib['href'] = $this->env['permaurl'];
+              $attrib['href'] = $this->env['permaurl'];
             }
         }
 
@@ -794,41 +868,16 @@
         if (!$attrib['href']) {
             $attrib['href'] = '#';
         }
-        if ($command) {
+        if ($attrib['task']) {
+            if ($attrib['classact'])
+                $attrib['class'] = $attrib['classact'];
+        }
+        else if ($command && !$attrib['onclick']) {
             $attrib['onclick'] = sprintf(
                 "return %s.command('%s','%s',this)",
                 JS_OBJECT_NAME,
                 $command,
                 $attrib['prop']
-            );
-        }
-        if ($command && $attrib['imageover']) {
-            $attrib['onmouseover'] = sprintf(
-                "return %s.button_over('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-            $attrib['onmouseout'] = sprintf(
-                "return %s.button_out('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-        }
-
-        if ($command && $attrib['imagesel']) {
-            $attrib['onmousedown'] = sprintf(
-                "return %s.button_sel('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
-            );
-            $attrib['onmouseup'] = sprintf(
-                "return %s.button_out('%s','%s')",
-                JS_OBJECT_NAME,
-                $command,
-                $attrib['id']
             );
         }
 
@@ -839,19 +888,18 @@
             $attrib_str = html::attrib_string(
                 $attrib,
                 array(
-                    'style', 'class', 'id', 'width',
-                    'height', 'border', 'hspace',
-                    'vspace', 'align', 'alt', 'tabindex'
+                    'style', 'class', 'id', 'width', 'height', 'border', 'hspace',
+                    'vspace', 'align', 'alt', 'tabindex', 'title'
                 )
             );
             $btn_content = sprintf('<img src="%s"%s />', $this->abs_url($attrib['image']), $attrib_str);
             if ($attrib['label']) {
                 $btn_content .= ' '.$attrib['label'];
             }
-            $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'title', 'target');
+            $link_attrib = array('href', 'onclick', 'onmouseover', 'onmouseout', 'onmousedown', 'onmouseup', 'target');
         }
         else if ($attrib['type']=='link') {
-            $btn_content = $attrib['label'] ? $attrib['label'] : $attrib['command'];
+            $btn_content = isset($attrib['content']) ? $attrib['content'] : ($attrib['label'] ? $attrib['label'] : $attrib['command']);
             $link_attrib = array('href', 'onclick', 'title', 'id', 'class', 'style', 'tabindex', 'target');
         }
         else if ($attrib['type']=='input') {
@@ -864,8 +912,7 @@
             $attrib_str = html::attrib_string(
                 $attrib,
                 array(
-                    'type', 'value', 'onclick',
-                    'id', 'class', 'style', 'tabindex'
+                    'type', 'value', 'onclick', 'id', 'class', 'style', 'tabindex'
                 )
             );
             $out = sprintf('<input%s disabled="disabled" />', $attrib_str);
@@ -892,17 +939,46 @@
      */
     public function form_tag($attrib, $content = null)
     {
-      if ($this->framed) {
+      if ($this->framed || !empty($_REQUEST['_framed'])) {
         $hiddenfield = new html_hiddenfield(array('name' => '_framed', 'value' => '1'));
         $hidden = $hiddenfield->show();
       }
-      
+
       if (!$content)
         $attrib['noclose'] = true;
-      
+
       return html::tag('form',
         $attrib + array('action' => "./", 'method' => "get"),
-        $hidden . $content);
+        $hidden . $content,
+        array('id','class','style','name','method','action','enctype','onsubmit'));
+    }
+
+
+    /**
+     * Build a form tag with a unique request token
+     *
+     * @param array Named tag parameters including 'action' and 'task' values which will be put into hidden fields
+     * @param string Form content
+     * @return string HTML code for the form
+     */
+    public function request_form($attrib, $content = '')
+    {
+        $hidden = new html_hiddenfield();
+        if ($attrib['task']) {
+            $hidden->add(array('name' => '_task', 'value' => $attrib['task']));
+        }
+        if ($attrib['action']) {
+            $hidden->add(array('name' => '_action', 'value' => $attrib['action']));
+        }
+
+        unset($attrib['task'], $attrib['request']);
+        $attrib['action'] = './';
+
+        // we already have a <form> tag
+        if ($attrib['form'])
+            return $hidden->show() . $content;
+        else
+            return $this->form_tag($attrib, $hidden->show() . $content);
     }
 
 
@@ -922,15 +998,19 @@
             return $username;
         }
 
-        // get e-mail address form default identity
-        if ($sql_arr = $this->app->user->get_identity()) {
+        // Current username is an e-mail address
+        if (strpos($_SESSION['username'], '@')) {
+            $username = $_SESSION['username'];
+        }
+        // get e-mail address from default identity
+        else if ($sql_arr = $this->app->user->get_identity()) {
             $username = $sql_arr['email'];
         }
         else {
             $username = $this->app->user->get_username();
         }
 
-        return $username;
+        return idn_to_utf8($username);
     }
 
 
@@ -947,13 +1027,19 @@
 
         $_SESSION['temp'] = true;
 
-        $input_user   = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser', 'size' => 30) + $attrib);
-        $input_pass   = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd', 'size' => 30) + $attrib);
+        // save original url
+        $url = get_input_value('_url', RCUBE_INPUT_POST);
+        if (empty($url) && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING']))
+            $url = $_SERVER['QUERY_STRING'];
+
+        $input_user   = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser') + $attrib);
+        $input_pass   = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd') + $attrib);
         $input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
         $input_tzone  = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
+        $input_url    = new html_hiddenfield(array('name' => '_url', 'id' => 'rcmloginurl', 'value' => $url));
         $input_host   = null;
 
-        if (is_array($default_host)) {
+        if (is_array($default_host) && count($default_host) > 1) {
             $input_host = new html_select(array('name' => '_host', 'id' => 'rcmloginhost'));
 
             foreach ($default_host as $key => $value) {
@@ -966,8 +1052,13 @@
                 }
             }
         }
+        else if (is_array($default_host) && ($host = array_pop($default_host))) {
+            $hide_host = true;
+            $input_host = new html_hiddenfield(array(
+                'name' => '_host', 'id' => 'rcmloginhost', 'value' => $host) + $attrib);
+        }
         else if (empty($default_host)) {
-            $input_host = new html_inputfield(array('name' => '_host', 'id' => 'rcmloginhost', 'size' => 30));
+            $input_host = new html_inputfield(array('name' => '_host', 'id' => 'rcmloginhost') + $attrib);
         }
 
         $form_name  = !empty($attrib['form']) ? $attrib['form'] : 'form';
@@ -983,21 +1074,50 @@
         $table->add(null, $input_pass->show());
 
         // add host selection row
-        if (is_object($input_host)) {
+        if (is_object($input_host) && !$hide_host) {
             $table->add('title', html::label('rcmloginhost', Q(rcube_label('server'))));
             $table->add(null, $input_host->show(get_input_value('_host', RCUBE_INPUT_POST)));
         }
 
         $out = $input_action->show();
         $out .= $input_tzone->show();
+        $out .= $input_url->show();
         $out .= $table->show();
+
+        if ($hide_host) {
+            $out .= $input_host->show();
+        }
 
         // surround html output with a form tag
         if (empty($attrib['form'])) {
-            $out = $this->form_tag(array('name' => $form_name, 'method' => "post"), $out);
+            $out = $this->form_tag(array('name' => $form_name, 'method' => 'post'), $out);
         }
 
         return $out;
+    }
+
+
+    /**
+     * GUI object 'preloader'
+     * Loads javascript code for images preloading
+     *
+     * @param array Named parameters
+     * @return void
+     */
+    private function preloader($attrib)
+    {
+        $images = preg_split('/[\s\t\n,]+/', $attrib['images'], -1, PREG_SPLIT_NO_EMPTY);
+        $images = array_map(array($this, 'abs_url'), $images);
+
+        if (empty($images) || $this->app->task == 'logout')
+            return;
+
+        $this->add_script('$(document).ready(function(){
+            var images = ' . json_serialize($images) .';
+            for (var i=0; i<images.length; i++) {
+                img = new Image();
+                img.src = images[i];
+            }});', 'foot');
     }
 
 
@@ -1018,6 +1138,10 @@
         if (empty($attrib['id'])) {
             $attrib['id'] = 'rcmqsearchbox';
         }
+        if ($attrib['type'] == 'search' && !$this->browser->khtml) {
+            unset($attrib['type'], $attrib['results']);
+        }
+
         $input_q = new html_inputfield($attrib);
         $out = $input_q->show();
 
@@ -1029,7 +1153,7 @@
                 'name' => "rcmqsearchform",
                 'onsubmit' => JS_OBJECT_NAME . ".command('search');return false;",
                 'style' => "display:inline"),
-              $out);
+                $out);
         }
 
         return $out;
@@ -1059,39 +1183,67 @@
      * @param array Named parameters for the select tag
      * @return string HTML code for the gui object
      */
-    static function charset_selector($attrib)
+    function charset_selector($attrib)
     {
         // pass the following attributes to the form class
         $field_attrib = array('name' => '_charset');
         foreach ($attrib as $attr => $value) {
-            if (in_array($attr, array('id', 'class', 'style', 'size', 'tabindex'))) {
+            if (in_array($attr, array('id', 'name', 'class', 'style', 'size', 'tabindex'))) {
                 $field_attrib[$attr] = $value;
             }
         }
+
         $charsets = array(
-            'US-ASCII'     => 'ASCII (English)',
-            'EUC-JP'       => 'EUC-JP (Japanese)',
-            'EUC-KR'       => 'EUC-KR (Korean)',
-            'BIG5'         => 'BIG5 (Chinese)',
-            'GB2312'       => 'GB2312 (Chinese)',
-            'ISO-2022-JP'  => 'ISO-2022-JP (Japanese)',
-            'ISO-8859-1'   => 'ISO-8859-1 (Latin-1)',
-            'ISO-8859-2'   => 'ISO-8895-2 (Central European)',
-            'ISO-8859-7'   => 'ISO-8859-7 (Greek)',
-            'ISO-8859-9'   => 'ISO-8859-9 (Turkish)',
-            'Windows-1251' => 'Windows-1251 (Cyrillic)',
-            'Windows-1252' => 'Windows-1252 (Western)',
-            'Windows-1255' => 'Windows-1255 (Hebrew)',
-            'Windows-1256' => 'Windows-1256 (Arabic)',
-            'Windows-1257' => 'Windows-1257 (Baltic)',
-            'UTF-8'        => 'UTF-8'
-            );
+            'UTF-8'        => 'UTF-8 ('.rcube_label('unicode').')',
+            'US-ASCII'     => 'ASCII ('.rcube_label('english').')',
+            'ISO-8859-1'   => 'ISO-8859-1 ('.rcube_label('westerneuropean').')',
+            'ISO-8859-2'   => 'ISO-8895-2 ('.rcube_label('easterneuropean').')',
+            'ISO-8859-4'   => 'ISO-8895-4 ('.rcube_label('baltic').')',
+            'ISO-8859-5'   => 'ISO-8859-5 ('.rcube_label('cyrillic').')',
+            'ISO-8859-6'   => 'ISO-8859-6 ('.rcube_label('arabic').')',
+            'ISO-8859-7'   => 'ISO-8859-7 ('.rcube_label('greek').')',
+            'ISO-8859-8'   => 'ISO-8859-8 ('.rcube_label('hebrew').')',
+            'ISO-8859-9'   => 'ISO-8859-9 ('.rcube_label('turkish').')',
+            'ISO-8859-10'   => 'ISO-8859-10 ('.rcube_label('nordic').')',
+            'ISO-8859-11'   => 'ISO-8859-11 ('.rcube_label('thai').')',
+            'ISO-8859-13'   => 'ISO-8859-13 ('.rcube_label('baltic').')',
+            'ISO-8859-14'   => 'ISO-8859-14 ('.rcube_label('celtic').')',
+            'ISO-8859-15'   => 'ISO-8859-15 ('.rcube_label('westerneuropean').')',
+            'ISO-8859-16'   => 'ISO-8859-16 ('.rcube_label('southeasterneuropean').')',
+            'WINDOWS-1250' => 'Windows-1250 ('.rcube_label('easterneuropean').')',
+            'WINDOWS-1251' => 'Windows-1251 ('.rcube_label('cyrillic').')',
+            'WINDOWS-1252' => 'Windows-1252 ('.rcube_label('westerneuropean').')',
+            'WINDOWS-1253' => 'Windows-1253 ('.rcube_label('greek').')',
+            'WINDOWS-1254' => 'Windows-1254 ('.rcube_label('turkish').')',
+            'WINDOWS-1255' => 'Windows-1255 ('.rcube_label('hebrew').')',
+            'WINDOWS-1256' => 'Windows-1256 ('.rcube_label('arabic').')',
+            'WINDOWS-1257' => 'Windows-1257 ('.rcube_label('baltic').')',
+            'WINDOWS-1258' => 'Windows-1258 ('.rcube_label('vietnamese').')',
+            'ISO-2022-JP'  => 'ISO-2022-JP ('.rcube_label('japanese').')',
+            'ISO-2022-KR'  => 'ISO-2022-KR ('.rcube_label('korean').')',
+            'ISO-2022-CN'  => 'ISO-2022-CN ('.rcube_label('chinese').')',
+            'EUC-JP'       => 'EUC-JP ('.rcube_label('japanese').')',
+            'EUC-KR'       => 'EUC-KR ('.rcube_label('korean').')',
+            'EUC-CN'       => 'EUC-CN ('.rcube_label('chinese').')',
+            'BIG5'         => 'BIG5 ('.rcube_label('chinese').')',
+            'GB2312'       => 'GB2312 ('.rcube_label('chinese').')',
+        );
 
-            $select = new html_select($field_attrib);
-            $select->add(array_values($charsets), array_keys($charsets));
+        if (!empty($_POST['_charset']))
+	        $set = $_POST['_charset'];
+	    else if (!empty($attrib['selected']))
+	        $set = $attrib['selected'];
+	    else
+	        $set = $this->get_charset();
 
-            $set = $_POST['_charset'] ? $_POST['_charset'] : $this->get_charset();
-            return $select->show($set);
+	    $set = strtoupper($set);
+	    if (!isset($charsets[$set]))
+	        $charsets[$set] = $set;
+
+        $select = new html_select($field_attrib);
+        $select->add(array_values($charsets), array_keys($charsets));
+
+        return $select->show($set);
     }
 
 }  // end class rcube_template

--
Gitblit v1.9.1