Source for file Template.class.php
Documentation is available at Template.class.php
require
(SM_PATH .
'functions/template.php');
* This file contains an abstract (PHP 4, so "abstract" is relative)
* class meant to define the basic template interface for the
* SquirrelMail core application. Subclasses should extend this
* class with any custom functionality needed to interface a target
* templating engine with SquirrelMail.
* @copyright © 2003-2006 The SquirrelMail Project Team
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* @version $Id: Template.class.php,v 1.6 2006/10/02 12:37:46 pdontthink Exp $
* The SquirrelMail Template class.
* Basic template class for capturing values and pluging them into a template.
* This class uses a similar API to Smarty.
* Methods that must be implemented by subclasses are as follows (see method
* stubs below for further information about expected behavior):
* @author Paul Lesniewski
* The template set base directory (relative path from
* the main SquirrelMail directory (SM_PATH))
* The template engine (please use constants defined in constants.php)
* The fall-back template ID
* The fall-back template directory (relative
* path from the main SquirrelMail directory (SM_PATH))
* The fall-back template engine (please use
* constants defined in constants.php)
* Template file cache. Structured as an array, whose keys
* are all the template file names (with path information relative
* to the template set's base directory, e.g., "css/style.css")
* found in all parent template sets including the ultimate fall-back
* template set. Array values are sub-arrays with the
* following key-value pairs:
* PATH -- file path, relative to SM_PATH
* SET_ID -- the ID of the template set that this file belongs to
* ENGINE -- the engine needed to render this template file
* Extra template engine class objects for rendering templates
* that require a different engine than the one for the current
* template set. Keys should be the name of the template engine,
* values are the corresponding class objects.
* Please do not call directly. Use Template::construct_template().
* @param string $template_set_id the template ID
//FIXME: find a way to test that this is ONLY ever called
// from the construct_template() method (I doubt it
// is worth the trouble to parse the current stack trace)
// trigger_error('Please do not use default Template() constructor. Instead, use Template::construct_template().', E_USER_ERROR);
* This method should always be called instead of trying
* to get a Template object from the normal/default constructor,
* and is necessary in order to control the return value.
* @param string $template_set_id the template ID
* @return object The correct Template object for the given template set
$template =
new Template($template_set_id);
return $template->get_template_engine_subclass();
* Set up internal attributes
* This method does most of the work for setting up
* newly constructed objects.
* @param string $template_set_id the template ID
// FIXME: do we want to place any restrictions on the ID like
// making sure no slashes included?
// set up template directories
// determine template engine
// FIXME: assuming PHP template engine may not necessarily be a good thing
// get template file cache
* Determine what the ultimate fallback template set is.
* NOTE that if the fallback setting cannot be found in the
* main SquirrelMail configuration settings that the value
* of $default is returned.
* @param string $default The template set ID to use if
* the fallback setting cannot be
* found in SM config (optional;
* defaults to "default").
* @return string The ID of the fallback template set.
// FIXME: do we want to place any restrictions on the ID such as
// making sure no slashes included?
// values are in main SM config file
global $templateset_fallback, $aTemplateSet;
$aTemplateSet =
(!isset
($aTemplateSet) ||
!is_array($aTemplateSet)
?
array() :
$aTemplateSet);
$templateset_fallback =
(!isset
($templateset_fallback)
?
0 :
$templateset_fallback);
return (!empty($aTemplateSet[$templateset_fallback]['ID'])
?
$aTemplateSet[$templateset_fallback]['ID'] :
$default);
* Determine what the default template set is.
* NOTE that if the default setting cannot be found in the
* main SquirrelMail configuration settings that the value
* of $default is returned.
* @param string $default The template set ID to use if
* the default setting cannot be
* found in SM config (optional;
* defaults to "default").
* @return string The ID of the default template set.
// FIXME: do we want to place any restrictions on the ID such as
// making sure no slashes included?
// values are in main SM config file
$aTemplateSet =
(!isset
($aTemplateSet) ||
!is_array($aTemplateSet)
?
array() :
$aTemplateSet);
$templateset_default =
(!isset
($templateset_default)
?
0 :
$templateset_default);
return (!empty($aTemplateSet[$templateset_default]['ID'])
?
$aTemplateSet[$templateset_default]['ID'] :
$default);
* Instantiate and return correct subclass for this template
* set's templating engine.
* @param string $template_set_id The template set whose engine
* is to be used as an override
* (if not given, this template
* set's engine is used) (optional).
* @return object The Template subclass object for the template engine.
// FIXME: assuming PHP template engine may not necessarily be a good thing
$engine_class_file =
SM_PATH .
'class/template/'
.
$engine .
'Template.class.php';
.
') was specified in template configuration file',
$engine_class =
$engine .
'Template';
require_once($engine_class_file);
return new $engine_class($template_set_id);
* Determine the relative template directory path for
* @param string $template_set_id The template ID from which to build
* @return string The relative template path (based off of SM_PATH)
return 'templates/' .
$template_set_id .
'/';
* Determine the relative images directory path for
* @param string $template_set_id The template ID from which to build
* @return string The relative images path (based off of SM_PATH)
return 'templates/' .
$template_set_id .
'/images/';
* Return the relative template directory path for this template set.
* @return string The relative path to the template directory based
* from the main SquirrelMail directory (SM_PATH).
* Return the template ID for the fallback template set.
* @return string The ID of the fallback template set.
* Return the relative template directory path for the
* @return string The relative path to the fallback template
* directory based from the main SquirrelMail
* Get template set config setting
* Given a template set ID and setting name, returns the
* setting's value. Note that settings are cached in
* session, so "live" changes to template configuration
* won't be reflected until the user logs out and back
* @param string $template_set_id The template set for which
* to look up the setting.
* @param string $setting The name of the setting to
* @param mixed $default When the requested setting
* is not found, the contents
* of this value are returned
* instead (optional; default
* NOTE that unlike sqGetGlobalVar(),
* this function will also return
* the default value if the
* requested setting is found
* @param boolean $live_config When TRUE, the target template
* set's configuration file is
* reloaded every time this
* method is called. Default
* behavior is to only load the
* configuration file if it had
* never been loaded before, but
* not again after that (optional;
* default FALSE). Use with care!
* Should mostly be used for
* @return mixed The desired setting's value or if not found,
* the contents of $default are returned.
$default=
NULL, $live_config=
FALSE) {
sqGetGlobalVar('template_configuration_settings',
$template_configuration_settings,
if ($live_config) unset
($template_configuration_settings[$template_set_id]);
// NOTE: could use isset() instead of empty() below, but
// this function is designed to replace empty values
// as well as non-existing values with $default
if (!empty($template_configuration_settings[$template_set_id][$setting]))
return $template_configuration_settings[$template_set_id][$setting];
// if template set configuration has been loaded, but this
// setting is not known, return $default
if (!empty($template_configuration_settings[$template_set_id]))
// otherwise (template set configuration has not been loaded before),
// load it into session and return the desired setting after that
trigger_error('No template configuration file was found where expected: ("'
.
$template_config_file .
'")', E_USER_ERROR);
// we require() the file to let PHP do the variable value
// parsing for us, and read the file in manually so we can
// know what variable names are used in the config file
// (settings can be different depending on specific requirements
// of different template engines)... the other way this may
// be accomplished is to somehow diff the symbol table
// before/after the require(), but anyway, this code should
// only run once for this template set...
require
($template_config_file);
$file_contents =
implode("\n", file($template_config_file));
// note that this assumes no template settings have
// a string in them that looks like a variable name like $x
// also note that this will attempt to grab things like
// $Id found in CVS headers, so we try to adjust for that
// by checking that the variable is actually set
preg_match_all('/\$(\w+)/', $file_contents, $variables, PREG_PATTERN_ORDER);
foreach ($variables[1] as $variable) {
$template_configuration_settings[$template_set_id][$variable]
'template_configuration_settings');
// NOTE: could use isset() instead of empty() below, but
// this function is designed to replace empty values
// as well as non-existing values with $default
if (!empty($template_configuration_settings[$template_set_id][$setting]))
return $template_configuration_settings[$template_set_id][$setting];
* Obtain template file hierarchy from cache.
* If the file hierarchy does not exist in session, it is
* constructed and stored in session before being returned
* @param boolean $regenerate_cache When TRUE, the file hierarchy
* is reloaded and stored fresh
* (optional; default FALSE).
* @param array $additional_files Must be in same form as the
* files in the file hierarchy
* cache. These are then added
* to the cache (optional; default
* empty - no additional files).
* @return array Template file hierarchy array, whose keys
* are all the template file names (with path
* information relative to the template set's
* base directory, e.g., "css/style.css")
* found in all parent template sets including
* the ultimate fall-back template set.
* Array values are sub-arrays with the
* following key-value pairs:
* PATH -- file path, relative to SM_PATH
* SET_ID -- the ID of the template set that this file belongs to
* ENGINE -- the engine needed to render this template file
$additional_files=
array()) {
sqGetGlobalVar('template_file_hierarchy', $template_file_hierarchy,
if ($regenerate_cache) unset
($template_file_hierarchy);
if (!empty($template_file_hierarchy)) {
// have to add additional files if given before returning
if (!empty($additional_files)) {
$template_file_hierarchy =
array_merge($template_file_hierarchy,
'template_file_hierarchy');
return $template_file_hierarchy;
// nothing in cache apparently, so go build it now
// FIXME: not sure if there is any possibility that
// this could be called when $sTemplateID has
// yet to be defined... throw error for now,
// but if the error occurs, it's a coding error
// rather than a configuration error
if (empty($sTemplateID)) {
// additional files, if any
if (!empty($additional_files)) {
$template_file_hierarchy =
array_merge($template_file_hierarchy,
'template_file_hierarchy');
return $template_file_hierarchy;
* Traverse template hierarchy and catalogue all template
* files (for storing in cache).
* Paths to all files in all parent, grand-parent, great grand
* parent, etc. template sets (including the fallback template)
* are catalogued; for identically named files, the file earlier
* in the hierarchy (closest to this template set) is used.
* @param string $template_set_id The template set in which to
* @param array $file_list The file list so far to be added
* to (allows recursive behavior)
* (optional; default empty array).
* @param string $directory The directory in which to search for
* files (must be given as full path).
* If empty, starts at top-level template
* set directory (optional; default empty).
* NOTE! Use with care, as behavior is
* unpredictable if directory given is not
* part of correct template set.
* @return mixed The top-level caller will have an array of template
* files returned to it; recursive calls to this function
* do not receive any return value at all. The format
* of the template file array is as described for the
* Template class attribute $template_file_cache
$directory =
$template_base_dir;
$files_and_dirs =
list_files($directory, '', FALSE, TRUE, FALSE, TRUE);
// recurse for all the subdirectories in the template set
foreach ($files_and_dirs['DIRECTORIES'] as $dir) {
// place all found files in the cache
// FIXME: assuming PHP template engine may not necessarily be a good thing
foreach ($files_and_dirs['FILES'] as $file) {
// remove the part of the file path corresponding to the
// template set's base directory
// only put file in cache if not already found in earlier template
if (!isset
($file_list[$relative_file])) {
$file_list[$relative_file] =
array(
'SET_ID' =>
$template_set_id,
// now if we are currently at the top-level of the template
// set base directory, we need to move on to the parent
if ($directory ==
$template_base_dir) {
// use fallback when we run out of parents
// were we already all the way to the last level? just exit
// note that this code allows the fallback set to have
// a parent, too, but can result in endless loops
// if ($parent_id == $template_set_id) {
if ($fallback_id ==
$template_set_id) {
* Look for a template file in a plugin; add to template
* The file is searched for in the following order:
* - A directory for the current template set within the plugin:
* SM_PATH/plugins/<plugin name>/templates/<template name>/
* - In a directory for one of the current template set's ancestor
* (inherited) template sets within the plugin:
* SM_PATH/plugins/<plugin name>/templates/<parent template name>/
* - In a directory for the fallback template set within the plugin:
* SM_PATH/plugins/<plugin name>/templates/<fallback template name>/
* @param string $plugin The name of the plugin
* @param string $file The name of the template file
* @param string $template_set_id The ID of the template for which
* to start looking for the file
* (optional; default is current
* @return boolean TRUE if the template file was found, FALSE otherwise.
if (empty($template_set_id))
$file_path =
SM_PATH .
'plugins/' .
$plugin .
'/'
// FIXME: assuming PHP template engine may not necessarily be a good thing
$file_list =
array('plugins/' .
$plugin .
'/' .
$file =>
array(
'SET_ID' =>
$template_set_id,
// not found yet, try parent template set
// (use fallback when we run out of parents)
// were we already all the way to the last level? just exit
// note that this code allows the fallback set to have
// a parent, too, but can result in endless loops
// if ($parent_id == $template_set_id) {
if ($fallback_id ==
$template_set_id) {
* Find the right template file.
* The template file is taken from the template file cache, thus
* the file is taken from the current template, one of its
* ancestors or the fallback template.
* Note that it is perfectly acceptable to load template files from
* template subdirectories. For example, JavaScript templates found
* in the js/ subdirectory would be loaded by passing
* "js/<javascript file name>" as the $filename.
* Note that the caller can also ask for ALL files in a directory
* (and those in the same directory for all ancestor template sets)
* by giving a $filename that is a directory name (ending with a
* If not found and the file is a plugin template file (indicated
* by the presence of "plugins/" on the beginning of $filename),
* the target plugin is searched for a substitue template file
* before just returning nothing.
* Plugin authors must note that the $filename MUST be prefaced
* with "plugins/<plugin name>/" in order to correctly resolve the
* @param string $filename The name of the template file,
* "plugins/<plugin name>/"
* indicating that it is a plugin
* template, or ending with a
* slash, indicating that all files
* for that directory name should
* @return mixed The full path to the template file or a list
* of all files in the given directory if $filename
* ends with a slash; if not found, an empty string
* is returned. The caller is responsible for
* throwing errors or other actions if template
// return list of all files in a directory (and that
if ($filename{strlen($filename) -
1} ==
'/') {
// only want files in the requested directory
// (AND not in a subdirectory!)
if (strpos($file, $filename) ===
0
$return_array[] =
$file_info['PATH'];
// figure out what to do with files not found
// plugins get one more chance below; any other
// files we just give up now
if (strpos($filename, 'plugins/') !==
0)
$plugin_name =
substr($filename, 8, strpos($filename, '/', 8) -
8);
* Get template engine needed to render given template file.
* If at all possible, just returns a reference to $this, but
* some template files may require a different engine, thus
* an object for that engine (which will subsequently be kept
* in this object for future use) is returned.
* @param string $filename The name of the template file,
* @return object The needed template object to render the template.
// for files that we cannot find engine info for,
// otherwise, compare $this' engine to the file's engine
// need to load another engine... if already instantiated,
// and stored herein, return that
// FIXME: this assumes same engine setup in all template
// set config files that have same engine in common
// (but keeping a separate class object for every
// template set seems like overkill... for now we
// won't do that unless it becomes a problem)
// otherwise, instantiate new engine object, add to cache
// now, need to copy over all the assigned variables
// from $this to the rendering engine (YUCK! -- we need
// to discourage template authors from creating
// situations where engine changes occur)
$rendering_engine->clear_all_assign();
return $rendering_engine;
* Return all JavaScript files provided by the template.
* All files found in the template set's "js" directory (and
* that of its ancestors) with the extension ".js" are returned.
* @param boolean $full_path When FALSE, only the file names
* are included in the return array;
* otherwise, path information is
* included (relative to SM_PATH)
* (OPTIONAL; default only file names)
* @return array The required file names/paths.
// since any page from a parent template set
// could end up being loaded, we have to load
// all js files from ancestor template sets,
//$directory = SM_PATH . $this->get_template_file_directory() . 'js';
//$js_files = list_files($directory, '.js', !$full_path);
// parse out .js files only
foreach ($js_files as $file) {
* Return all alternate stylesheets provided by template.
* All files found in the template set's "css/alternates"
* directory (and that of its ancestors) with the extension
* Note that prettified names are constructed herein by
* taking the file name, changing underscores to spaces,
* removing the ".css" from the end of the file, and
* capitalizing each word in the resultant name.
* @param boolean $full_path When FALSE, only the file names
* are included in the return array;
* otherwise, path information is
* included (relative to SM_PATH)
* (OPTIONAL; default only file names)
* @return array A list of the available alternate stylesheets,
* where the keys are the file names (formatted
* according to $full_path) for the stylesheets,
* and the values are the prettified version of
* the file names for display to the user.
// since any page from a parent template set
// could end up being loaded, we will load
// all alternate css files from ancestor
// template sets, not just this set
//$directory = SM_PATH . $this->get_template_file_directory() . 'css/alternates';
//$css_files = list_files($directory, '.css', !$full_path);
// parse out .css files only
foreach ($css_files as $file) {
$return_array[$file] =
$pretty_name;
$return_array[basename($file)] =
$pretty_name;
* Return all standard stylsheets provided by the template.
* All files found in the template set's "css" directory (and
* that of its ancestors) with the extension ".css" except
* "rtl.css" (which is dealt with separately) are returned.
* @param boolean $full_path When FALSE, only the file names
* are included in the return array;
* otherwise, path information is
* included (relative to SM_PATH)
* (OPTIONAL; default only file names)
* @return array The required file names/paths.
// since any page from a parent template set
// could end up being loaded, we have to load
// all css files from ancestor template sets,
//$directory = SM_PATH . $this->get_template_file_directory() . 'css';
//$css_files = list_files($directory, '.css', !$full_path);
// need to leave out "rtl.css"
foreach ($css_files as $file) {
// return sheets for the current template set
// last so we can enable any custom overrides
// of styles in ancestor sheets
* Generate links to all this template set's standard stylesheets
* Subclasses can override this function if stylesheets are
* created differently for the template set's target output
* @return string The stylesheet links as they should be sent
* Push out any other stylesheet links as provided (for
* stylesheets not included with the current template set)
* Subclasses can override this function if stylesheets are
* created differently for the template set's target output
* @param mixed $sheets List of the desired stylesheets
* (file path to be used in stylesheet
* href attribute) to output (or single
FIXME: We could make the incoming array more complex so it can
also contain the other parameters for create_css_link()
such as $name, $alt, $mtype, and $xhtml_end
* @return string The stylesheet links as they should be sent
if (!is_array($sheets)) $sheets =
array($sheets);
foreach ($sheets as $sheet) {
* Send HTTP header(s) to browser.
* Subclasses can override this function if headers are
* managed differently in the template set's target output
* @param mixed $headers A list of (or a single) header
if (!is_array($headers)) $headers =
array($headers);
foreach ($headers as $header) {
* Generate a link to the right-to-left stylesheet for
* this template set by getting the "rtl.css" file from
* this template set, its parent (or grandparent, etc.)
* template set, the fall-back template set, or finally,
* fall back to SquirrelMail's own "rtl.css" if need be.
* Subclasses can override this function if stylesheets are
* created differently for the template set's target output
* @return string The stylesheet link as it should be sent
// get right template file
// fall back to SquirrelMail's own default stylesheet
* @param string $file The template file to use
echo
$this->fetch($file);
* Applies the template and returns the resultant content string.
* @param string $file The template file to use
* @return string The template contents after applying the given template
// get right template file
// special case stylesheet.tpl falls back to SquirrelMail's
// own default stylesheet
if (empty($template) &&
$file ==
'css/stylesheet.tpl') {
$template =
SM_PATH .
'css/default.css';
.
'" could not be fetched!', E_USER_ERROR);
$aPluginOutput =
array();
array($aPluginOutput, $this));
$this->assign('plugin_output', $aPluginOutput);
//$output = $this->apply_template($template);
$output =
$rendering_engine->apply_template($template);
// CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
// RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
// using this hook will probably be rejected by the
* Assigns values to template variables
* Note: this is an abstract method that must be implemented by subclass.
* @param array|string$tpl_var the template variable name(s)
* @param mixed $value the value to assign
function assign($tpl_var, $value =
NULL) {
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the assign() method.', E_USER_ERROR);
* Assigns values to template variables by reference
* Note: this is an abstract method that must be implemented by subclass.
* @param string $tpl_var the template variable name
* @param mixed $value the referenced value to assign
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the assign_by_ref() method.', E_USER_ERROR);
* Clears the values of all assigned varaiables.
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the clear_all_assign() method.', E_USER_ERROR);
* Returns assigned variable value(s).
* @param string $varname If given, the value of that variable
* is returned, assuming it has been
* previously assigned. If not specified
* an array of all assigned variables is
* @return mixed Desired single variable value or list of all
* assigned variable values.
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the get_template_vars() method.', E_USER_ERROR);
* Appends values to template variables
* Note: this is an abstract method that must be implemented by subclass.
* @param array|string$tpl_var the template variable name(s)
* @param mixed $value the value to append
* @param boolean $merge when $value is given as an array,
* this indicates whether or not that
* array itself should be appended as
* a new template variable value or if
* that array's values should be merged
* into the existing array of template
function append($tpl_var, $value =
NULL, $merge =
FALSE) {
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the append() method.', E_USER_ERROR);
* Appends values to template variables by reference
* Note: this is an abstract method that must be implemented by subclass.
* @param string $tpl_var the template variable name
* @param mixed $value the referenced value to append
* @param boolean $merge when $value is given as an array,
* this indicates whether or not that
* array itself should be appended as
* a new template variable value or if
* that array's values should be merged
* into the existing array of template
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the append_by_ref() method.', E_USER_ERROR);
* Applys the template and generates final output destined
* Note: this is an abstract method that must be implemented by subclass.
* @param string $filepath The full file path to the template to be applied
* @return string The output for the given template
trigger_error('Template subclass (' .
$this->template_engine .
'Template.class.php) needs to implement the apply_template() method.', E_USER_ERROR);
Documentation generated on Sat, 07 Oct 2006 16:13:56 +0300 by phpDocumentor 1.3.0RC6