From e543bc1ac4b537f59693c18877e555ffbfb8d70a Mon Sep 17 00:00:00 2001 From: Regis Houssin Date: Fri, 27 Apr 2012 15:07:12 +0200 Subject: [PATCH] New: add Savant pure php template engine --- htdocs/includes/savant/Savant3.php | 1337 +++++++++++++++++ htdocs/includes/savant/Savant3/Error.php | 125 ++ htdocs/includes/savant/Savant3/Exception.php | 29 + htdocs/includes/savant/Savant3/Filter.php | 85 ++ htdocs/includes/savant/Savant3/Plugin.php | 67 + .../Savant3_Filter_trimwhitespace.php | 147 ++ .../resources/Savant3_Plugin_ahref.php | 109 ++ .../Savant3/resources/Savant3_Plugin_date.php | 123 ++ .../resources/Savant3_Plugin_htmlAttribs.php | 63 + .../resources/Savant3_Plugin_image.php | 199 +++ htdocs/main.inc.php | 8 + 11 files changed, 2292 insertions(+) create mode 100644 htdocs/includes/savant/Savant3.php create mode 100644 htdocs/includes/savant/Savant3/Error.php create mode 100644 htdocs/includes/savant/Savant3/Exception.php create mode 100644 htdocs/includes/savant/Savant3/Filter.php create mode 100644 htdocs/includes/savant/Savant3/Plugin.php create mode 100644 htdocs/includes/savant/Savant3/resources/Savant3_Filter_trimwhitespace.php create mode 100644 htdocs/includes/savant/Savant3/resources/Savant3_Plugin_ahref.php create mode 100644 htdocs/includes/savant/Savant3/resources/Savant3_Plugin_date.php create mode 100644 htdocs/includes/savant/Savant3/resources/Savant3_Plugin_htmlAttribs.php create mode 100644 htdocs/includes/savant/Savant3/resources/Savant3_Plugin_image.php diff --git a/htdocs/includes/savant/Savant3.php b/htdocs/includes/savant/Savant3.php new file mode 100644 index 00000000000..cdcd4731480 --- /dev/null +++ b/htdocs/includes/savant/Savant3.php @@ -0,0 +1,1337 @@ + +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Savant3.php,v 1.42 2006/01/01 18:31:00 pmjones Exp $ +* +*/ + + +/** +* Always have these classes available. +*/ +include_once dirname(__FILE__) . '/Savant3/Filter.php'; +include_once dirname(__FILE__) . '/Savant3/Plugin.php'; + + +/** +* +* Provides an object-oriented template system for PHP5. +* +* Savant3 helps you separate business logic from presentation logic +* using PHP as the template language. By default, Savant3 does not +* compile templates. However, you may pass an optional compiler object +* to compile template source to include-able PHP code. It is E_STRICT +* compliant for PHP5. +* +* Please see the documentation at {@link http://phpsavant.com/}, and be +* sure to donate! :-) +* +* @author Paul M. Jones +* +* @package Savant3 +* +* @version @package_version@ +* +*/ + +class Savant3 { + + + /** + * + * Array of configuration parameters. + * + * @access protected + * + * @var array + * + */ + + protected $__config = array( + 'template_path' => array(), + 'resource_path' => array(), + 'error_text' => "\n\ntemplate error, examine fetch() result\n\n", + 'exceptions' => false, + 'autoload' => false, + 'compiler' => null, + 'filters' => array(), + 'plugins' => array(), + 'template' => null, + 'plugin_conf' => array(), + 'extract' => false, + 'fetch' => null, + 'escape' => array('htmlspecialchars'), + ); + + + // ----------------------------------------------------------------- + // + // Constructor and magic methods + // + // ----------------------------------------------------------------- + + + /** + * + * Constructor. + * + * @access public + * + * @param array $config An associative array of configuration keys for + * the Savant3 object. Any, or none, of the keys may be set. + * + * @return object Savant3 A Savant3 instance. + * + */ + + public function __construct($config = null) + { + // force the config to an array + settype($config, 'array'); + + // set the default template search path + if (isset($config['template_path'])) { + // user-defined dirs + $this->setPath('template', $config['template_path']); + } else { + // no directories set, use the + // default directory only + $this->setPath('template', null); + } + + // set the default resource search path + if (isset($config['resource_path'])) { + // user-defined dirs + $this->setPath('resource', $config['resource_path']); + } else { + // no directories set, use the + // default directory only + $this->setPath('resource', null); + } + + // set the error reporting text + if (isset($config['error_text'])) { + $this->setErrorText($config['error_text']); + } + + // set the autoload flag + if (isset($config['autoload'])) { + $this->setAutoload($config['autoload']); + } + + // set the extraction flag + if (isset($config['extract'])) { + $this->setExtract($config['extract']); + } + + // set the exceptions flag + if (isset($config['exceptions'])) { + $this->setExceptions($config['exceptions']); + } + + // set the template to use for output + if (isset($config['template'])) { + $this->setTemplate($config['template']); + } + + // set the output escaping callbacks + if (isset($config['escape'])) { + $this->setEscape($config['escape']); + } + + // set the default plugin configs + if (isset($config['plugin_conf']) && is_array($config['plugin_conf'])) { + foreach ($config['plugin_conf'] as $name => $opts) { + $this->setPluginConf($name, $opts); + } + } + + // set the default filter callbacks + if (isset($config['filters'])) { + $this->addFilters($config['filters']); + } + } + + + /** + * + * Executes a main plugin method with arbitrary parameters. + * + * @access public + * + * @param string $func The plugin method name. + * + * @param array $args The parameters passed to the method. + * + * @return mixed The plugin output, or a Savant3_Error with an + * ERR_PLUGIN code if it can't find the plugin. + * + */ + + public function __call($func, $args) + { + $plugin = $this->plugin($func); + + if ($this->isError($plugin)) { + return $plugin; + } + + // try to avoid the very-slow call_user_func_array() + // for plugins with very few parameters. thanks to + // Andreas Korthaus for profiling the code to find + // the slowdown. + switch (count($args)) { + + case 0: + return $plugin->$func(); + + case 1: + return $plugin->$func($args[0]); + break; + + case 2: + return $plugin->$func($args[0], $args[1]); + break; + + case 3: + return $plugin->$func($args[0], $args[1], $args[2]); + break; + + default: + return call_user_func_array(array($plugin, $func), $args); + break; + } + } + + + /** + * + * Magic method to echo this object as template output. + * + * Note that if there is an error, this will output a simple + * error text string and will not return an error object. Use + * fetch() to get an error object when errors occur. + * + * @access public + * + * @return string The template output. + * + */ + + public function __toString() + { + return $this->getOutput(); + } + + + /** + * + * Reports the API version for this class. + * + * @access public + * + * @return string A PHP-standard version number. + * + */ + + public function apiVersion() + { + return '@package_version@'; + } + + + /** + * + * Returns an internal plugin object; creates it as needed. + * + * @access public + * + * @param string $name The plugin name. If this plugin has not + * been created yet, this method creates it automatically. + * + * @return mixed The plugin object, or a Savant3_Error with an + * ERR_PLUGIN code if it can't find the plugin. + * + */ + + public function plugin($name) + { + // shorthand reference + $plugins =& $this->__config['plugins']; + $autoload = $this->__config['autoload']; + + // is the plugin method object already instantiated? + if (! array_key_exists($name, $plugins)) { + + // not already instantiated, so load it up. + // set up the class name. + $class = "Savant3_Plugin_$name"; + + // has the class been loaded? + if (! class_exists($class, $autoload)) { + + // class is not loaded, set up the file name. + $file = "$class.php"; + + // make sure the class file is available from the resource path. + $result = $this->findFile('resource', $file); + if (! $result) { + // not available, this is an error + return $this->error( + 'ERR_PLUGIN', + array('method' => $name) + ); + } else { + // available, load the class file + include_once $result; + } + } + + // get the default configuration for the plugin. + $plugin_conf =& $this->__config['plugin_conf']; + if (! empty($plugin_conf[$name])) { + $opts = $plugin_conf[$name]; + } else { + $opts = array(); + } + + // add the Savant reference + $opts['Savant'] = $this; + + // instantiate the plugin with its options. + $plugins[$name] = new $class($opts); + } + + // return the plugin object + return $plugins[$name]; + } + + + // ----------------------------------------------------------------- + // + // Public configuration management (getters and setters). + // + // ----------------------------------------------------------------- + + + /** + * + * Returns a copy of the Savant3 configuration parameters. + * + * @access public + * + * @param string $key The specific configuration key to return. If null, + * returns the entire configuration array. + * + * @return mixed A copy of the $this->__config array. + * + */ + + public function getConfig($key = null) + { + if (is_null($key)) { + // no key requested, return the entire config array + return $this->__config; + } elseif (empty($this->__config[$key])) { + // no such key + return null; + } else { + // return the requested key + return $this->__config[$key]; + } + } + + + /** + * + * Sets __autoload() usage on or off. + * + * @access public + * + * @param bool $flag True to use __autoload(), false to not use it. + * + * @return void + * + */ + + public function setAutoload($flag) + { + $this->__config['autoload'] = (bool) $flag; + } + + + /** + * + * Sets a custom compiler/pre-processor callback for template sources. + * + * By default, Savant3 does not use a compiler; use this to set your + * own custom compiler (pre-processor) for template sources. + * + * @access public + * + * @param mixed $compiler A compiler callback value suitable for the + * first parameter of call_user_func(). Set to null/false/empty to + * use PHP itself as the template markup (i.e., no compiling). + * + * @return void + * + */ + + public function setCompiler($compiler) + { + $this->__config['compiler'] = $compiler; + } + + + /** + * + * Sets the custom error text for __toString(). + * + * @access public + * + * @param string $text The error text when a template is echoed. + * + * @return void + * + */ + + public function setErrorText($text) + { + $this->__config['error_text'] = $text; + } + + + /** + * + * Sets whether or not exceptions will be thrown. + * + * @access public + * + * @param bool $flag True to turn on exception throwing, false + * to turn it off. + * + * @return void + * + */ + + public function setExceptions($flag) + { + $this->__config['exceptions'] = (bool) $flag; + } + + + /** + * + * Sets whether or not variables will be extracted. + * + * @access public + * + * @param bool $flag True to turn on variable extraction, false + * to turn it off. + * + * @return void + * + */ + + public function setExtract($flag) + { + $this->__config['extract'] = (bool) $flag; + } + + + /** + * + * Sets config array for a plugin. + * + * @access public + * + * @param string $plugin The plugin to configure. + * + * @param array $config The configuration array for the plugin. + * + * @return void + * + */ + + public function setPluginConf($plugin, $config = null) + { + $this->__config['plugin_conf'][$plugin] = $config; + } + + + /** + * + * Sets the template name to use. + * + * @access public + * + * @param string $template The template name. + * + * @return void + * + */ + + public function setTemplate($template) + { + $this->__config['template'] = $template; + } + + + // ----------------------------------------------------------------- + // + // Output escaping and management. + // + // ----------------------------------------------------------------- + + + /** + * + * Clears then sets the callbacks to use when calling $this->escape(). + * + * Each parameter passed to this function is treated as a separate + * callback. For example: + * + * + * $savant->setEscape( + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @return void + * + */ + + public function setEscape() + { + $this->__config['escape'] = (array) @func_get_args(); + } + + + /** + * + * Adds to the callbacks used when calling $this->escape(). + * + * Each parameter passed to this function is treated as a separate + * callback. For example: + * + * + * $savant->addEscape( + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @return void + * + */ + + public function addEscape() + { + $args = (array) @func_get_args(); + $this->__config['escape'] = array_merge( + $this->__config['escape'], $args + ); + } + + + /** + * + * Gets the array of output-escaping callbacks. + * + * @access public + * + * @return array The array of output-escaping callbacks. + * + */ + + public function getEscape() + { + return $this->__config['escape']; + } + + + /** + * + * Applies escaping to a value. + * + * You can override the predefined escaping callbacks by passing + * added parameters as replacement callbacks. + * + * + * // use predefined callbacks + * $result = $savant->escape($value); + * + * // use replacement callbacks + * $result = $savant->escape( + * $value, + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * + * Unfortunately, a call to "echo htmlspecialchars()" is twice + * as fast as a call to "echo $this->escape()" under the default + * escaping (which is htmlspecialchars). The benchmark showed + * 0.007 seconds for htmlspecialchars(), and 0.014 seconds for + * $this->escape(), on 300 calls each. + * + * @access public + * + * @param mixed $value The value to be escaped. + * + * @return mixed + * + */ + + public function escape($value) + { + // were custom callbacks passed? + if (func_num_args() == 1) { + + // no, only a value was passed. + // loop through the predefined callbacks. + foreach ($this->__config['escape'] as $func) { + // this if() shaves 0.001sec off of 300 calls. + if (is_string($func)) { + $value = $func($value); + } else { + $value = call_user_func($func, $value); + } + } + + } else { + + // yes, use the custom callbacks + $callbacks = func_get_args(); + + // drop $value + array_shift($callbacks); + + // loop through custom callbacks. + foreach ($callbacks as $func) { + // this if() shaves 0.001sec off of 300 calls. + if (is_string($func)) { + $value = $func($value); + } else { + $value = call_user_func($func, $value); + } + } + + } + + return $value; + } + + + /** + * + * Prints a value after escaping it for output. + * + * You can override the predefined escaping callbacks by passing + * added parameters as replacement callbacks. + * + * + * // use predefined callbacks + * $this->eprint($value); + * + * // use replacement callbacks + * $this->eprint( + * $value, + * 'stripslashes', + * 'htmlspecialchars', + * array('StaticClass', 'method'), + * array($object, $method) + * ); + * + * + * @access public + * + * @param mixed $value The value to be escaped and printed. + * + * @return void + * + */ + + public function eprint($value) + { + // avoid the very slow call_user_func_array() when there + // are no custom escaping callbacks. thanks to + // Andreas Korthaus for profiling the code to find + // the slowdown. + $num = func_num_args(); + if ($num == 1) { + echo $this->escape($value); + } else { + $args = func_get_args(); + echo call_user_func_array( + array($this, 'escape'), + $args + ); + } + } + + + // ----------------------------------------------------------------- + // + // File management + // + // ----------------------------------------------------------------- + + + /** + * + * Sets an entire array of search paths for templates or resources. + * + * @access public + * + * @param string $type The type of path to set, typically 'template' + * or 'resource'. + * + * @param string|array $path The new set of search paths. If null or + * false, resets to the current directory only. + * + * @return void + * + */ + + public function setPath($type, $path) + { + // clear out the prior search dirs + $this->__config[$type . '_path'] = array(); + + // always add the fallback directories as last resort + switch (strtolower($type)) { + case 'template': + // the current directory + $this->addPath($type, '.'); + break; + case 'resource': + // the Savant3 distribution resources + $this->addPath($type, dirname(__FILE__) . '/Savant3/resources/'); + break; + } + + // actually add the user-specified directories + $this->addPath($type, $path); + } + + + /** + * + * Adds to the search path for templates and resources. + * + * @access public + * + * @param string|array $path The directory or stream to search. + * + * @return void + * + */ + + public function addPath($type, $path) + { + // convert from path string to array of directories + if (is_string($path) && ! strpos($path, '://')) { + + // the path config is a string, and it's not a stream + // identifier (the "://" piece). add it as a path string. + $path = explode(PATH_SEPARATOR, $path); + + // typically in path strings, the first one is expected + // to be searched first. however, Savant3 uses a stack, + // so the first would be last. reverse the path string + // so that it behaves as expected with path strings. + $path = array_reverse($path); + + } else { + + // just force to array + settype($path, 'array'); + + } + + // loop through the path directories + foreach ($path as $dir) { + + // no surrounding spaces allowed! + $dir = trim($dir); + + // add trailing separators as needed + if (strpos($dir, '://') && substr($dir, -1) != '/') { + // stream + $dir .= '/'; + } elseif (substr($dir, -1) != DIRECTORY_SEPARATOR) { + // directory + $dir .= DIRECTORY_SEPARATOR; + } + + // add to the top of the search dirs + array_unshift( + $this->__config[$type . '_path'], + $dir + ); + } + } + + + /** + * + * Searches the directory paths for a given file. + * + * @param array $type The type of path to search (template or resource). + * + * @param string $file The file name to look for. + * + * @return string|bool The full path and file name for the target file, + * or boolean false if the file is not found in any of the paths. + * + */ + + protected function findFile($type, $file) + { + // get the set of paths + $set = $this->__config[$type . '_path']; + + // start looping through the path set + foreach ($set as $path) { + + // get the path to the file + $fullname = $path . $file; + + // is the path based on a stream? + if (strpos($path, '://') === false) { + // not a stream, so do a realpath() to avoid + // directory traversal attempts on the local file + // system. Suggested by Ian Eure, initially + // rejected, but then adopted when the secure + // compiler was added. + $path = realpath($path); // needed for substr() later + $fullname = realpath($fullname); + } + + // the substr() check added by Ian Eure to make sure + // that the realpath() results in a directory registered + // with Savant so that non-registered directores are not + // accessible via directory traversal attempts. + if (file_exists($fullname) && is_readable($fullname) && + substr($fullname, 0, strlen($path)) == $path) { + return $fullname; + } + } + + // could not find the file in the set of paths + return false; + } + + + // ----------------------------------------------------------------- + // + // Variable and reference assignment + // + // ----------------------------------------------------------------- + + + /** + * + * Sets variables for the template (by copy). + * + * This method is overloaded; you can assign all the properties of + * an object, an associative array, or a single value by name. + * + * You are not allowed to assign any variable named '__config' as + * it would conflict with internal configuration tracking. + * + * In the following examples, the template will have two variables + * assigned to it; the variables will be known inside the template as + * "$this->var1" and "$this->var2". + * + * + * $Savant3 = new Savant3(); + * + * // assign by object + * $obj = new stdClass; + * $obj->var1 = 'something'; + * $obj->var2 = 'else'; + * $Savant3->assign($obj); + * + * // assign by associative array + * $ary = array('var1' => 'something', 'var2' => 'else'); + * $Savant3->assign($ary); + * + * // assign by name and value + * $Savant3->assign('var1', 'something'); + * $Savant3->assign('var2', 'else'); + * + * // assign directly + * $Savant3->var1 = 'something'; + * $Savant3->var2 = 'else'; + * + * + * @access public + * + * @return bool True on success, false on failure. + * + */ + + public function assign() + { + // get the arguments; there may be 1 or 2. + $arg0 = @func_get_arg(0); + $arg1 = @func_get_arg(1); + + // assign from object + if (is_object($arg0)) { + // assign public properties + foreach (get_object_vars($arg0) as $key => $val) { + // can't assign to __config + if ($key != '__config') { + $this->$key = $val; + } + } + return true; + } + + // assign from associative array + if (is_array($arg0)) { + foreach ($arg0 as $key => $val) { + // can't assign to __config + if ($key != '__config') { + $this->$key = $val; + } + } + return true; + } + + // assign by name and value (can't assign to __config). + if (is_string($arg0) && func_num_args() > 1 && $arg0 != '__config') { + $this->$arg0 = $arg1; + return true; + } + + // $arg0 was not object, array, or string. + return false; + } + + + /** + * + * Sets variables for the template (by reference). + * + * You are not allowed to assign any variable named '__config' as + * it would conflict with internal configuration tracking. + * + * + * $Savant3 = new Savant3(); + * + * // assign by name and value + * $Savant3->assignRef('var1', $ref); + * + * // assign directly + * $Savant3->ref =& $var1; + * + * + * @access public + * + * @return bool True on success, false on failure. + * + */ + + public function assignRef($key, &$val) + { + // assign by name and reference (can't assign to __config). + if ($key != '__config') { + $this->$key =& $val; + return true; + } else { + return false; + } + } + + + // ----------------------------------------------------------------- + // + // Template processing + // + // ----------------------------------------------------------------- + + + /** + * + * Displays a template directly (equivalent to echo $tpl). + * + * @access public + * + * @param string $tpl The template source to compile and display. + * + * @return void + * + */ + + public function display($tpl = null) + { + echo $this->getOutput($tpl); + } + + /** + * Returns output, including error_text if an error occurs. + * + * @param $tpl The template to process; if null, uses the + * default template set with setTemplate(). + * + * @return string The template output. + */ + public function getOutput($tpl = null) + { + $output = $this->fetch($tpl); + if ($this->isError($output)) { + $text = $this->__config['error_text']; + return $this->escape($text); + } else { + return $output; + } + } + + + /** + * + * Compiles, executes, and filters a template source. + * + * @access public + * + * @param string $tpl The template to process; if null, uses the + * default template set with setTemplate(). + * + * @return mixed The template output string, or a Savant3_Error. + * + */ + + public function fetch($tpl = null) + { + // make sure we have a template source to work with + if (is_null($tpl)) { + $tpl = $this->__config['template']; + } + + // get a path to the compiled template script + $result = $this->template($tpl); + + // did we get a path? + if (! $result || $this->isError($result)) { + + // no. return the error result. + return $result; + + } else { + + // yes. execute the template script. move the script-path + // out of the local scope, then clean up the local scope to + // avoid variable name conflicts. + $this->__config['fetch'] = $result; + unset($result); + unset($tpl); + + // are we doing extraction? + if ($this->__config['extract']) { + // pull variables into the local scope. + extract(get_object_vars($this), EXTR_REFS); + } + + // buffer output so we can return it instead of displaying. + ob_start(); + + // are we using filters? + if ($this->__config['filters']) { + // use a second buffer to apply filters. we used to set + // the ob_start() filter callback, but that would + // silence errors in the filters. Hendy Irawan provided + // the next three lines as a "verbose" fix. + ob_start(); + include $this->__config['fetch']; + echo $this->applyFilters(ob_get_clean()); + } else { + // no filters being used. + include $this->__config['fetch']; + } + + // reset the fetch script value, get the buffer, and return. + $this->__config['fetch'] = null; + return ob_get_clean(); + } + } + + + /** + * + * Compiles a template and returns path to compiled script. + * + * By default, Savant does not compile templates, it uses PHP as the + * markup language, so the "compiled" template is the same as the source + * template. + * + * Used inside a template script like so: + * + * + * include $this->template($tpl); + * + * + * @access protected + * + * @param string $tpl The template source name to look for. + * + * @return string The full path to the compiled template script. + * + * @throws object An error object with a 'ERR_TEMPLATE' code. + * + */ + + protected function template($tpl = null) + { + // set to default template if none specified. + if (is_null($tpl)) { + $tpl = $this->__config['template']; + } + + // find the template source. + $file = $this->findFile('template', $tpl); + if (! $file) { + return $this->error( + 'ERR_TEMPLATE', + array('template' => $tpl) + ); + } + + // are we compiling source into a script? + if ($this->__config['compiler']) { + // compile the template source and get the path to the + // compiled script (will be returned instead of the + // source path) + $result = call_user_func( + array($this->__config['compiler'], 'compile'), + $file + ); + } else { + // no compiling requested, use the source path + $result = $file; + } + + // is there a script from the compiler? + if (! $result || $this->isError($result)) { + // return an error, along with any error info + // generated by the compiler. + return $this->error( + 'ERR_COMPILER', + array( + 'template' => $tpl, + 'compiler' => $result + ) + ); + } else { + // no errors, the result is a path to a script + return $result; + } + } + + + // ----------------------------------------------------------------- + // + // Filter management and processing + // + // ----------------------------------------------------------------- + + + /** + * + * Resets the filter stack to the provided list of callbacks. + * + * @access protected + * + * @param array An array of filter callbacks. + * + * @return void + * + */ + + public function setFilters() + { + $this->__config['filters'] = (array) @func_get_args(); + } + + + /** + * + * Adds filter callbacks to the stack of filters. + * + * @access protected + * + * @param array An array of filter callbacks. + * + * @return void + * + */ + + public function addFilters() + { + // add the new filters to the static config variable + // via the reference + foreach ((array) @func_get_args() as $callback) { + $this->__config['filters'][] = $callback; + } + } + + + /** + * + * Runs all filter callbacks on buffered output. + * + * @access protected + * + * @param string The template output. + * + * @return void + * + */ + + protected function applyFilters($buffer) + { + $autoload = $this->__config['autoload']; + foreach ($this->__config['filters'] as $callback) { + + // if the callback is a static Savant3_Filter method, + // and not already loaded, try to auto-load it. + if (is_array($callback) && + is_string($callback[0]) && + substr($callback[0], 0, 15) == 'Savant3_Filter_' && + ! class_exists($callback[0], $autoload)) { + + // load the Savant3_Filter_*.php resource + $file = $callback[0] . '.php'; + $result = $this->findFile('resource', $file); + if ($result) { + include_once $result; + } + } + + // can't pass a third $this param, it chokes the OB system. + $buffer = call_user_func($callback, $buffer); + } + + return $buffer; + } + + + // ----------------------------------------------------------------- + // + // Error handling + // + // ----------------------------------------------------------------- + + + /** + * + * Returns an error object or throws an exception. + * + * @access public + * + * @param string $code A Savant3 'ERR_*' string. + * + * @param array $info An array of error-specific information. + * + * @param int $level The error severity level, default is + * E_USER_ERROR (the most severe possible). + * + * @param bool $trace Whether or not to include a backtrace, default + * true. + * + * @return object Savant3_Error + * + */ + + public function error($code, $info = array(), $level = E_USER_ERROR, + $trace = true) + { + $autoload = $this->__config['autoload']; + + // are we throwing exceptions? + if ($this->__config['exceptions']) { + if (! class_exists('Savant3_Exception', $autoload)) { + include_once dirname(__FILE__) . '/Savant3/Exception.php'; + } + throw new Savant3_Exception($code); + } + + + // the error config array + $config = array( + 'code' => $code, + 'info' => (array) $info, + 'level' => $level, + 'trace' => $trace + ); + + // make sure the Savant3 error class is available + if (! class_exists('Savant3_Error', $autoload)) { + include_once dirname(__FILE__) . '/Savant3/Error.php'; + } + + // return it + $err = new Savant3_Error($config); + return $err; + } + + + /** + * + * Tests if an object is of the Savant3_Error class. + * + * @access public + * + * @param object $obj The object to be tested. + * + * @return boolean True if $obj is an error object of the type + * Savant3_Error, or is a subclass that Savant3_Error. False if not. + * + */ + + public function isError($obj) + { + $autoload = $this->__config['autoload']; + + // is it even an object? + if (! is_object($obj)) { + // not an object, so can't be a Savant3_Error + return false; + } else { + // make sure the Savant3 error class is available for + // comparison + if (! class_exists('Savant3_Error', $autoload)) { + include_once dirname(__FILE__) . '/Savant3/Error.php'; + } + // now compare the parentage + $is = $obj instanceof Savant3_Error; + $sub = is_subclass_of($obj, 'Savant3_Error'); + return ($is || $sub); + } + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/savant/Savant3/Error.php b/htdocs/includes/savant/Savant3/Error.php new file mode 100644 index 00000000000..211fc2af233 --- /dev/null +++ b/htdocs/includes/savant/Savant3/Error.php @@ -0,0 +1,125 @@ + +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Error.php,v 1.5 2005/05/27 14:03:50 pmjones Exp $ +* +*/ + +/** +* +* Provides a simple error class for Savant. +* +* @package Savant3 +* +* @author Paul M. Jones +* +*/ + +class Savant3_Error { + + + /** + * + * The error code, typically a Savant 'ERR_*' string. + * + * @access public + * + * @var string + * + */ + + public $code = null; + + + /** + * + * An array of error-specific information. + * + * @access public + * + * @var array + * + */ + + public $info = array(); + + + /** + * + * The error severity level. + * + * @access public + * + * @var int + * + */ + + public $level = E_USER_ERROR; + + + /** + * + * A debug backtrace for the error, if any. + * + * @access public + * + * @var array + * + */ + + public $trace = null; + + + /** + * + * Constructor. + * + * @access public + * + * @param array $conf An associative array where the key is a + * Savant3_Error property and the value is the value for that + * property. + * + */ + + public function __construct($conf = array()) + { + // set public properties + foreach ($conf as $key => $val) { + $this->$key = $val; + } + + // add a backtrace + if ($conf['trace'] === true) { + $this->trace = debug_backtrace(); + } + } + + + /** + * + * Magic method for output dump. + * + * @access public + * + * @return void + */ + + public function __toString() + { + ob_start(); + echo get_class($this) . ': '; + print_r(get_object_vars($this)); + return ob_get_clean(); + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/savant/Savant3/Exception.php b/htdocs/includes/savant/Savant3/Exception.php new file mode 100644 index 00000000000..f564115be34 --- /dev/null +++ b/htdocs/includes/savant/Savant3/Exception.php @@ -0,0 +1,29 @@ + +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Exception.php,v 1.1 2005/05/27 14:04:36 pmjones Exp $ +* +*/ + + +/** +* +* A simple Savant3_Exception class. +* +* @package Savant3 +* +* @author Paul M. Jones +* +*/ + +class Savant3_Exception extends Exception { +} +?> \ No newline at end of file diff --git a/htdocs/includes/savant/Savant3/Filter.php b/htdocs/includes/savant/Savant3/Filter.php new file mode 100644 index 00000000000..836f8fbeabb --- /dev/null +++ b/htdocs/includes/savant/Savant3/Filter.php @@ -0,0 +1,85 @@ + +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Filter.php,v 1.5 2005/04/29 16:23:50 pmjones Exp $ +* +*/ + +/** +* +* Abstract Savant3_Filter class. +* +* You have to extend this class for it to be useful; e.g., "class +* Savant3_Filter_example extends Savant3_Filter". +* +* @package Savant3 +* +* @author Paul M. Jones +* +*/ + +abstract class Savant3_Filter { + + + /** + * + * Optional reference to the calling Savant object. + * + * @access protected + * + * @var object + * + */ + + protected $Savant = null; + + + /** + * + * Constructor. + * + * @access public + * + * @param array $conf An array of configuration keys and values for + * this filter. + * + * @return void + * + */ + + public function __construct($conf = null) + { + settype($conf, 'array'); + foreach ($conf as $key => $val) { + $this->$key = $val; + } + } + + + /** + * + * Stub method for extended behaviors. + * + * @access public + * + * @param string $text The text buffer to filter. + * + * @return string The text buffer after it has been filtered. + * + */ + + public static function filter($text) + { + return $text; + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/savant/Savant3/Plugin.php b/htdocs/includes/savant/Savant3/Plugin.php new file mode 100644 index 00000000000..ca7a0b471e7 --- /dev/null +++ b/htdocs/includes/savant/Savant3/Plugin.php @@ -0,0 +1,67 @@ + +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Plugin.php,v 1.5 2005/04/29 16:23:50 pmjones Exp $ +* +*/ + +/** +* +* Abstract Savant3_Plugin class. +* +* You have to extend this class for it to be useful; e.g., "class +* Savant3_Plugin_example extends Savant2_Plugin". Be sure to add a +* method named for the plugin itself; e.g., "function example()". +* +* @package Savant3 +* +* @author Paul M. Jones +* +*/ + +abstract class Savant3_Plugin { + + /** + * + * Reference to the calling Savant object. + * + * @access protected + * + * @var object + * + */ + + protected $Savant = null; + + + /** + * + * Constructor. + * + * @access public + * + * @param array $conf An array of configuration keys and values for + * this plugin. + * + * @return void + * + */ + + public function __construct($conf = null) + { + settype($conf, 'array'); + foreach ($conf as $key => $val) { + $this->$key = $val; + } + } +} +?> \ No newline at end of file diff --git a/htdocs/includes/savant/Savant3/resources/Savant3_Filter_trimwhitespace.php b/htdocs/includes/savant/Savant3/resources/Savant3_Filter_trimwhitespace.php new file mode 100644 index 00000000000..198362802d2 --- /dev/null +++ b/htdocs/includes/savant/Savant3/resources/Savant3_Filter_trimwhitespace.php @@ -0,0 +1,147 @@ + +* +* @author Contributions from Lars Noschinski +* +* @author Converted to a Savant3 filter by Paul M. Jones +* +* @license http://www.gnu.org/copyleft/lesser.html LGPL +* +* @version $Id: Savant3_Filter_trimwhitespace.php,v 1.4 2005/05/29 15:27:07 pmjones Exp $ +* +*/ + +/** +* +* Filter to remove extra white space within the text. +* +* @package Savant3 +* +* @author Monte Ohrt +* +* @author Contributions from Lars Noschinski +* +* @author Converted to a Savant3 filter by Paul M. Jones +* +*/ + +class Savant3_Filter_trimwhitespace extends Savant3_Filter { + + + /** + * + * Removes extra white space within the text. + * + * Trim leading white space and blank lines from template source + * after it gets interpreted, cleaning up code and saving bandwidth. + * Does not affect
, , or
+	*  blocks.
+	* 
+	* @access public
+	* 
+	* @param string $buffer The source text to be filtered.
+	* 
+	* @return string The filtered text.
+	* 
+	*/
+	
+	public static function filter($buffer)
+	{
+		// Pull out the script blocks
+		preg_match_all("!]+>.*?!is", $buffer, $match);
+		$script_blocks = $match[0];
+		$buffer = preg_replace(
+			"!]+>.*?!is",
+			'@@@SAVANT:TRIM:SCRIPT@@@',
+			$buffer
+		);
+	
+		// Pull out the pre blocks
+		preg_match_all("!]*>.*?!is", $buffer, $match);
+		$pre_blocks = $match[0];
+		$buffer = preg_replace(
+			"!]*>.*?!is",
+			'@@@SAVANT:TRIM:PRE@@@',
+			$buffer
+		);
+	
+		// Pull out the textarea blocks
+		preg_match_all("!]+>.*?!is", $buffer, $match);
+		$textarea_blocks = $match[0];
+		$buffer = preg_replace(
+			"!]+>.*?!is",
+			'@@@SAVANT:TRIM:TEXTAREA@@@',
+			$buffer
+		);
+	
+		// remove all leading spaces, tabs and carriage returns NOT
+		// preceeded by a php close tag.
+		$buffer = trim(preg_replace('/((?)\n)[\s]+/m', '\1', $buffer));
+	
+		// replace script blocks
+		Savant3_Filter_trimwhitespace::replace(
+			"@@@SAVANT:TRIM:SCRIPT@@@",
+			$script_blocks,
+			$buffer
+		);
+	
+		// replace pre blocks
+		Savant3_Filter_trimwhitespace::replace(
+			"@@@SAVANT:TRIM:PRE@@@",
+			$pre_blocks,
+			$buffer
+		);
+	
+		// replace textarea blocks
+		Savant3_Filter_trimwhitespace::replace(
+			"@@@SAVANT:TRIM:TEXTAREA@@@",
+			$textarea_blocks,
+			$buffer
+		);
+	
+		return $buffer;
+	}
+	
+	
+	/**
+	* 
+	* Does a simple search-and-replace on the source text.
+	* 
+	* @access protected
+	* 
+	* @param string $search The string to search for.
+	* 
+	* @param string $replace Replace with this text.
+	* 
+	* @param string &$buffer The source text.
+	* 
+	* @return string The text after search-and-replace.
+	* 
+	*/
+	
+	protected static function replace($search, $replace, &$buffer)
+	{
+		$len = strlen($search);
+		$pos = 0;
+		$count = count($replace);
+		
+		for ($i = 0; $i < $count; $i++) {
+			// does the search-string exist in the buffer?
+			$pos = strpos($buffer, $search, $pos);
+			if ($pos !== false) {
+				// replace the search-string
+				$buffer = substr_replace($buffer, $replace[$i], $pos, $len);
+			} else {
+				break;
+			}
+		}
+	}
+}
+?>
\ No newline at end of file
diff --git a/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_ahref.php b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_ahref.php
new file mode 100644
index 00000000000..6d6bb319bb3
--- /dev/null
+++ b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_ahref.php
@@ -0,0 +1,109 @@
+... tag.
+* 
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+* @license http://www.gnu.org/copyleft/lesser.html LGPL
+* 
+* @version $Id: Savant3_Plugin_ahref.php,v 1.4 2005/08/09 12:56:14 pmjones Exp $
+*
+*/
+
+/**
+* 
+* Generates an ... tag.
+*
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+*/
+
+class Savant3_Plugin_ahref extends Savant3_Plugin {
+
+	/**
+	* 
+	* Generate an HTML ... tag.
+	* 
+	* @access public
+	* 
+	* @param string|array $href A string URL for the resulting tag.  May
+	* also be an array with any combination of the keys 'scheme',
+	* 'host', 'path', 'query', and 'fragment' (c.f. PHP's native
+	* parse_url() function).
+	* 
+	* @param string $text The displayed text of the link.
+	* 
+	* @param string|array $attr Any extra attributes for the  tag.
+	* 
+	* @return string The ... tag.
+	* 
+	*/
+	
+	public function ahref($href, $text, $attr = null)
+	{
+		$html = ' $val) {
+				$key = htmlspecialchars($key);
+				$val = htmlspecialchars($val);
+				$html .= " $key=\"$val\"";
+			}
+		} elseif (! is_null($attr)) {
+			// from scalar
+			$html .= htmlspecialchars(" $attr");
+		}
+		
+		// set the link text, close the tag, and return
+		$html .= '>' . $text . '';
+		return $html;
+	}
+}
+?>
\ No newline at end of file
diff --git a/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_date.php b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_date.php
new file mode 100644
index 00000000000..c35db4ca7b6
--- /dev/null
+++ b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_date.php
@@ -0,0 +1,123 @@
+
+* 
+* @license http://www.gnu.org/copyleft/lesser.html LGPL
+* 
+* @version $Id: Savant3_Plugin_date.php,v 1.3 2005/03/07 14:40:16 pmjones Exp $
+*
+*/
+
+/**
+* 
+* Plugin to generate a formatted date using strftime() conventions.
+* 
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+*/
+
+class Savant3_Plugin_date extends Savant3_Plugin {
+	
+	/**
+	* 
+	* The default strftime() format string.
+	* 
+	* @access public
+	* 
+	* @var array
+	* 
+	*/
+	
+	public $default = '%c';
+	
+	
+	/**
+	* 
+	* Custom strftime() format strings to use for dates.
+	* 
+	* You can preset the format strings via Savant3::setPluginConf().
+	* 
+	* 
+	* $conf = array(
+	*     'custom' => array(
+	*         'mydate' => '%Y-%m-%d',
+	*         'mytime' => '%R'
+	*     )
+	* );
+	* 
+	* $Savant->setPluginConf('date', $conf);
+	* 
+	* 
+	* ... and in your template, to use a preset custom string by name:
+	* 
+	* 
+	* echo $this->date($value, 'mydate');
+	* 
+	* 
+	* @access public
+	* 
+	* @var array
+	* 
+	*/
+	
+	public $custom = array(
+		'date'    => '%Y-%m-%d',
+		'time'    => '%H:%M:%S'
+	);
+	
+	
+	/**
+	* 
+	* Outputs a formatted date using strftime() conventions.
+	* 
+	* @access public
+	* 
+	* @param string $datestring Any date-time string suitable for
+	* strtotime().
+	* 
+	* @param string $format The strftime() formatting string, or a named
+	* custom string key from $this->custom.
+	* 
+	* @return string The formatted date string.
+	* 
+	*/
+	
+	function date($datestring, $format = null)
+	{
+		settype($format, 'string');
+		
+		if (is_null($format)) {
+			$format = $this->default;
+		}
+		
+		// does the format string have a % sign in it?
+		if (strpos($format, '%') === false) {
+			// no, look for a custom format string
+			if (! empty($this->custom[$format])) {
+				// found a custom format string
+				$format = $this->custom[$format];
+			} else {
+				// did not find the custom format, revert to default
+				$format = $this->default;
+			}
+		}
+		
+		// convert the date string to the specified format
+		if (trim($datestring != '')) {
+			return strftime($format, strtotime($datestring));
+		} else {
+			// no datestring, return VOID
+			return;
+		}
+	}
+
+}
+?>
\ No newline at end of file
diff --git a/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_htmlAttribs.php b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_htmlAttribs.php
new file mode 100644
index 00000000000..09257f77cf7
--- /dev/null
+++ b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_htmlAttribs.php
@@ -0,0 +1,63 @@
+
+* 
+* @license http://www.gnu.org/copyleft/lesser.html LGPL
+* 
+* @version $Id: Savant3_Plugin_htmlAttribs.php,v 1.3 2005/09/12 17:49:27 pmjones Exp $
+* 
+*/
+
+/**
+* 
+* Plugin to convert an associative array to a string of tag attributes.
+* 
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+*/
+
+class Savant3_Plugin_htmlAttribs extends Savant3_Plugin {
+
+	/**
+	* 
+	* Converts an associative array to a string of tag attributes.
+	* 
+	* @access public
+	* 
+	* @param array $attribs From this array, each key-value pair is 
+	* converted to an attribute name and value.
+	* 
+	* @return string The XHTML for the attributes.
+	* 
+	*/
+	
+	public function htmlAttribs($attribs)
+	{
+		$xhtml = '';
+		foreach ((array) $attribs as $key => $val) {
+		
+			if ($val === null) {
+				continue;
+			}
+			
+			if (is_array($val)) {
+				$val = implode(' ', $val);
+			}
+			
+			$key = htmlspecialchars($key);
+			$val = htmlspecialchars($val);
+			
+			$xhtml .= " $key=\"$val\"";
+		}
+		return $xhtml;
+	}
+}
+?>
\ No newline at end of file
diff --git a/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_image.php b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_image.php
new file mode 100644
index 00000000000..4b316e7537c
--- /dev/null
+++ b/htdocs/includes/savant/Savant3/resources/Savant3_Plugin_image.php
@@ -0,0 +1,199 @@
+ tag.
+* 
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+* @license http://www.gnu.org/copyleft/lesser.html LGPL
+* 
+* @version $Id: Savant3_Plugin_image.php,v 1.7 2005/08/12 14:34:09 pmjones Exp $
+*
+*/
+
+/**
+* 
+* Plugin to generate an  tag.
+*
+* Support for alpha transparency of PNG files in Microsoft IE added by
+* Edward Ritter; thanks, Edward.
+* 
+* @package Savant3
+* 
+* @author Paul M. Jones 
+* 
+*/
+
+class Savant3_Plugin_image extends Savant3_Plugin {
+	
+	
+	/**
+	* 
+	* The document root.
+	* 
+	* @access public
+	* 
+	* @var string
+	* 
+	*/
+	
+	protected $documentRoot = null;
+	
+	
+	/**
+	* 
+	* The base directory for images within the document root.
+	* 
+	* @access public
+	* 
+	* @var string
+	* 
+	*/
+	
+	protected $imageDir = null;
+	
+	
+	/**
+	* 
+	* Outputs an  tag.
+	* 
+	* Microsoft IE alpha PNG support added by Edward Ritter.
+	* 
+	* @access public
+	* 
+	* @param string $file The path to the image on the local file system
+	* relative to $this->imageDir.
+	* 
+	* @param string $alt Alternative descriptive text for the image;
+	* defaults to the filename of the image.
+	* 
+	* @param int $border The border width for the image; defaults to zero.
+	* 
+	* @param int $width The displayed image width in pixels; defaults to
+	* the width of the image.
+	* 
+	* @param int $height The displayed image height in pixels; defaults to
+	* the height of the image.
+	* 
+	* @return string An  tag.
+	* 
+	*/
+	
+	public function image($file, $alt = null, $height = null, $width = null,
+		$attr = null)
+	{
+		// is the document root set?
+		if (is_null($this->documentRoot) && isset($_SERVER['DOCUMENT_ROOT'])) {
+			// no, so set it
+			$this->documentRoot = $_SERVER['DOCUMENT_ROOT'];
+		}
+		
+		// make sure there's a DIRECTORY_SEPARATOR between the docroot
+		// and the image dir
+		if (substr($this->documentRoot, -1) != DIRECTORY_SEPARATOR &&
+			substr($this->imageDir, 0, 1) != DIRECTORY_SEPARATOR) {
+			$this->documentRoot .= DIRECTORY_SEPARATOR;
+		}
+		
+		// make sure there's a separator between the imageDir and the
+		// file name
+		if (substr($this->imageDir, -1) != DIRECTORY_SEPARATOR &&
+			substr($file, 0, 1) != DIRECTORY_SEPARATOR) {
+			$this->imageDir .= DIRECTORY_SEPARATOR;
+		}
+		
+		// the image file type code (PNG = 3)
+		$type = null;
+		
+		// get the file information
+		$info = false;
+		
+		if (strpos($file, '://') === false) {
+			// no "://" in the file, so it's local
+			$file = $this->imageDir . $file;
+			$tmp = $this->documentRoot . $file;
+			$info = @getimagesize($tmp);
+		} else {
+			// don't attempt to get file info from streams, it takes
+			// way too long.
+			$info = false;
+		}
+		
+		// did we find the file info?
+		if (is_array($info)) {
+		
+			// capture type info regardless
+			$type = $info[2];
+			
+			// capture size info where both not specified
+			if (is_null($width) && is_null($height)) {
+				$width = $info[0];
+				$height = $info[1];
+			}
+		}
+		
+		// clean up
+		unset($info);
+		
+		// is the file a PNG? if so, check user agent, we will need to
+		// make special allowances for Microsoft IE.
+		if (stristr($_SERVER['HTTP_USER_AGENT'], 'MSIE') && $type === 3) {
+			
+			// support alpha transparency for PNG files in MSIE
+			$html = 'Savant->htmlAttribs($attr);
+
+			// done
+			$html .= '>';
+			
+		} else {
+			
+			// not IE, so build a normal image tag.
+			$html = 'Savant->htmlAttribs($attr);
+			
+			// done
+			$html .= ' />';
+			
+		}
+		
+		// done!
+		return $html;
+	}
+}
+
+?>
\ No newline at end of file
diff --git a/htdocs/main.inc.php b/htdocs/main.inc.php
index d10a9e91706..73759787103 100644
--- a/htdocs/main.inc.php
+++ b/htdocs/main.inc.php
@@ -665,6 +665,14 @@ if (! defined('NOREQUIRETRAN'))
     }
 }
 
+// Use php template engine
+if ($conf->global->MAIN_USE_TEMPLATE_ENGINE && ! defined('NOTEMPLATEENGINE'))
+{
+	require_once(DOL_DOCUMENT_ROOT.'/includes/savant/Savant3.php');
+	
+	$tpl = new Savant3();
+}
+
 // Case forcing style from url
 if (GETPOST('theme'))
 {