diff --git a/NEWS b/NEWS index 396556a7..48febe25 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,8 @@ + - added caching to config file loading (Monte) + - added "extra" parameter to mailto function (Monte, + Massimiliano Perantoni) + - added mailto function to default plugin list (Monte) + Version 2.3.1 ------------- diff --git a/Smarty.class.php b/Smarty.class.php index 76579f63..78cb4b87 100644 --- a/Smarty.class.php +++ b/Smarty.class.php @@ -109,6 +109,16 @@ class Smarty var $cache_modified_check = false; // respect If-Modified-Since headers on cached content + var $config_overwrite = true; // whether config file variables of the + // same name overwrite each other or not + var $config_booleanize = true; // whether config file values of + // on/true/yes and off/false/no get converted + // to boolean values automatically + var $config_read_hidden = false; // whether hidden sections [.foobar] + // are readable from the templates or not + var $config_fix_newlines = true; // whether or not to fix mac or dos formatted + // newlines [\r\n] -> \n + var $default_template_handler_func = ''; // function to handle missing templates var $php_handling = SMARTY_PHP_PASSTHRU; @@ -142,6 +152,8 @@ class Smarty var $compiler_class = 'Smarty_Compiler'; // the compiler class used by // Smarty to compile templates + var $config_class = 'Config_File'; // the config class used by + // Smarty to load config vars var $request_vars_order = "EGPCS"; // the order in which request variables are // registered, similar to variables_order @@ -169,7 +181,8 @@ class Smarty var $_foreach = array(); // keeps track of foreach blocks var $_tag_stack = array(); // keeps track of tag hierarchy var $_conf_obj = null; // configuration object - var $_config = array(); // loaded configuration settings + var $_config = array( // loaded configuration settings + array('vars' => array(), 'files' => array())); var $_smarty_md5 = 'f8d698aea36fcbead2b9d5359ffca76f'; // md5 checksum of the string 'Smarty' var $_version = '2.3.1'; // Smarty version number var $_extract = false; // flag for custom functions @@ -670,8 +683,10 @@ class Smarty extract($this->_tpl_vars); /* Initialize config array. */ + /* $this->_config = array(array('vars' => array(), 'files' => array())); + */ if (count($this->autoload_filters)) $this->_autoload_filters(); @@ -1104,9 +1119,9 @@ function _generate_debug_output() { $smarty_compiler->_tpl_vars = &$this->_tpl_vars; $smarty_compiler->default_modifiers = $this->default_modifiers; - if ($smarty_compiler->_compile_file($tpl_file, $template_source, $template_compiled)) + if ($smarty_compiler->_compile_file($tpl_file, $template_source, $template_compiled)) { return true; - else { + } else { $this->trigger_error($smarty_compiler->_error_msg); return false; } @@ -1129,14 +1144,19 @@ function _generate_debug_output() { $this->_tpl_vars = array_merge($this->_tpl_vars, $_smarty_include_vars); extract($this->_tpl_vars); + // config vars are treated as local, so push a copy of the + // current ones onto the front of the stack array_unshift($this->_config, $this->_config[0]); + $_smarty_compile_path = $this->_get_compile_path($_smarty_include_tpl_file); if ($this->_process_template($_smarty_include_tpl_file, $_smarty_compile_path)) { include($_smarty_compile_path); } + // pop the local vars off the front of the stack array_shift($this->_config); + $this->_inclusion_depth--; if ($this->debugging) { @@ -1184,11 +1204,28 @@ function _generate_debug_output() { } } + /*======================================================================*\ - Function: _config_load + Function: clear_config + Purpose: clear configuration values +\*======================================================================*/ + function clear_config($var = null) + { + if(!isset($var)) { + // clear all values + $this->_config = array(array('vars' => array(), + 'files' => array())); + } else { + unset($this->_config[0]['vars'][$var]); + } + } + + +/*======================================================================*\ + Function: config_load Purpose: load configuration values \*======================================================================*/ - function _config_load($file, $section, $scope) + function config_load($file, $section = null, $scope = 'global') { if(@is_dir($this->config_dir)) { $_config_dir = $this->config_dir; @@ -1196,17 +1233,35 @@ function _generate_debug_output() { // config_dir not found, try include_path $this->_get_include_path($this->config_dir,$_config_dir); } - - if ($this->_conf_obj === null) { - /* Prepare the configuration object. */ - if (!class_exists('Config_File')) - require_once SMARTY_DIR.'Config_File.class.php'; - $this->_conf_obj = new Config_File($_config_dir); - $this->_conf_obj->read_hidden = false; - } else { - $this->_conf_obj->set_path($_config_dir); - } + $_file_path = $_config_dir . '/' . $file; + + if(!is_object($this->_conf_obj)) { + require_once SMARTY_DIR . $this->config_class . '.class.php'; + $this->_conf_obj = new Config_File($_config_dir); + $this->_conf_obj->overwrite = $this->config_overwrite; + $this->_conf_obj->booleanize = $this->config_booleanize; + $this->_conf_obj->read_hidden = $this->config_read_hidden; + $this->_conf_obj->fix_newlines = $this->config_fix_newlines; + } + + if(!isset($this->_config[0]['files'][$file])) { + // get path to compiled object file + $_cache_file = $this->_get_auto_filename($this->cache_dir, $file, 'SMARTY_CONFIG'); + + // see if cache file is up to date + if(@filemtime($_cache_file) == @filemtime($_file_path)) { + // load cache file + $this->_conf_obj = unserialize($this->_read_file($_cache_file)); + } else { + // load file and save cache + $this->_conf_obj->load_file($file); + $_conf_obj_file_contents = serialize($this->_conf_obj); + $this->_write_file($_cache_file, $_conf_obj_file_contents, true); + touch($_cache_file,filemtime($_file_path)); + } + } + if ($this->debugging) { $debug_start_time = $this->_get_microtime(); } @@ -1216,30 +1271,32 @@ function _generate_debug_output() { } if (!isset($this->_config[0]['files'][$file])) { - $this->_config[0]['vars'] = array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file)); + $this->_config[0]['vars'] = @array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file)); $this->_config[0]['files'][$file] = true; } + if ($scope == 'parent') { if (count($this->_config) > 0 && !isset($this->_config[1]['files'][$file])) { - $this->_config[1]['vars'] = array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file)); + $this->_config[1]['vars'] = @array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file)); $this->_config[1]['files'][$file] = true; } - } else if ($scope == 'global') + } else if ($scope == 'global') { for ($i = 1, $for_max = count($this->_config); $i < $for_max; $i++) { if (!isset($this->_config[$i]['files'][$file])) { - $this->_config[$i]['vars'] = array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file)); + $this->_config[$i]['vars'] = @array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file)); $this->_config[$i]['files'][$file] = true; } } - + } + if (!empty($section)) { - $this->_config[0]['vars'] = array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[0]['vars'] = @array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file, $section)); if ($scope == 'parent') { if (count($this->_config) > 0) - $this->_config[1]['vars'] = array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[1]['vars'] = @array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file, $section)); } else if ($scope == 'global') for ($i = 1, $for_max = count($this->_config); $i < $for_max; $i++) - $this->_config[$i]['vars'] = array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[$i]['vars'] = @array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file, $section)); } if ($this->debugging) { @@ -1249,6 +1306,7 @@ function _generate_debug_output() { 'depth' => $this->_inclusion_depth, 'exec_time' => $this->_get_microtime() - $debug_start_time); } + } diff --git a/Smarty_Compiler.class.php b/Smarty_Compiler.class.php index 5a2beaed..f0cd8af4 100644 --- a/Smarty_Compiler.class.php +++ b/Smarty_Compiler.class.php @@ -555,7 +555,7 @@ class Smarty_Compiler extends Smarty { $scope = 'local'; } - $output = '_config_load(' . $attrs['file'] . ', ' . $attrs['section'] . ", '$scope'); ?>"; + $output = 'config_load(' . $attrs['file'] . ', ' . $attrs['section'] . ", '$scope'); ?>"; return $output; } diff --git a/docs/designers.sgml b/docs/designers.sgml index 3db4c042..c6cbcb06 100644 --- a/docs/designers.sgml +++ b/docs/designers.sgml @@ -3138,10 +3138,10 @@ You must supply a <b>state</b>. selected - string + string/array No empty - the selected array element + the selected array element(s) options diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index 76579f63..78cb4b87 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -109,6 +109,16 @@ class Smarty var $cache_modified_check = false; // respect If-Modified-Since headers on cached content + var $config_overwrite = true; // whether config file variables of the + // same name overwrite each other or not + var $config_booleanize = true; // whether config file values of + // on/true/yes and off/false/no get converted + // to boolean values automatically + var $config_read_hidden = false; // whether hidden sections [.foobar] + // are readable from the templates or not + var $config_fix_newlines = true; // whether or not to fix mac or dos formatted + // newlines [\r\n] -> \n + var $default_template_handler_func = ''; // function to handle missing templates var $php_handling = SMARTY_PHP_PASSTHRU; @@ -142,6 +152,8 @@ class Smarty var $compiler_class = 'Smarty_Compiler'; // the compiler class used by // Smarty to compile templates + var $config_class = 'Config_File'; // the config class used by + // Smarty to load config vars var $request_vars_order = "EGPCS"; // the order in which request variables are // registered, similar to variables_order @@ -169,7 +181,8 @@ class Smarty var $_foreach = array(); // keeps track of foreach blocks var $_tag_stack = array(); // keeps track of tag hierarchy var $_conf_obj = null; // configuration object - var $_config = array(); // loaded configuration settings + var $_config = array( // loaded configuration settings + array('vars' => array(), 'files' => array())); var $_smarty_md5 = 'f8d698aea36fcbead2b9d5359ffca76f'; // md5 checksum of the string 'Smarty' var $_version = '2.3.1'; // Smarty version number var $_extract = false; // flag for custom functions @@ -670,8 +683,10 @@ class Smarty extract($this->_tpl_vars); /* Initialize config array. */ + /* $this->_config = array(array('vars' => array(), 'files' => array())); + */ if (count($this->autoload_filters)) $this->_autoload_filters(); @@ -1104,9 +1119,9 @@ function _generate_debug_output() { $smarty_compiler->_tpl_vars = &$this->_tpl_vars; $smarty_compiler->default_modifiers = $this->default_modifiers; - if ($smarty_compiler->_compile_file($tpl_file, $template_source, $template_compiled)) + if ($smarty_compiler->_compile_file($tpl_file, $template_source, $template_compiled)) { return true; - else { + } else { $this->trigger_error($smarty_compiler->_error_msg); return false; } @@ -1129,14 +1144,19 @@ function _generate_debug_output() { $this->_tpl_vars = array_merge($this->_tpl_vars, $_smarty_include_vars); extract($this->_tpl_vars); + // config vars are treated as local, so push a copy of the + // current ones onto the front of the stack array_unshift($this->_config, $this->_config[0]); + $_smarty_compile_path = $this->_get_compile_path($_smarty_include_tpl_file); if ($this->_process_template($_smarty_include_tpl_file, $_smarty_compile_path)) { include($_smarty_compile_path); } + // pop the local vars off the front of the stack array_shift($this->_config); + $this->_inclusion_depth--; if ($this->debugging) { @@ -1184,11 +1204,28 @@ function _generate_debug_output() { } } + /*======================================================================*\ - Function: _config_load + Function: clear_config + Purpose: clear configuration values +\*======================================================================*/ + function clear_config($var = null) + { + if(!isset($var)) { + // clear all values + $this->_config = array(array('vars' => array(), + 'files' => array())); + } else { + unset($this->_config[0]['vars'][$var]); + } + } + + +/*======================================================================*\ + Function: config_load Purpose: load configuration values \*======================================================================*/ - function _config_load($file, $section, $scope) + function config_load($file, $section = null, $scope = 'global') { if(@is_dir($this->config_dir)) { $_config_dir = $this->config_dir; @@ -1196,17 +1233,35 @@ function _generate_debug_output() { // config_dir not found, try include_path $this->_get_include_path($this->config_dir,$_config_dir); } - - if ($this->_conf_obj === null) { - /* Prepare the configuration object. */ - if (!class_exists('Config_File')) - require_once SMARTY_DIR.'Config_File.class.php'; - $this->_conf_obj = new Config_File($_config_dir); - $this->_conf_obj->read_hidden = false; - } else { - $this->_conf_obj->set_path($_config_dir); - } + $_file_path = $_config_dir . '/' . $file; + + if(!is_object($this->_conf_obj)) { + require_once SMARTY_DIR . $this->config_class . '.class.php'; + $this->_conf_obj = new Config_File($_config_dir); + $this->_conf_obj->overwrite = $this->config_overwrite; + $this->_conf_obj->booleanize = $this->config_booleanize; + $this->_conf_obj->read_hidden = $this->config_read_hidden; + $this->_conf_obj->fix_newlines = $this->config_fix_newlines; + } + + if(!isset($this->_config[0]['files'][$file])) { + // get path to compiled object file + $_cache_file = $this->_get_auto_filename($this->cache_dir, $file, 'SMARTY_CONFIG'); + + // see if cache file is up to date + if(@filemtime($_cache_file) == @filemtime($_file_path)) { + // load cache file + $this->_conf_obj = unserialize($this->_read_file($_cache_file)); + } else { + // load file and save cache + $this->_conf_obj->load_file($file); + $_conf_obj_file_contents = serialize($this->_conf_obj); + $this->_write_file($_cache_file, $_conf_obj_file_contents, true); + touch($_cache_file,filemtime($_file_path)); + } + } + if ($this->debugging) { $debug_start_time = $this->_get_microtime(); } @@ -1216,30 +1271,32 @@ function _generate_debug_output() { } if (!isset($this->_config[0]['files'][$file])) { - $this->_config[0]['vars'] = array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file)); + $this->_config[0]['vars'] = @array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file)); $this->_config[0]['files'][$file] = true; } + if ($scope == 'parent') { if (count($this->_config) > 0 && !isset($this->_config[1]['files'][$file])) { - $this->_config[1]['vars'] = array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file)); + $this->_config[1]['vars'] = @array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file)); $this->_config[1]['files'][$file] = true; } - } else if ($scope == 'global') + } else if ($scope == 'global') { for ($i = 1, $for_max = count($this->_config); $i < $for_max; $i++) { if (!isset($this->_config[$i]['files'][$file])) { - $this->_config[$i]['vars'] = array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file)); + $this->_config[$i]['vars'] = @array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file)); $this->_config[$i]['files'][$file] = true; } } - + } + if (!empty($section)) { - $this->_config[0]['vars'] = array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[0]['vars'] = @array_merge($this->_config[0]['vars'], $this->_conf_obj->get($file, $section)); if ($scope == 'parent') { if (count($this->_config) > 0) - $this->_config[1]['vars'] = array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[1]['vars'] = @array_merge($this->_config[1]['vars'], $this->_conf_obj->get($file, $section)); } else if ($scope == 'global') for ($i = 1, $for_max = count($this->_config); $i < $for_max; $i++) - $this->_config[$i]['vars'] = array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file, $section)); + $this->_config[$i]['vars'] = @array_merge($this->_config[$i]['vars'], $this->_conf_obj->get($file, $section)); } if ($this->debugging) { @@ -1249,6 +1306,7 @@ function _generate_debug_output() { 'depth' => $this->_inclusion_depth, 'exec_time' => $this->_get_microtime() - $debug_start_time); } + } diff --git a/libs/Smarty_Compiler.class.php b/libs/Smarty_Compiler.class.php index 5a2beaed..f0cd8af4 100644 --- a/libs/Smarty_Compiler.class.php +++ b/libs/Smarty_Compiler.class.php @@ -555,7 +555,7 @@ class Smarty_Compiler extends Smarty { $scope = 'local'; } - $output = '_config_load(' . $attrs['file'] . ', ' . $attrs['section'] . ", '$scope'); ?>"; + $output = 'config_load(' . $attrs['file'] . ', ' . $attrs['section'] . ", '$scope'); ?>"; return $output; } diff --git a/libs/plugins/function.mailto.php b/libs/plugins/function.mailto.php new file mode 100644 index 00000000..21180dbb --- /dev/null +++ b/libs/plugins/function.mailto.php @@ -0,0 +1,124 @@ + + * Credits: Jason Sweat (added cc, bcc and subject functionality) + * Purpose: automate mailto address link creation, and optionally + * encode them. + * Input: address = e-mail address + * text = (optional) text to display, default is address + * encode = (optional) can be one of: + * none : no encoding (default) + * javascript : encode with javascript + * hex : encode with hexidecimal (no javascript) + * cc = (optional) address(es) to carbon copy + * bcc = (optional) address(es) to blind carbon copy + * subject = (optional) e-mail subject + * newsgroups = (optional) newsgroup(s) to post to + * followupto = (optional) address(es) to follow up to + * extra = (optional) extra tags for the href link + * + * Examples: {mailto address="me@domain.com"} + * {mailto address="me@domain.com" encode="javascript"} + * {mailto address="me@domain.com" encode="hex"} + * {mailto address="me@domain.com" subject="Hello to you!"} + * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"} + * {mailto address="me@domain.com" extra='class="mailto"'} + * ------------------------------------------------------------- + */ +function smarty_function_mailto($params, &$smarty) +{ + extract($params); + + if (empty($address)) { + $smarty->trigger_error("mailto: missing 'address' parameter"); + return; + } + + if (empty($text)) { + $text = $address; + } + + // netscape and mozilla do not decode %40 (@) in BCC field (bug?) + // so, don't encode it. + + $mail_parms = array(); + if (!empty($cc)) { + $mail_parms[] = 'cc='.str_replace('%40','@',rawurlencode($cc)); + } + + if (!empty($bcc)) { + $mail_parms[] = 'bcc='.str_replace('%40','@',rawurlencode($bcc)); + } + + if (!empty($subject)) { + $mail_parms[] = 'subject='.rawurlencode($subject); + } + + if (!empty($newsgroups)) { + $mail_parms[] = 'newsgroups='.rawurlencode($newsgroups); + } + + if (!empty($followupto)) { + $mail_parms[] = 'followupto='.str_replace('%40','@',rawurlencode($followupto)); + } + + for ($i=0; $itrigger_error("mailto: 'encode' parameter must be none, javascript or hex"); + return; + } + + if ($encode == 'javascript' ) { + $string = 'document.write(\''.$text.'\');'; + + for ($x=0; $x < strlen($string); $x++) { + $js_encode .= '%' . bin2hex($string[$x]); + } + + echo ''; + + } elseif ($encode == 'hex') { + + preg_match('!^(.*)(\?.*)$!',$address,$match); + if(!empty($match[2])) { + $smarty->trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript."); + return; + } + for ($x=0; $x < strlen($address); $x++) { + if(preg_match('!\w!',$address[$x])) { + $address_encode .= '%' . bin2hex($address[$x]); + } else { + $address_encode .= $address[$x]; + } + } + for ($x=0; $x < strlen($text); $x++) { + $text_encode .= '&#x' . bin2hex($text[$x]).';'; + } + + echo ''.$text_encode.''; + + } else { + // no encoding + echo ''.$text.''; + + } + +} + +/* vim: set expandtab: */ + +?> diff --git a/plugins/function.mailto.php b/plugins/function.mailto.php new file mode 100644 index 00000000..21180dbb --- /dev/null +++ b/plugins/function.mailto.php @@ -0,0 +1,124 @@ + + * Credits: Jason Sweat (added cc, bcc and subject functionality) + * Purpose: automate mailto address link creation, and optionally + * encode them. + * Input: address = e-mail address + * text = (optional) text to display, default is address + * encode = (optional) can be one of: + * none : no encoding (default) + * javascript : encode with javascript + * hex : encode with hexidecimal (no javascript) + * cc = (optional) address(es) to carbon copy + * bcc = (optional) address(es) to blind carbon copy + * subject = (optional) e-mail subject + * newsgroups = (optional) newsgroup(s) to post to + * followupto = (optional) address(es) to follow up to + * extra = (optional) extra tags for the href link + * + * Examples: {mailto address="me@domain.com"} + * {mailto address="me@domain.com" encode="javascript"} + * {mailto address="me@domain.com" encode="hex"} + * {mailto address="me@domain.com" subject="Hello to you!"} + * {mailto address="me@domain.com" cc="you@domain.com,they@domain.com"} + * {mailto address="me@domain.com" extra='class="mailto"'} + * ------------------------------------------------------------- + */ +function smarty_function_mailto($params, &$smarty) +{ + extract($params); + + if (empty($address)) { + $smarty->trigger_error("mailto: missing 'address' parameter"); + return; + } + + if (empty($text)) { + $text = $address; + } + + // netscape and mozilla do not decode %40 (@) in BCC field (bug?) + // so, don't encode it. + + $mail_parms = array(); + if (!empty($cc)) { + $mail_parms[] = 'cc='.str_replace('%40','@',rawurlencode($cc)); + } + + if (!empty($bcc)) { + $mail_parms[] = 'bcc='.str_replace('%40','@',rawurlencode($bcc)); + } + + if (!empty($subject)) { + $mail_parms[] = 'subject='.rawurlencode($subject); + } + + if (!empty($newsgroups)) { + $mail_parms[] = 'newsgroups='.rawurlencode($newsgroups); + } + + if (!empty($followupto)) { + $mail_parms[] = 'followupto='.str_replace('%40','@',rawurlencode($followupto)); + } + + for ($i=0; $itrigger_error("mailto: 'encode' parameter must be none, javascript or hex"); + return; + } + + if ($encode == 'javascript' ) { + $string = 'document.write(\''.$text.'\');'; + + for ($x=0; $x < strlen($string); $x++) { + $js_encode .= '%' . bin2hex($string[$x]); + } + + echo ''; + + } elseif ($encode == 'hex') { + + preg_match('!^(.*)(\?.*)$!',$address,$match); + if(!empty($match[2])) { + $smarty->trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript."); + return; + } + for ($x=0; $x < strlen($address); $x++) { + if(preg_match('!\w!',$address[$x])) { + $address_encode .= '%' . bin2hex($address[$x]); + } else { + $address_encode .= $address[$x]; + } + } + for ($x=0; $x < strlen($text); $x++) { + $text_encode .= '&#x' . bin2hex($text[$x]).';'; + } + + echo ''.$text_encode.''; + + } else { + // no encoding + echo ''.$text.''; + + } + +} + +/* vim: set expandtab: */ + +?>