3 require(SM_PATH
. 'functions/template.php');
8 * This file contains an abstract (PHP 4, so "abstract" is relative)
9 * class meant to define the basic template interface for the
10 * SquirrelMail core application. Subclasses should extend this
11 * class with any custom functionality needed to interface a target
12 * templating engine with SquirrelMail.
14 * @copyright © 2003-2006 The SquirrelMail Project Team
15 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
17 * @package squirrelmail
18 * @subpackage Template
24 * The SquirrelMail Template class.
26 * Basic template class for capturing values and pluging them into a template.
27 * This class uses a similar API to Smarty.
29 * Methods that must be implemented by subclasses are as follows (see method
30 * stubs below for further information about expected behavior):
40 * @author Paul Lesniewski
41 * @package squirrelmail
53 var $template_set_id = '';
56 * The template set base directory (relative path from
57 * the main SquirrelMail directory (SM_PATH))
62 var $template_dir = '';
65 * The template engine (please use constants defined in constants.php)
70 var $template_engine = '';
73 * The fall-back template ID
78 var $fallback_template_set_id = '';
81 * The fall-back template directory (relative
82 * path from the main SquirrelMail directory (SM_PATH))
87 var $fallback_template_dir = '';
90 * The fall-back template engine (please use
91 * constants defined in constants.php)
96 var $fallback_template_engine = '';
99 * Template file cache. Structured as an array, whose keys
100 * are all the template file names (with path information relative
101 * to the template set's base directory, e.g., "css/style.css")
102 * found in all parent template sets including the ultimate fall-back
103 * template set. Array values are sub-arrays with the
104 * following key-value pairs:
106 * PATH -- file path, relative to SM_PATH
107 * SET_ID -- the ID of the template set that this file belongs to
108 * ENGINE -- the engine needed to render this template file
111 var $template_file_cache = array();
114 * Extra template engine class objects for rendering templates
115 * that require a different engine than the one for the current
116 * template set. Keys should be the name of the template engine,
117 * values are the corresponding class objects.
122 var $other_template_engine_objects = array();
127 * Please do not call directly. Use Template::construct_template().
129 * @param string $template_set_id the template ID
132 function Template($template_set_id) {
133 //FIXME: find a way to test that this is ONLY ever called
134 // from the construct_template() method (I doubt it
135 // is worth the trouble to parse the current stack trace)
137 // trigger_error('Please do not use default Template() constructor. Instead, use Template::construct_template().', E_USER_ERROR);
139 $this->set_up_template($template_set_id);
146 * This method should always be called instead of trying
147 * to get a Template object from the normal/default constructor,
148 * and is necessary in order to control the return value.
150 * @param string $template_set_id the template ID
152 * @return object The correct Template object for the given template set
157 function construct_template($template_set_id) {
159 $template = new Template($template_set_id);
160 return $template->get_template_engine_subclass();
165 * Set up internal attributes
167 * This method does most of the work for setting up
168 * newly constructed objects.
170 * @param string $template_set_id the template ID
173 function set_up_template($template_set_id) {
175 // FIXME: do we want to place any restrictions on the ID like
176 // making sure no slashes included?
179 $this->template_set_id
= $template_set_id;
182 $this->fallback_template_set_id
= Template
::get_fallback_template_set();
185 // set up template directories
188 = Template
::calculate_template_file_directory($this->template_set_id
);
189 $this->fallback_template_dir
190 = Template
::calculate_template_file_directory($this->fallback_template_set_id
);
193 // determine template engine
194 // FIXME: assuming PHP template engine may not necessarily be a good thing
196 $this->template_engine
= Template
::get_template_config($this->template_set_id
,
201 // get template file cache
203 $this->template_file_cache
= Template
::cache_template_file_hierarchy();
208 * Determine what the ultimate fallback template set is.
210 * NOTE that if the fallback setting cannot be found in the
211 * main SquirrelMail configuration settings that the value
212 * of $default is returned.
214 * @param string $default The template set ID to use if
215 * the fallback setting cannot be
216 * found in SM config (optional;
217 * defaults to "default").
219 * @return string The ID of the fallback template set.
224 function get_fallback_template_set($default='default') {
226 // FIXME: do we want to place any restrictions on the ID such as
227 // making sure no slashes included?
229 // values are in main SM config file
231 global $templateset_fallback, $aTemplateSet;
232 $aTemplateSet = (!isset($aTemplateSet) ||
!is_array($aTemplateSet)
233 ?
array() : $aTemplateSet);
234 $templateset_fallback = (!isset($templateset_fallback)
235 ?
0 : $templateset_fallback);
237 return (!empty($aTemplateSet[$templateset_fallback]['ID'])
238 ?
$aTemplateSet[$templateset_fallback]['ID'] : $default);
243 * Determine what the default template set is.
245 * NOTE that if the default setting cannot be found in the
246 * main SquirrelMail configuration settings that the value
247 * of $default is returned.
249 * @param string $default The template set ID to use if
250 * the default setting cannot be
251 * found in SM config (optional;
252 * defaults to "default").
254 * @return string The ID of the default template set.
259 function get_default_template_set($default='default') {
261 // FIXME: do we want to place any restrictions on the ID such as
262 // making sure no slashes included?
264 // values are in main SM config file
266 global $templateset_default, $aTemplateSet;
267 $aTemplateSet = (!isset($aTemplateSet) ||
!is_array($aTemplateSet)
268 ?
array() : $aTemplateSet);
269 $templateset_default = (!isset($templateset_default)
270 ?
0 : $templateset_default);
272 return (!empty($aTemplateSet[$templateset_default]['ID'])
273 ?
$aTemplateSet[$templateset_default]['ID'] : $default);
278 * Instantiate and return correct subclass for this template
279 * set's templating engine.
281 * @param string $template_set_id The template set whose engine
282 * is to be used as an override
283 * (if not given, this template
284 * set's engine is used) (optional).
286 * @return object The Template subclass object for the template engine.
289 function get_template_engine_subclass($template_set_id='') {
291 if (empty($template_set_id)) $template_set_id = $this->template_set_id
;
292 // FIXME: assuming PHP template engine may not necessarily be a good thing
293 $engine = Template
::get_template_config($template_set_id,
294 'template_engine', SQ_PHP_TEMPLATE
);
297 $engine_class_file = SM_PATH
. 'class/template/'
298 . $engine . 'Template.class.php';
300 if (!file_exists($engine_class_file)) {
301 trigger_error('Unknown template engine (' . $engine
302 . ') was specified in template configuration file',
306 $engine_class = $engine . 'Template';
307 require_once($engine_class_file);
308 return new $engine_class($template_set_id);
313 * Determine the relative template directory path for
314 * the given template ID.
316 * @param string $template_set_id The template ID from which to build
319 * @return string The relative template path (based off of SM_PATH)
324 function calculate_template_file_directory($template_set_id) {
326 return 'templates/' . $template_set_id . '/';
331 * Determine the relative images directory path for
332 * the given template ID.
334 * @param string $template_set_id The template ID from which to build
337 * @return string The relative images path (based off of SM_PATH)
342 function calculate_template_images_directory($template_set_id) {
344 return 'templates/' . $template_set_id . '/images/';
349 * Return the relative template directory path for this template set.
351 * @return string The relative path to the template directory based
352 * from the main SquirrelMail directory (SM_PATH).
355 function get_template_file_directory() {
357 return $this->template_dir
;
362 * Return the template ID for the fallback template set.
364 * @return string The ID of the fallback template set.
367 function get_fallback_template_set_id() {
369 return $this->fallback_template_set_id
;
374 * Return the relative template directory path for the
375 * fallback template set.
377 * @return string The relative path to the fallback template
378 * directory based from the main SquirrelMail
379 * directory (SM_PATH).
382 function get_fallback_template_file_directory() {
384 return $this->fallback_template_dir
;
389 * Get template set config setting
391 * Given a template set ID and setting name, returns the
392 * setting's value. Note that settings are cached in
393 * session, so "live" changes to template configuration
394 * won't be reflected until the user logs out and back
397 * @param string $template_set_id The template set for which
398 * to look up the setting.
399 * @param string $setting The name of the setting to
401 * @param mixed $default When the requested setting
402 * is not found, the contents
403 * of this value are returned
404 * instead (optional; default
406 * NOTE that unlike sqGetGlobalVar(),
407 * this function will also return
408 * the default value if the
409 * requested setting is found
411 * @param boolean $live_config When TRUE, the target template
412 * set's configuration file is
413 * reloaded every time this
414 * method is called. Default
415 * behavior is to only load the
416 * configuration file if it had
417 * never been loaded before, but
418 * not again after that (optional;
419 * default FALSE). Use with care!
420 * Should mostly be used for
423 * @return mixed The desired setting's value or if not found,
424 * the contents of $default are returned.
429 function get_template_config($template_set_id, $setting,
430 $default=NULL, $live_config=FALSE) {
432 sqGetGlobalVar('template_configuration_settings',
433 $template_configuration_settings,
437 if ($live_config) unset($template_configuration_settings[$template_set_id]);
440 // NOTE: could use isset() instead of empty() below, but
441 // this function is designed to replace empty values
442 // as well as non-existing values with $default
444 if (!empty($template_configuration_settings[$template_set_id][$setting]))
445 return $template_configuration_settings[$template_set_id][$setting];
448 // if template set configuration has been loaded, but this
449 // setting is not known, return $default
451 if (!empty($template_configuration_settings[$template_set_id]))
455 // otherwise (template set configuration has not been loaded before),
456 // load it into session and return the desired setting after that
458 $template_config_file = SM_PATH
459 . Template
::calculate_template_file_directory($template_set_id)
462 if (!file_exists($template_config_file)) {
464 trigger_error('No template configuration file was found where expected: ("'
465 . $template_config_file . '")', E_USER_ERROR
);
469 // we require() the file to let PHP do the variable value
470 // parsing for us, and read the file in manually so we can
471 // know what variable names are used in the config file
472 // (settings can be different depending on specific requirements
473 // of different template engines)... the other way this may
474 // be accomplished is to somehow diff the symbol table
475 // before/after the require(), but anyway, this code should
476 // only run once for this template set...
478 require($template_config_file);
479 $file_contents = implode("\n", file($template_config_file));
482 // note that this assumes no template settings have
483 // a string in them that looks like a variable name like $x
484 // also note that this will attempt to grab things like
485 // $Id found in CVS headers, so we try to adjust for that
486 // by checking that the variable is actually set
488 preg_match_all('/\$(\w+)/', $file_contents, $variables, PREG_PATTERN_ORDER
);
489 foreach ($variables[1] as $variable) {
490 if (isset($
$variable))
491 $template_configuration_settings[$template_set_id][$variable]
495 sqsession_register($template_configuration_settings,
496 'template_configuration_settings');
498 // NOTE: could use isset() instead of empty() below, but
499 // this function is designed to replace empty values
500 // as well as non-existing values with $default
502 if (!empty($template_configuration_settings[$template_set_id][$setting]))
503 return $template_configuration_settings[$template_set_id][$setting];
512 * Obtain template file hierarchy from cache.
514 * If the file hierarchy does not exist in session, it is
515 * constructed and stored in session before being returned
518 * @param boolean $regenerate_cache When TRUE, the file hierarchy
519 * is reloaded and stored fresh
520 * (optional; default FALSE).
521 * @param array $additional_files Must be in same form as the
522 * files in the file hierarchy
523 * cache. These are then added
524 * to the cache (optional; default
525 * empty - no additional files).
527 * @return array Template file hierarchy array, whose keys
528 * are all the template file names (with path
529 * information relative to the template set's
530 * base directory, e.g., "css/style.css")
531 * found in all parent template sets including
532 * the ultimate fall-back template set.
533 * Array values are sub-arrays with the
534 * following key-value pairs:
536 * PATH -- file path, relative to SM_PATH
537 * SET_ID -- the ID of the template set that this file belongs to
538 * ENGINE -- the engine needed to render this template file
543 function cache_template_file_hierarchy($regenerate_cache=FALSE,
544 $additional_files=array()) {
546 sqGetGlobalVar('template_file_hierarchy', $template_file_hierarchy,
547 SQ_SESSION
, array());
550 if ($regenerate_cache) unset($template_file_hierarchy);
553 if (!empty($template_file_hierarchy)) {
555 // have to add additional files if given before returning
557 if (!empty($additional_files)) {
558 $template_file_hierarchy = array_merge($template_file_hierarchy,
560 sqsession_register($template_file_hierarchy,
561 'template_file_hierarchy');
564 return $template_file_hierarchy;
568 // nothing in cache apparently, so go build it now
570 // FIXME: not sure if there is any possibility that
571 // this could be called when $sTemplateID has
572 // yet to be defined... throw error for now,
573 // but if the error occurs, it's a coding error
574 // rather than a configuration error
577 if (empty($sTemplateID)) {
579 trigger_error('Template set ID unknown', E_USER_ERROR
);
583 $template_file_hierarchy = Template
::catalog_template_files($sTemplateID);
585 // additional files, if any
587 if (!empty($additional_files)) {
588 $template_file_hierarchy = array_merge($template_file_hierarchy,
592 sqsession_register($template_file_hierarchy,
593 'template_file_hierarchy');
595 return $template_file_hierarchy;
602 * Traverse template hierarchy and catalogue all template
603 * files (for storing in cache).
605 * Paths to all files in all parent, grand-parent, great grand
606 * parent, etc. template sets (including the fallback template)
607 * are catalogued; for identically named files, the file earlier
608 * in the hierarchy (closest to this template set) is used.
610 * @param string $template_set_id The template set in which to
612 * @param array $file_list The file list so far to be added
613 * to (allows recursive behavior)
614 * (optional; default empty array).
615 * @param string $directory The directory in which to search for
616 * files (must be given as full path).
617 * If empty, starts at top-level template
618 * set directory (optional; default empty).
619 * NOTE! Use with care, as behavior is
620 * unpredictable if directory given is not
621 * part of correct template set.
623 * @return mixed The top-level caller will have an array of template
624 * files returned to it; recursive calls to this function
625 * do not receive any return value at all. The format
626 * of the template file array is as described for the
627 * Template class attribute $template_file_cache
632 function catalog_template_files($template_set_id, $file_list=array(), $directory='') {
634 $template_base_dir = SM_PATH
635 . Template
::calculate_template_file_directory($template_set_id);
637 if (empty($directory)) {
638 $directory = $template_base_dir;
641 $files_and_dirs = list_files($directory, '', FALSE, TRUE, FALSE, TRUE);
643 // recurse for all the subdirectories in the template set
645 foreach ($files_and_dirs['DIRECTORIES'] as $dir) {
646 $file_list = Template
::catalog_template_files($template_set_id, $file_list, $dir);
649 // place all found files in the cache
650 // FIXME: assuming PHP template engine may not necessarily be a good thing
652 $engine = Template
::get_template_config($template_set_id,
653 'template_engine', SQ_PHP_TEMPLATE
);
654 foreach ($files_and_dirs['FILES'] as $file) {
656 // remove the part of the file path corresponding to the
657 // template set's base directory
659 $relative_file = substr($file, strlen($template_base_dir));
661 // only put file in cache if not already found in earlier template
663 if (!isset($file_list[$relative_file])) {
664 $file_list[$relative_file] = array(
666 'SET_ID' => $template_set_id,
674 // now if we are currently at the top-level of the template
675 // set base directory, we need to move on to the parent
676 // template set, if any
678 if ($directory == $template_base_dir) {
680 // use fallback when we run out of parents
682 $fallback_id = Template
::get_fallback_template_set();
683 $parent_id = Template
::get_template_config($template_set_id,
684 'parent_template_set',
687 // were we already all the way to the last level? just exit
689 // note that this code allows the fallback set to have
690 // a parent, too, but can result in endless loops
691 // if ($parent_id == $template_set_id) {
693 if ($fallback_id == $template_set_id) {
697 $file_list = Template
::catalog_template_files($parent_id, $file_list);
706 * Look for a template file in a plugin; add to template
707 * file cache if found.
709 * The file is searched for in the following order:
711 * - A directory for the current template set within the plugin:
712 * SM_PATH/plugins/<plugin name>/templates/<template name>/
713 * - In a directory for one of the current template set's ancestor
714 * (inherited) template sets within the plugin:
715 * SM_PATH/plugins/<plugin name>/templates/<parent template name>/
716 * - In a directory for the fallback template set within the plugin:
717 * SM_PATH/plugins/<plugin name>/templates/<fallback template name>/
719 * @param string $plugin The name of the plugin
720 * @param string $file The name of the template file
721 * @param string $template_set_id The ID of the template for which
722 * to start looking for the file
723 * (optional; default is current
726 * @return boolean TRUE if the template file was found, FALSE otherwise.
729 function find_and_cache_plugin_template_file($plugin, $file, $template_set_id='') {
731 if (empty($template_set_id))
732 $template_set_id = $this->template_set_id
;
734 $file_path = SM_PATH
. 'plugins/' . $plugin . '/'
735 . $this->calculate_template_file_directory($template_set_id)
738 if (file_exists($file_path)) {
739 // FIXME: assuming PHP template engine may not necessarily be a good thing
740 $engine = $this->get_template_config($template_set_id,
741 'template_engine', SQ_PHP_TEMPLATE
);
742 $file_list = array('plugins/' . $plugin . '/' . $file => array(
743 'PATH' => $file_path,
744 'SET_ID' => $template_set_id,
748 $this->template_file_cache
749 = $this->cache_template_file_hierarchy(FALSE, $file_list);
754 // not found yet, try parent template set
755 // (use fallback when we run out of parents)
757 $fallback_id = $this->get_fallback_template_set();
758 $parent_id = $this->get_template_config($template_set_id,
759 'parent_template_set',
762 // were we already all the way to the last level? just exit
764 // note that this code allows the fallback set to have
765 // a parent, too, but can result in endless loops
766 // if ($parent_id == $template_set_id) {
768 if ($fallback_id == $template_set_id) {
772 return $this->find_and_cache_plugin_template_file($plugin, $file, $parent_id);
777 * Find the right template file.
779 * The template file is taken from the template file cache, thus
780 * the file is taken from the current template, one of its
781 * ancestors or the fallback template.
783 * Note that it is perfectly acceptable to load template files from
784 * template subdirectories. For example, JavaScript templates found
785 * in the js/ subdirectory would be loaded by passing
786 * "js/<javascript file name>" as the $filename.
788 * Note that the caller can also ask for ALL files in a directory
789 * (and those in the same directory for all ancestor template sets)
790 * by giving a $filename that is a directory name (ending with a
793 * If not found and the file is a plugin template file (indicated
794 * by the presence of "plugins/" on the beginning of $filename),
795 * the target plugin is searched for a substitue template file
796 * before just returning nothing.
798 * Plugin authors must note that the $filename MUST be prefaced
799 * with "plugins/<plugin name>/" in order to correctly resolve the
802 * @param string $filename The name of the template file,
803 * possibly prefaced with
804 * "plugins/<plugin name>/"
805 * indicating that it is a plugin
806 * template, or ending with a
807 * slash, indicating that all files
808 * for that directory name should
811 * @return mixed The full path to the template file or a list
812 * of all files in the given directory if $filename
813 * ends with a slash; if not found, an empty string
814 * is returned. The caller is responsible for
815 * throwing errors or other actions if template
819 function get_template_file_path($filename) {
821 // return list of all files in a directory (and that
824 if ($filename{strlen($filename) - 1} == '/') {
826 $return_array = array();
827 foreach ($this->template_file_cache
as $file => $file_info) {
829 // only want files in the requested directory
830 // (AND not in a subdirectory!)
832 if (strpos($file, $filename) === 0
833 && strpos($file, '/', strlen($filename)) === FALSE)
834 $return_array[] = $file_info['PATH'];
837 return $return_array;
841 // figure out what to do with files not found
843 if (empty($this->template_file_cache
[$filename]['PATH'])) {
845 // plugins get one more chance below; any other
846 // files we just give up now
848 if (strpos($filename, 'plugins/') !== 0)
851 $plugin_name = substr($filename, 8, strpos($filename, '/', 8) - 8);
852 $file = substr($filename, strlen($plugin_name) +
9);
854 if (!$this->find_and_cache_plugin_template_file($plugin_name, $file))
859 return $this->template_file_cache
[$filename]['PATH'];
864 * Get template engine needed to render given template file.
866 * If at all possible, just returns a reference to $this, but
867 * some template files may require a different engine, thus
868 * an object for that engine (which will subsequently be kept
869 * in this object for future use) is returned.
871 * @param string $filename The name of the template file,
873 * @return object The needed template object to render the template.
876 function get_rendering_template_engine_object($filename) {
878 // for files that we cannot find engine info for,
881 if (empty($this->template_file_cache
[$filename]['ENGINE']))
885 // otherwise, compare $this' engine to the file's engine
887 $engine = $this->template_file_cache
[$filename]['ENGINE'];
888 if ($this->template_engine
== $engine)
892 // need to load another engine... if already instantiated,
893 // and stored herein, return that
894 // FIXME: this assumes same engine setup in all template
895 // set config files that have same engine in common
896 // (but keeping a separate class object for every
897 // template set seems like overkill... for now we
898 // won't do that unless it becomes a problem)
900 if (!empty($this->other_template_engine_objects
[$engine])) {
901 $rendering_engine = $this->other_template_engine_objects
[$engine];
904 // otherwise, instantiate new engine object, add to cache
908 $template_set_id = $this->template_file_cache
[$filename]['SET_ID'];
909 $this->other_template_engine_objects
[$engine]
910 = $this->get_template_engine_subclass($template_set_id);
911 $rendering_engine = $this->other_template_engine_objects
[$engine];
915 // now, need to copy over all the assigned variables
916 // from $this to the rendering engine (YUCK! -- we need
917 // to discourage template authors from creating
918 // situations where engine changes occur)
920 $rendering_engine->clear_all_assign();
921 $rendering_engine->assign($this->get_template_vars());
924 // finally ready to go
926 return $rendering_engine;
931 * Return all JavaScript files provided by the template.
933 * All files found in the template set's "js" directory (and
934 * that of its ancestors) with the extension ".js" are returned.
936 * @param boolean $full_path When FALSE, only the file names
937 * are included in the return array;
938 * otherwise, path information is
939 * included (relative to SM_PATH)
940 * (OPTIONAL; default only file names)
942 * @return array The required file names/paths.
945 function get_javascript_includes($full_path=FALSE) {
947 // since any page from a parent template set
948 // could end up being loaded, we have to load
949 // all js files from ancestor template sets,
952 //$directory = SM_PATH . $this->get_template_file_directory() . 'js';
953 //$js_files = list_files($directory, '.js', !$full_path);
955 $js_files = $this->get_template_file_path('js/');
958 // parse out .js files only
960 $return_array = array();
961 foreach ($js_files as $file) {
963 if (substr($file, strlen($file) - 3) != '.js') continue;
966 $return_array[] = $file;
968 $return_array[] = basename($file);
973 return $return_array;
978 * Return all alternate stylesheets provided by template.
980 * All files found in the template set's "css/alternates"
981 * directory (and that of its ancestors) with the extension
982 * ".css" are returned.
984 * Note that prettified names are constructed herein by
985 * taking the file name, changing underscores to spaces,
986 * removing the ".css" from the end of the file, and
987 * capitalizing each word in the resultant name.
989 * @param boolean $full_path When FALSE, only the file names
990 * are included in the return array;
991 * otherwise, path information is
992 * included (relative to SM_PATH)
993 * (OPTIONAL; default only file names)
995 * @return array A list of the available alternate stylesheets,
996 * where the keys are the file names (formatted
997 * according to $full_path) for the stylesheets,
998 * and the values are the prettified version of
999 * the file names for display to the user.
1002 function get_alternative_stylesheets($full_path=FALSE) {
1004 // since any page from a parent template set
1005 // could end up being loaded, we will load
1006 // all alternate css files from ancestor
1007 // template sets, not just this set
1009 //$directory = SM_PATH . $this->get_template_file_directory() . 'css/alternates';
1010 //$css_files = list_files($directory, '.css', !$full_path);
1012 $css_files = $this->get_template_file_path('css/alternates/');
1015 // parse out .css files only
1017 $return_array = array();
1018 foreach ($css_files as $file) {
1020 if (substr($file, strlen($file) - 4) != '.css') continue;
1022 $pretty_name = ucwords(str_replace('_', ' ', substr(basename($file), 0, -4)));
1025 $return_array[$file] = $pretty_name;
1027 $return_array[basename($file)] = $pretty_name;
1032 return $return_array;
1037 * Return all standard stylsheets provided by the template.
1039 * All files found in the template set's "css" directory (and
1040 * that of its ancestors) with the extension ".css" except
1041 * "rtl.css" (which is dealt with separately) are returned.
1043 * @param boolean $full_path When FALSE, only the file names
1044 * are included in the return array;
1045 * otherwise, path information is
1046 * included (relative to SM_PATH)
1047 * (OPTIONAL; default only file names)
1049 * @return array The required file names/paths.
1052 function get_stylesheets($full_path=FALSE) {
1054 // since any page from a parent template set
1055 // could end up being loaded, we have to load
1056 // all css files from ancestor template sets,
1057 // not just this set
1059 //$directory = SM_PATH . $this->get_template_file_directory() . 'css';
1060 //$css_files = list_files($directory, '.css', !$full_path);
1062 $css_files = $this->get_template_file_path('css/');
1065 // need to leave out "rtl.css"
1067 $return_array = array();
1068 foreach ($css_files as $file) {
1070 if (substr($file, strlen($file) - 4) != '.css') continue;
1071 if (strtolower(basename($file)) == 'rtl.css') continue;
1074 $return_array[] = $file;
1076 $return_array[] = basename($file);
1081 return $return_array;
1086 * Generate links to all this template set's standard stylesheets
1088 * Subclasses can override this function if stylesheets are
1089 * created differently for the template set's target output
1092 * @return string The stylesheet links as they should be sent
1096 function fetch_standard_stylesheet_links()
1099 $sheets = $this->get_stylesheets(TRUE);
1100 return $this->fetch_external_stylesheet_links($sheets);
1105 * Push out any other stylesheet links as provided (for
1106 * stylesheets not included with the current template set)
1108 * Subclasses can override this function if stylesheets are
1109 * created differently for the template set's target output
1112 * @param mixed $sheets List of the desired stylesheets
1113 * (file path to be used in stylesheet
1114 * href attribute) to output (or single
1115 * stylesheet file path).
1116 FIXME: We could make the incoming array more complex so it can
1117 also contain the other parameters for create_css_link()
1118 such as $name, $alt, $mtype, and $xhtml_end
1121 * @return string The stylesheet links as they should be sent
1125 function fetch_external_stylesheet_links($sheets)
1128 if (!is_array($sheets)) $sheets = array($sheets);
1131 foreach ($sheets as $sheet) {
1132 $output .= create_css_link($sheet);
1140 * Send HTTP header(s) to browser.
1142 * Subclasses can override this function if headers are
1143 * managed differently in the template set's target output
1146 * @param mixed $headers A list of (or a single) header
1150 function header($headers)
1153 if (!is_array($headers)) $headers = array($headers);
1155 foreach ($headers as $header) {
1162 * Generate a link to the right-to-left stylesheet for
1163 * this template set by getting the "rtl.css" file from
1164 * this template set, its parent (or grandparent, etc.)
1165 * template set, the fall-back template set, or finally,
1166 * fall back to SquirrelMail's own "rtl.css" if need be.
1168 * Subclasses can override this function if stylesheets are
1169 * created differently for the template set's target output
1172 * @return string The stylesheet link as it should be sent
1176 function fetch_right_to_left_stylesheet_link()
1179 // get right template file
1181 $sheet = $this->get_template_file_path('css/rtl.css');
1183 // fall back to SquirrelMail's own default stylesheet
1185 if (empty($sheet)) {
1186 $sheet = SM_PATH
. 'css/rtl.css';
1189 return create_css_link($sheet);
1194 * Display the template
1196 * @param string $file The template file to use
1199 function display($file)
1202 echo $this->fetch($file);
1207 * Applies the template and returns the resultant content string.
1209 * @param string $file The template file to use
1211 * @return string The template contents after applying the given template
1214 function fetch($file) {
1216 // get right template file
1218 $template = $this->get_template_file_path($file);
1221 // special case stylesheet.tpl falls back to SquirrelMail's
1222 // own default stylesheet
1224 if (empty($template) && $file == 'css/stylesheet.tpl') {
1225 $template = SM_PATH
. 'css/default.css';
1229 if (empty($template)) {
1231 trigger_error('The template "' . htmlspecialchars($file)
1232 . '" could not be fetched!', E_USER_ERROR
);
1236 $aPluginOutput = array();
1237 $aPluginOutput = concat_hook_function('template_construct_' . $file,
1238 array($aPluginOutput, $this));
1239 $this->assign('plugin_output', $aPluginOutput);
1241 //$output = $this->apply_template($template);
1242 $rendering_engine = $this->get_rendering_template_engine_object($file);
1243 $output = $rendering_engine->apply_template($template);
1245 // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
1246 // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
1247 // using this hook will probably be rejected by the
1248 // SquirrelMail team.
1250 $output = filter_hook_function('template_output', $output);
1259 * Assigns values to template variables
1261 * Note: this is an abstract method that must be implemented by subclass.
1263 * @param array|string $tpl_var the template variable name(s)
1264 * @param mixed $value the value to assign
1267 function assign($tpl_var, $value = NULL) {
1269 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the assign() method.', E_USER_ERROR
);
1274 * Assigns values to template variables by reference
1276 * Note: this is an abstract method that must be implemented by subclass.
1278 * @param string $tpl_var the template variable name
1279 * @param mixed $value the referenced value to assign
1282 function assign_by_ref($tpl_var, &$value) {
1284 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the assign_by_ref() method.', E_USER_ERROR
);
1289 * Clears the values of all assigned varaiables.
1292 function clear_all_assign() {
1294 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the clear_all_assign() method.', E_USER_ERROR
);
1299 * Returns assigned variable value(s).
1301 * @param string $varname If given, the value of that variable
1302 * is returned, assuming it has been
1303 * previously assigned. If not specified
1304 * an array of all assigned variables is
1305 * returned. (optional)
1307 * @return mixed Desired single variable value or list of all
1308 * assigned variable values.
1311 function get_template_vars($varname=NULL) {
1313 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the get_template_vars() method.', E_USER_ERROR
);
1318 * Appends values to template variables
1320 * Note: this is an abstract method that must be implemented by subclass.
1322 * @param array|string $tpl_var the template variable name(s)
1323 * @param mixed $value the value to append
1324 * @param boolean $merge when $value is given as an array,
1325 * this indicates whether or not that
1326 * array itself should be appended as
1327 * a new template variable value or if
1328 * that array's values should be merged
1329 * into the existing array of template
1333 function append($tpl_var, $value = NULL, $merge = FALSE) {
1335 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the append() method.', E_USER_ERROR
);
1340 * Appends values to template variables by reference
1342 * Note: this is an abstract method that must be implemented by subclass.
1344 * @param string $tpl_var the template variable name
1345 * @param mixed $value the referenced value to append
1346 * @param boolean $merge when $value is given as an array,
1347 * this indicates whether or not that
1348 * array itself should be appended as
1349 * a new template variable value or if
1350 * that array's values should be merged
1351 * into the existing array of template
1355 function append_by_ref($tpl_var, &$value, $merge = FALSE) {
1357 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the append_by_ref() method.', E_USER_ERROR
);
1362 * Applys the template and generates final output destined
1363 * for the user's browser
1365 * Note: this is an abstract method that must be implemented by subclass.
1367 * @param string $filepath The full file path to the template to be applied
1369 * @return string The output for the given template
1372 function apply_template($filepath) {
1374 trigger_error('Template subclass (' . $this->template_engine
. 'Template.class.php) needs to implement the apply_template() method.', E_USER_ERROR
);