+-----------------------------------------------------------------------+
| program/include/rcube_template.php |
| |
- | This file is part of the RoundCube Webmail client |
- | Copyright (C) 2006-2009, RoundCube Dev. - Switzerland |
+ | This file is part of the Roundcube Webmail client |
+ | Copyright (C) 2006-2011, The Roundcube Dev Team |
| Licensed under the GNU GPL |
| |
| PURPOSE: |
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
- $Id$
+ $Id: rcube_template.php 5481 2011-11-24 07:53:00Z alec $
*/
*/
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_labels = array();
+ private $js_commands = array();
+ private $object_handlers = array();
+ private $plugin_skin_path;
+ private $template_name;
+
+ public $browser;
+ public $framed = false;
+ public $env = array();
public $type = 'html';
public $ajax_call = false;
+ // deprecated names of templates used before 0.5
+ private $deprecated_templates = array(
+ 'contact' => 'showcontact',
+ 'contactadd' => 'addcontact',
+ 'contactedit' => 'editcontact',
+ 'identityedit' => 'editidentity',
+ 'messageprint' => 'printmessage',
+ );
+
/**
* Constructor
*
- * @todo Use jQuery's $(document).ready() here.
* @todo Replace $this->config with the real rcube_config object
*/
public function __construct($task, $framed = false)
$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('x_frame_options', $this->app->config->get('x_frame_options', 'sameorigin'));
// load the correct skin (in case user-defined)
$this->set_skin($this->config['skin']);
// add common javascripts
- $javascript = 'var '.JS_OBJECT_NAME.' = new rcube_webmail();';
+ $this->add_script('var '.JS_OBJECT_NAME.' = new rcube_webmail();', 'head_top');
// 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."');";
+ $this->add_script(JS_OBJECT_NAME.'.init();', 'docready');
- $this->add_script($javascript, 'head_top');
- $this->add_script($javascript_foot, 'foot');
$this->scripts_path = 'program/js/';
+ $this->include_script('jquery.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'),
}
}
-
/**
* Set page title variable
*/
$this->pagetitle = $title;
}
-
/**
* Getter for the current page title
*
else {
$title = ucfirst($this->env['task']);
}
-
+
return $title;
}
-
/**
* Set skin
*/
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;
}
/**
public function template_exists($name)
{
$filename = $this->config['skin_path'] . '/templates/' . $name . '.html';
-
- return (is_file($filename) && is_readable($filename));
+ return (is_file($filename) && is_readable($filename)) || ($this->deprecated_templates[$name] && $this->template_exists($this->deprecated_templates[$name]));
}
/**
*/
public function command()
{
- $this->js_commands[] = func_get_args();
+ $cmd = func_get_args();
+ if (strpos($cmd[0], 'plugin.') !== false)
+ $this->js_commands[] = array('triggerEvent', $cmd[0], $cmd[1]);
+ else
+ $this->js_commands[] = $cmd;
}
-
/**
* Add a localized label to the client environment
*/
public function add_label()
{
- $arg_list = func_get_args();
- foreach ($arg_list as $i => $name) {
- $this->command('add_label', $name, rcube_label($name));
+ $args = func_get_args();
+ if (count($args) == 1 && is_array($args[0]))
+ $args = $args[0];
+
+ foreach ($args as $name) {
+ $this->js_labels[$name] = rcube_label($name);
}
}
-
/**
* Invoke display_message command
*
- * @param string Message to display
- * @param string Message type [notice|confirm|error]
- * @param array Key-value pairs to be replaced in localized text
+ * @param string $message Message to display
+ * @param string $type Message type [notice|confirm|error]
+ * @param array $vars Key-value pairs to be replaced in localized text
+ * @param boolean $override Override last set message
+ * @param int $timeout Message display time in seconds
* @uses self::command()
*/
- public function show_message($message, $type='notice', $vars=NULL)
+ public function show_message($message, $type='notice', $vars=null, $override=true, $timeout=0)
{
- $this->command(
- 'display_message',
- rcube_label(array('name' => $message, 'vars' => $vars)),
- $type);
- }
+ if ($override || !$this->message) {
+ if (rcube_label_exists($message)) {
+ if (!empty($vars))
+ $vars = array_map('Q', $vars);
+ $msgtext = rcube_label(array('name' => $message, 'vars' => $vars));
+ }
+ else
+ $msgtext = $message;
+ $this->message = $message;
+ $this->command('display_message', $msgtext, $type, $timeout * 1000);
+ }
+ }
/**
* Delete all stored env variables and commands
{
$this->env = array();
$this->js_env = array();
+ $this->js_labels = array();
$this->js_commands = array();
$this->object_handlers = array();
parent::reset();
}
-
/**
* Redirect to a certain url
*
exit;
}
-
/**
* Send the request output to the client.
* This will either parse a skin tempalte or send an AJAX response
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->write();
}
+ // set output asap
+ ob_flush();
+ flush();
+
if ($exit) {
exit;
}
public function write($template = '')
{
// unlock interface after iframe load
+ $unlock = preg_replace('/[^a-z0-9]/i', '', $_REQUEST['_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));
+ }
+
+ if (!empty($this->script_files))
+ $this->set_env('request_token', $this->app->get_request_token());
+
// 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');
+ // send clickjacking protection headers
+ $iframe = $this->framed || !empty($_REQUEST['_framed']);
+ if (!headers_sent() && ($xframe = $this->app->config->get('x_frame_options', 'sameorigin')))
+ header('X-Frame-Options: ' . ($iframe && $xframe == 'deny' ? 'sameorigin' : $xframe));
+
// call super method
parent::write($template, $this->config['skin_path']);
}
/**
- * Parse a specific skin template and deliver to stdout
- *
- * Either returns nothing, or exists hard (exit();)
+ * Parse a specific skin template and deliver to stdout (or return)
*
* @param string Template name
* @param boolean Exit script
- * @return void
+ * @param boolean Don't write to stdout, return parsed content instead
+ *
* @link http://php.net/manual/en/function.exit.php
*/
- private function parse($name = 'main', $exit = true)
+ function parse($name = 'main', $exit = true, $write = true)
{
$skin_path = $this->config['skin_path'];
+ $plugin = false;
+ $realname = $name;
+ $temp = explode('.', $name, 2);
+
+ $this->plugin_skin_path = null;
+ $this->template_name = $realname;
+
+ if (count($temp) > 1) {
+ $plugin = $temp[0];
+ $name = $temp[1];
+ $skin_dir = $plugin . '/skins/' . $this->config['skin'];
+ $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir;
+
+ // fallback to default skin
+ if (!is_dir($skin_path)) {
+ $skin_dir = $plugin . '/skins/default';
+ $skin_path = $this->plugin_skin_path = $this->app->plugins->dir . $skin_dir;
+ }
+ }
+
$path = "$skin_path/templates/$name.html";
+ if (!is_readable($path) && $this->deprecated_templates[$realname]) {
+ $path = "$skin_path/templates/".$this->deprecated_templates[$realname].".html";
+ if (is_readable($path))
+ raise_error(array('code' => 502, 'type' => 'php',
+ 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Using deprecated template '".$this->deprecated_templates[$realname]
+ ."' in ".$this->config['skin_path']."/templates. Please rename to '".$realname."'"),
+ true, false);
+ }
+
// read template file
if (($templ = @file_get_contents($path)) === false) {
raise_error(array(
'type' => 'php',
'line' => __LINE__,
'file' => __FILE__,
- 'message' => 'Error loading template for '.$name
+ 'message' => 'Error loading template for '.$realname
), true, true);
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);
- // 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" spellcheck="false"></textarea></form></div>'
- );
+ // trigger generic hook where plugins can put additional content to the page
+ $hook = $this->app->plugins->exec_hook("render_page", array('template' => $realname, 'content' => $output));
+
+ $output = $this->parse_with_globals($hook['content']);
+
+ // make sure all <form> tags have a valid request token
+ $output = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $output);
+ $this->footer = preg_replace_callback('/<form\s+([^>]+)>/Ui', array($this, 'alter_form_tag'), $this->footer);
+
+ if ($write) {
+ // add debug console
+ if ($realname != 'error' && ($this->config['debug_level'] & 8)) {
+ $this->add_footer('<div id="console" style="position:absolute;top:5px;left:5px;width:405px;padding:2px;background:white;z-index:9000;display:none">
+ <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>'
+ );
+ $this->add_script(
+ "if (!window.console || !window.console.log) {\n".
+ " window.console = new rcube_console();\n".
+ " $('#console').show();\n".
+ "}", 'foot');
+ }
+ $this->write(trim($output));
}
- $output = $this->parse_with_globals($output);
- $this->write(trim($output), $skin_path);
+ else {
+ return $output;
+ }
+
if ($exit) {
exit;
}
}
-
/**
* Return executable javascript code for all registered commands
*
if (!$this->framed && !empty($this->js_env)) {
$out .= JS_OBJECT_NAME . '.set_env('.json_serialize($this->js_env).");\n";
}
+ if (!empty($this->js_labels)) {
+ $this->command('add_label', $this->js_labels);
+ }
foreach ($this->js_commands as $i => $args) {
$method = array_shift($args);
foreach ($args as $i => $arg) {
$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;
}
*/
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;
}
*/
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]];
}
/**
*/
private function parse_conditions($input)
{
- $matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE);
+ $matches = preg_split('/<roundcube:(if|elseif|else|endif)\s+([^>]+)>\n?/is', $input, 2, PREG_SPLIT_DELIM_CAPTURE);
if ($matches && count($matches) == 4) {
if (preg_match('/^(else|endif)$/i', $matches[1])) {
return $matches[0] . $this->parse_conditions($matches[3]);
$attrib = parse_attrib_string($matches[2]);
if (isset($attrib['condition'])) {
$condmet = $this->check_condition($attrib['condition']);
- $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE);
+ $submatches = preg_split('/<roundcube:(elseif|else|endif)\s+([^>]+)>\n?/is', $matches[3], 2, PREG_SPLIT_DELIM_CAPTURE);
if ($condmet) {
$result = $submatches[0];
- $result.= ($submatches[1] != 'endif' ? preg_replace('/.*<roundcube:endif\s+[^>]+>/Uis', '', $submatches[3], 1) : $submatches[3]);
+ $result.= ($submatches[1] != 'endif' ? preg_replace('/.*<roundcube:endif\s+[^>]+>\n?/Uis', '', $submatches[3], 1) : $submatches[3]);
}
else {
$result = "<roundcube:$submatches[1] $submatches[2]>" . $submatches[3];
*/
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;
}
* Parses expression and replaces variables
*
* @param string Expression statement
- * @return string Expression statement
+ * @return string Expression value
*/
private function parse_expression($expression)
{
'/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',
+ '/template:name/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'}",
+ $this->template_name,
),
$expression);
}
*/
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'])) {
// show a label
case 'label':
if ($attrib['name'] || $attrib['command']) {
- return Q(rcube_label($attrib + array('vars' => array('product' => $this->config['product_name']))));
+ $vars = $attrib + array('product' => $this->config['product_name']);
+ unset($vars['name'], $vars['command']);
+ $label = rcube_label($attrib + array('vars' => $vars));
+ return !$attrib['noshow'] ? (get_boolean((string)$attrib['html']) ? $label : Q($label)) : '';
}
break;
// include a file
case 'include':
- $path = realpath($this->config['skin_path'].$attrib['file']);
+ if (!$this->plugin_skin_path || !is_file($path = realpath($this->plugin_skin_path . $attrib['file'])))
+ $path = realpath(($attrib['skin_path'] ? $attrib['skin_path'] : $this->config['skin_path']).$attrib['file']);
+
if (is_readable($path)) {
if ($this->config['skin_include_php']) {
$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 == 'logo') {
+ $attrib += array('alt' => $this->xml_command(array('', 'object', 'name="productname"')));
+ if ($this->config['skin_logo'])
+ $attrib['src'] = $this->config['skin_logo'];
+ $content = html::img($attrib);
}
- if ($object=='version') {
+ else if ($object == 'productname') {
+ $name = !empty($this->config['product_name']) ? $this->config['product_name'] : 'Roundcube Webmail';
+ $content = Q($name);
+ }
+ 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') {
- $title = !empty($this->config['product_name']) ? $this->config['product_name'].' :: ' : '';
+ else if ($object == 'pagetitle') {
+ if (!empty($this->config['devel_mode']) && !empty($_SESSION['username']))
+ $title = $_SESSION['username'].' :: ';
+ else if (!empty($this->config['product_name']))
+ $title = $this->config['product_name'].' :: ';
+ else
+ $title = '';
$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']);
case 'cookie':
$value = htmlspecialchars($_COOKIE[$name]);
break;
+ case 'browser':
+ $value = $this->browser->{$name};
+ break;
}
if (is_array($value)) {
*/
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 '';
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'];
}
if (!$attrib['id']) {
}
// 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 ($this->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
// make valid href to specific buttons
if (in_array($attrib['command'], rcmail::$main_tasks)) {
$attrib['href'] = rcmail_url(null, null, $attrib['command']);
+ $attrib['onclick'] = sprintf("%s.switch_task('%s');return false", JS_OBJECT_NAME, $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'];
}
}
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,
$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']
- );
- }
$out = '';
$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') {
$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);
*/
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']) {
+ if ($this->framed || !empty($_REQUEST['_framed']))
+ $hidden->add(array('name' => '_framed', 'value' => '1'));
+ return $hidden->show() . $content;
+ }
+ else
+ return $this->form_tag($attrib, $hidden->show() . $content);
}
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 rcube_idn_to_utf8($username);
}
private function login_form($attrib)
{
$default_host = $this->config['default_host'];
+ $autocomplete = (int) $this->config['login_autocomplete'];
$_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'];
+
+ // set atocomplete attribute
+ $user_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off');
+ $host_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off');
+ $pass_attrib = $autocomplete > 1 ? array() : array('autocomplete' => 'off');
+
+ $input_task = new html_hiddenfield(array('name' => '_task', 'value' => 'login'));
$input_action = new html_hiddenfield(array('name' => '_action', 'value' => 'login'));
$input_tzone = new html_hiddenfield(array('name' => '_timezone', 'id' => 'rcmlogintz', 'value' => '_default_'));
+ $input_dst = new html_hiddenfield(array('name' => '_dstactive', 'id' => 'rcmlogindst', 'value' => '_default_'));
+ $input_url = new html_hiddenfield(array('name' => '_url', 'id' => 'rcmloginurl', 'value' => $url));
+ $input_user = new html_inputfield(array('name' => '_user', 'id' => 'rcmloginuser')
+ + $attrib + $user_attrib);
+ $input_pass = new html_passwordfield(array('name' => '_pass', 'id' => 'rcmloginpwd')
+ + $attrib + $pass_attrib);
$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) {
}
}
}
+ 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 + $host_attrib);
}
$form_name = !empty($attrib['form']) ? $attrib['form'] : 'form';
$table = new html_table(array('cols' => 2));
$table->add('title', html::label('rcmloginuser', Q(rcube_label('username'))));
- $table->add(null, $input_user->show(get_input_value('_user', RCUBE_INPUT_POST)));
+ $table->add('input', $input_user->show(get_input_value('_user', RCUBE_INPUT_GPC)));
$table->add('title', html::label('rcmloginpwd', Q(rcube_label('password'))));
- $table->add(null, $input_pass->show());
+ $table->add('input', $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)));
+ $table->add('input', $input_host->show(get_input_value('_host', RCUBE_INPUT_GPC)));
}
- $out = $input_action->show();
+ $out = $input_task->show();
+ $out .= $input_action->show();
$out .= $input_tzone->show();
+ $out .= $input_dst->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('var images = ' . json_serialize($images) .';
+ for (var i=0; i<images.length; i++) {
+ img = new Image();
+ img.src = images[i];
+ }', 'docready');
+ }
+
+
/**
* GUI object 'searchform'
* Returns code for search function
$attrib['id'] = 'rcmqsearchbox';
}
if ($attrib['type'] == 'search' && !$this->browser->khtml) {
- unset($attrib['type'], $attrib['results']);
+ unset($attrib['type'], $attrib['results']);
}
-
+
$input_q = new html_inputfield($attrib);
$out = $input_q->show();
'name' => "rcmqsearchform",
'onsubmit' => JS_OBJECT_NAME . ".command('search');return false;",
'style' => "display:inline"),
- $out);
+ $out);
}
return $out;
* @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'
- );
- $select = new html_select($field_attrib);
- $select->add(array_values($charsets), array_keys($charsets));
-
- $set = $_POST['_charset'] ? $_POST['_charset'] : $this->get_charset();
- return $select->show($set);
+ $charsets = array(
+ '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-8859-2 ('.rcube_label('easterneuropean').')',
+ 'ISO-8859-4' => 'ISO-8859-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').')',
+ );
+
+ if (!empty($_POST['_charset']))
+ $set = $_POST['_charset'];
+ else if (!empty($attrib['selected']))
+ $set = $attrib['selected'];
+ else
+ $set = $this->get_charset();
+
+ $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