Don't include svn files in any of the cached template files
[squirrelmail.git] / class / template / Template.class.php
CommitLineData
de4d58cb 1<?php
de4d58cb 2/**
3 * Template.class.php
4 *
6806cbe5 5 * This file contains an abstract (PHP 4, so "abstract" is relative)
6 * class meant to define the basic template interface for the
de4d58cb 7 * SquirrelMail core application. Subclasses should extend this
8 * class with any custom functionality needed to interface a target
9 * templating engine with SquirrelMail.
6806cbe5 10 *
4b5049de 11 * @copyright &copy; 2003-2007 The SquirrelMail Project Team
de4d58cb 12 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
13 * @version $Id$
14 * @package squirrelmail
15 * @subpackage Template
16 * @since 1.5.2
17 *
18 */
19
948be6d4 20/** load template functions */
d281e128 21require(SM_PATH . 'functions/template/general_util.php');
948be6d4 22
de4d58cb 23/**
24 * The SquirrelMail Template class.
25 *
26 * Basic template class for capturing values and pluging them into a template.
27 * This class uses a similar API to Smarty.
28 *
29 * Methods that must be implemented by subclasses are as follows (see method
30 * stubs below for further information about expected behavior):
31 *
32 * assign()
33 * assign_by_ref()
d4c2aa24 34 * clear_all_assign()
35 * get_template_vars()
de4d58cb 36 * append()
37 * append_by_ref()
38 * apply_template()
39 *
01520835 40 * @author Paul Lesniewski <paul at squirrelmail.org>
de4d58cb 41 * @package squirrelmail
42 *
43 */
44class Template
45{
46
47 /**
48 * The template ID
49 *
50 * @var string
51 *
52 */
d4c2aa24 53 var $template_set_id = '';
de4d58cb 54
55 /**
6806cbe5 56 * The template set base directory (relative path from
d4c2aa24 57 * the main SquirrelMail directory (SM_PATH))
de4d58cb 58 *
59 * @var string
60 *
61 */
62 var $template_dir = '';
63
64 /**
65 * The template engine (please use constants defined in constants.php)
66 *
67 * @var string
68 *
69 */
70 var $template_engine = '';
71
72 /**
d4c2aa24 73 * The fall-back template ID
de4d58cb 74 *
75 * @var string
76 *
77 */
d4c2aa24 78 var $fallback_template_set_id = '';
de4d58cb 79
80 /**
6806cbe5 81 * The fall-back template directory (relative
d4c2aa24 82 * path from the main SquirrelMail directory (SM_PATH))
de4d58cb 83 *
84 * @var string
85 *
86 */
d4c2aa24 87 var $fallback_template_dir = '';
de4d58cb 88
89 /**
6806cbe5 90 * The fall-back template engine (please use
d4c2aa24 91 * constants defined in constants.php)
de4d58cb 92 *
93 * @var string
94 *
95 */
d4c2aa24 96 var $fallback_template_engine = '';
de4d58cb 97
98 /**
d4c2aa24 99 * Template file cache. Structured as an array, whose keys
100 * are all the template file names (with path information relative
6806cbe5 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
d4c2aa24 104 * following key-value pairs:
de4d58cb 105 *
d4c2aa24 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
de4d58cb 109 *
110 */
d4c2aa24 111 var $template_file_cache = array();
de4d58cb 112
740c26f7 113 /**
d4c2aa24 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.
118 *
119 * @var array
120 *
121 */
122 var $other_template_engine_objects = array();
123
de4d58cb 124 /**
125 * Constructor
126 *
127 * Please do not call directly. Use Template::construct_template().
128 *
d4c2aa24 129 * @param string $template_set_id the template ID
de4d58cb 130 *
131 */
d4c2aa24 132 function Template($template_set_id) {
6806cbe5 133//FIXME: find a way to test that this is ONLY ever called
de4d58cb 134// from the construct_template() method (I doubt it
135// is worth the trouble to parse the current stack trace)
136// if (???)
137// trigger_error('Please do not use default Template() constructor. Instead, use Template::construct_template().', E_USER_ERROR);
138
d4c2aa24 139 $this->set_up_template($template_set_id);
de4d58cb 140
141 }
142
143 /**
144 * Construct Template
145 *
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.
149 *
d4c2aa24 150 * @param string $template_set_id the template ID
de4d58cb 151 *
152 * @return object The correct Template object for the given template set
153 *
d4c2aa24 154 * @static
155 *
de4d58cb 156 */
d4c2aa24 157 function construct_template($template_set_id) {
de4d58cb 158
d4c2aa24 159 $template = new Template($template_set_id);
34904996 160 $template->override_plugins();
de4d58cb 161 return $template->get_template_engine_subclass();
162
163 }
164
165 /**
6806cbe5 166 * Set up internal attributes
de4d58cb 167 *
6806cbe5 168 * This method does most of the work for setting up
de4d58cb 169 * newly constructed objects.
170 *
d4c2aa24 171 * @param string $template_set_id the template ID
de4d58cb 172 *
173 */
d4c2aa24 174 function set_up_template($template_set_id) {
de4d58cb 175
176 // FIXME: do we want to place any restrictions on the ID like
177 // making sure no slashes included?
178 // get template ID
179 //
d4c2aa24 180 $this->template_set_id = $template_set_id;
de4d58cb 181
182
d4c2aa24 183 $this->fallback_template_set_id = Template::get_fallback_template_set();
de4d58cb 184
185
186 // set up template directories
187 //
6806cbe5 188 $this->template_dir
d4c2aa24 189 = Template::calculate_template_file_directory($this->template_set_id);
6806cbe5 190 $this->fallback_template_dir
d4c2aa24 191 = Template::calculate_template_file_directory($this->fallback_template_set_id);
de4d58cb 192
193
6806cbe5 194 // determine template engine
d4c2aa24 195 // FIXME: assuming PHP template engine may not necessarily be a good thing
de4d58cb 196 //
6806cbe5 197 $this->template_engine = Template::get_template_config($this->template_set_id,
d4c2aa24 198 'template_engine',
199 SQ_PHP_TEMPLATE);
de4d58cb 200
de4d58cb 201
d4c2aa24 202 // get template file cache
203 //
204 $this->template_file_cache = Template::cache_template_file_hierarchy();
de4d58cb 205
d4c2aa24 206 }
de4d58cb 207
d4c2aa24 208 /**
209 * Determine what the ultimate fallback template set is.
210 *
211 * NOTE that if the fallback setting cannot be found in the
212 * main SquirrelMail configuration settings that the value
213 * of $default is returned.
214 *
215 * @param string $default The template set ID to use if
216 * the fallback setting cannot be
217 * found in SM config (optional;
218 * defaults to "default").
219 *
220 * @return string The ID of the fallback template set.
221 *
222 * @static
223 *
224 */
225 function get_fallback_template_set($default='default') {
de4d58cb 226
d4c2aa24 227// FIXME: do we want to place any restrictions on the ID such as
228// making sure no slashes included?
de4d58cb 229
d4c2aa24 230 // values are in main SM config file
de4d58cb 231 //
d4c2aa24 232 global $templateset_fallback, $aTemplateSet;
6806cbe5 233 $aTemplateSet = (!isset($aTemplateSet) || !is_array($aTemplateSet)
d4c2aa24 234 ? array() : $aTemplateSet);
6806cbe5 235 $templateset_fallback = (!isset($templateset_fallback)
293906dd 236 ? $default : $templateset_fallback);
d4c2aa24 237
293906dd 238 // iterate through all template sets, is this a valid skin ID?
239 //
240 $found_it = FALSE;
241 foreach ($aTemplateSet as $aTemplate) {
f4138e60 242 if ($aTemplate['ID'] === $templateset_fallback) {
293906dd 243 $found_it = TRUE;
244 break;
245 }
246 }
247
248 if ($found_it)
249 return $templateset_fallback;
250
251 // FIXME: note that it is possible for $default to
252 // point to an invalid (nonexistent) template set
253 // and that error will not be caught here
254 //
255 return $default;
d4c2aa24 256
257 }
258
259 /**
260 * Determine what the default template set is.
261 *
262 * NOTE that if the default setting cannot be found in the
263 * main SquirrelMail configuration settings that the value
264 * of $default is returned.
265 *
266 * @param string $default The template set ID to use if
267 * the default setting cannot be
268 * found in SM config (optional;
269 * defaults to "default").
270 *
271 * @return string The ID of the default template set.
272 *
273 * @static
274 *
275 */
276 function get_default_template_set($default='default') {
277
278// FIXME: do we want to place any restrictions on the ID such as
279// making sure no slashes included?
280
281 // values are in main SM config file
282 //
283 global $templateset_default, $aTemplateSet;
284 $aTemplateSet = (!isset($aTemplateSet) || !is_array($aTemplateSet)
285 ? array() : $aTemplateSet);
6806cbe5 286 $templateset_default = (!isset($templateset_default)
293906dd 287 ? $default : $templateset_default);
d4c2aa24 288
293906dd 289 // iterate through all template sets, is this a valid skin ID?
290 //
291 $found_it = FALSE;
292 foreach ($aTemplateSet as $aTemplate) {
f4138e60 293 if ($aTemplate['ID'] === $templateset_default) {
293906dd 294 $found_it = TRUE;
295 break;
296 }
297 }
298
299 if ($found_it)
300 return $templateset_default;
301
302 // FIXME: note that it is possible for $default to
303 // point to an invalid (nonexistent) template set
304 // and that error will not be caught here
305 //
306 return $default;
de4d58cb 307
308 }
309
34904996 310 /**
311 * Allow template set to override plugin configuration by either
312 * adding or removing plugins.
313 *
e4dd471d 314 * NOTE: due to when this code executes, plugins activated here
315 * do not have access to the config_override and loading_prefs
316 * hooks; instead, such plugins can use the
317 * "template_plugins_override_after" hook defined below.
318 *
34904996 319 */
320 function override_plugins() {
321
e4dd471d 322 global $disable_plugins, $plugins, $squirrelmail_plugin_hooks, $null;
34904996 323 if ($disable_plugins) return;
324
6806cbe5 325 $add_plugins = Template::get_template_config($this->template_set_id,
34904996 326 'add_plugins', array());
6806cbe5 327 $remove_plugins = Template::get_template_config($this->template_set_id,
34904996 328 'remove_plugins', array());
329
330//FIXME (?) we assume $add_plugins and $remove_plugins are arrays -- we could
6806cbe5 331// error check here, or just assume that template authors or admins
34904996 332// won't screw up their config files
333
334
335 // disable all plugins? (can still add some by using $add_plugins)
336 //
337 if (in_array('*', $remove_plugins)) {
338 $plugins = array();
339 $squirrelmail_plugin_hooks = array();
340 $remove_plugins = array();
341 }
342
343
344 foreach ($add_plugins as $plugin_name) {
345 // add plugin to global plugin array
346 //
347 $plugins[] = $plugin_name;
348
349
350 // enable plugin -- emulate code from use_plugin() function
351 // in SquirrelMail core, but also need to call the
352 // "squirrelmail_plugin_init_<plugin_name>" function, which
353 // in static configuration is not called (this inconsistency
354 // could be a source of anomalous-seeming bugs in poorly
355 // coded plugins)
356 //
357 if (file_exists(SM_PATH . "plugins/$plugin_name/setup.php")) {
358 include_once(SM_PATH . "plugins/$plugin_name/setup.php");
359
360 $function = "squirrelmail_plugin_init_$plugin_name";
361 if (function_exists($function))
362 $function();
363 }
364 }
365
366 foreach ($remove_plugins as $plugin_name) {
367 // remove plugin from both global plugin & plugin hook arrays
368 //
369 $plugin_key = array_search($plugin_name, $plugins);
370 if (!is_null($plugin_key) && $plugin_key !== FALSE) {
371 unset($plugins[$plugin_key]);
372 if (is_array($squirrelmail_plugin_hooks))
373 foreach (array_keys($squirrelmail_plugin_hooks) as $hookName) {
374 unset($squirrelmail_plugin_hooks[$hookName][$plugin_name]);
375 }
376 }
377 }
378
e4dd471d 379 do_hook('template_plugins_override_after', $null);
380
34904996 381 }
382
de4d58cb 383 /**
384 * Instantiate and return correct subclass for this template
385 * set's templating engine.
386 *
d4c2aa24 387 * @param string $template_set_id The template set whose engine
6806cbe5 388 * is to be used as an override
389 * (if not given, this template
d4c2aa24 390 * set's engine is used) (optional).
391 *
de4d58cb 392 * @return object The Template subclass object for the template engine.
393 *
394 */
d4c2aa24 395 function get_template_engine_subclass($template_set_id='') {
396
397 if (empty($template_set_id)) $template_set_id = $this->template_set_id;
398 // FIXME: assuming PHP template engine may not necessarily be a good thing
6806cbe5 399 $engine = Template::get_template_config($template_set_id,
d4c2aa24 400 'template_engine', SQ_PHP_TEMPLATE);
de4d58cb 401
6806cbe5 402
403 $engine_class_file = SM_PATH . 'class/template/'
d4c2aa24 404 . $engine . 'Template.class.php';
de4d58cb 405
406 if (!file_exists($engine_class_file)) {
6806cbe5 407 trigger_error('Unknown template engine (' . $engine
de4d58cb 408 . ') was specified in template configuration file',
409 E_USER_ERROR);
410 }
411
d4c2aa24 412 $engine_class = $engine . 'Template';
413 require_once($engine_class_file);
414 return new $engine_class($template_set_id);
de4d58cb 415
416 }
417
418 /**
6806cbe5 419 * Determine the relative template directory path for
de4d58cb 420 * the given template ID.
421 *
6806cbe5 422 * @param string $template_set_id The template ID from which to build
d4c2aa24 423 * the directory path
de4d58cb 424 *
425 * @return string The relative template path (based off of SM_PATH)
426 *
d4c2aa24 427 * @static
428 *
de4d58cb 429 */
d4c2aa24 430 function calculate_template_file_directory($template_set_id) {
de4d58cb 431
d4c2aa24 432 return 'templates/' . $template_set_id . '/';
de4d58cb 433
434 }
435
436 /**
6806cbe5 437 * Determine the relative images directory path for
de4d58cb 438 * the given template ID.
439 *
6806cbe5 440 * @param string $template_set_id The template ID from which to build
d4c2aa24 441 * the directory path
de4d58cb 442 *
443 * @return string The relative images path (based off of SM_PATH)
444 *
d4c2aa24 445 * @static
446 *
de4d58cb 447 */
d4c2aa24 448 function calculate_template_images_directory($template_set_id) {
de4d58cb 449
d4c2aa24 450 return 'templates/' . $template_set_id . '/images/';
de4d58cb 451
452 }
453
454 /**
455 * Return the relative template directory path for this template set.
456 *
457 * @return string The relative path to the template directory based
458 * from the main SquirrelMail directory (SM_PATH).
459 *
460 */
461 function get_template_file_directory() {
462
463 return $this->template_dir;
464
465 }
466
d4c2aa24 467 /**
468 * Return the template ID for the fallback template set.
469 *
470 * @return string The ID of the fallback template set.
471 *
472 */
473 function get_fallback_template_set_id() {
474
475 return $this->fallback_template_set_id;
476
477 }
de4d58cb 478
479 /**
6806cbe5 480 * Return the relative template directory path for the
d4c2aa24 481 * fallback template set.
de4d58cb 482 *
6806cbe5 483 * @return string The relative path to the fallback template
484 * directory based from the main SquirrelMail
d4c2aa24 485 * directory (SM_PATH).
486 *
487 */
488 function get_fallback_template_file_directory() {
489
490 return $this->fallback_template_dir;
491
492 }
493
494 /**
495 * Get template set config setting
496 *
6806cbe5 497 * Given a template set ID and setting name, returns the
498 * setting's value. Note that settings are cached in
d4c2aa24 499 * session, so "live" changes to template configuration
500 * won't be reflected until the user logs out and back
501 * in again.
502 *
503 * @param string $template_set_id The template set for which
504 * to look up the setting.
505 * @param string $setting The name of the setting to
6806cbe5 506 * retrieve.
d4c2aa24 507 * @param mixed $default When the requested setting
508 * is not found, the contents
509 * of this value are returned
6806cbe5 510 * instead (optional; default
d4c2aa24 511 * is NULL).
512 * NOTE that unlike sqGetGlobalVar(),
513 * this function will also return
6806cbe5 514 * the default value if the
515 * requested setting is found
d4c2aa24 516 * but is empty.
517 * @param boolean $live_config When TRUE, the target template
518 * set's configuration file is
6806cbe5 519 * reloaded every time this
d4c2aa24 520 * method is called. Default
521 * behavior is to only load the
522 * configuration file if it had
523 * never been loaded before, but
524 * not again after that (optional;
525 * default FALSE). Use with care!
526 * Should mostly be used for
527 * debugging.
528 *
6806cbe5 529 * @return mixed The desired setting's value or if not found,
d4c2aa24 530 * the contents of $default are returned.
531 *
532 * @static
533 *
534 */
6806cbe5 535 function get_template_config($template_set_id, $setting,
d4c2aa24 536 $default=NULL, $live_config=FALSE) {
537
6806cbe5 538 sqGetGlobalVar('template_configuration_settings',
539 $template_configuration_settings,
540 SQ_SESSION,
d4c2aa24 541 array());
542
543 if ($live_config) unset($template_configuration_settings[$template_set_id]);
544
545
546 // NOTE: could use isset() instead of empty() below, but
547 // this function is designed to replace empty values
548 // as well as non-existing values with $default
549 //
550 if (!empty($template_configuration_settings[$template_set_id][$setting]))
551 return $template_configuration_settings[$template_set_id][$setting];
552
553
6806cbe5 554 // if template set configuration has been loaded, but this
d4c2aa24 555 // setting is not known, return $default
556 //
557 if (!empty($template_configuration_settings[$template_set_id]))
558 return $default;
559
560
6806cbe5 561 // otherwise (template set configuration has not been loaded before),
d4c2aa24 562 // load it into session and return the desired setting after that
563 //
6806cbe5 564 $template_config_file = SM_PATH
d4c2aa24 565 . Template::calculate_template_file_directory($template_set_id)
566 . 'config.php';
567
568 if (!file_exists($template_config_file)) {
569
6806cbe5 570 trigger_error('No template configuration file was found where expected: ("'
d4c2aa24 571 . $template_config_file . '")', E_USER_ERROR);
572
573 } else {
574
575 // we require() the file to let PHP do the variable value
576 // parsing for us, and read the file in manually so we can
6806cbe5 577 // know what variable names are used in the config file
d4c2aa24 578 // (settings can be different depending on specific requirements
579 // of different template engines)... the other way this may
6806cbe5 580 // be accomplished is to somehow diff the symbol table
d4c2aa24 581 // before/after the require(), but anyway, this code should
582 // only run once for this template set...
583 //
584 require($template_config_file);
585 $file_contents = implode("\n", file($template_config_file));
586
587
588 // note that this assumes no template settings have
589 // a string in them that looks like a variable name like $x
590 // also note that this will attempt to grab things like
591 // $Id found in CVS headers, so we try to adjust for that
592 // by checking that the variable is actually set
593 //
594 preg_match_all('/\$(\w+)/', $file_contents, $variables, PREG_PATTERN_ORDER);
595 foreach ($variables[1] as $variable) {
596 if (isset($$variable))
6806cbe5 597 $template_configuration_settings[$template_set_id][$variable]
d4c2aa24 598 = $$variable;
599 }
600
6806cbe5 601 sqsession_register($template_configuration_settings,
d4c2aa24 602 'template_configuration_settings');
603
604 // NOTE: could use isset() instead of empty() below, but
605 // this function is designed to replace empty values
606 // as well as non-existing values with $default
607 //
608 if (!empty($template_configuration_settings[$template_set_id][$setting]))
609 return $template_configuration_settings[$template_set_id][$setting];
610 else
611 return $default;
612
613 }
614
615 }
616
6806cbe5 617 /**
d4c2aa24 618 * Obtain template file hierarchy from cache.
619 *
620 * If the file hierarchy does not exist in session, it is
621 * constructed and stored in session before being returned
622 * to the caller.
623 *
624 * @param boolean $regenerate_cache When TRUE, the file hierarchy
625 * is reloaded and stored fresh
626 * (optional; default FALSE).
627 * @param array $additional_files Must be in same form as the
628 * files in the file hierarchy
629 * cache. These are then added
630 * to the cache (optional; default
631 * empty - no additional files).
632 *
633 * @return array Template file hierarchy array, whose keys
6806cbe5 634 * are all the template file names (with path
635 * information relative to the template set's
636 * base directory, e.g., "css/style.css")
637 * found in all parent template sets including
638 * the ultimate fall-back template set.
639 * Array values are sub-arrays with the
d4c2aa24 640 * following key-value pairs:
641 *
642 * PATH -- file path, relative to SM_PATH
643 * SET_ID -- the ID of the template set that this file belongs to
644 * ENGINE -- the engine needed to render this template file
645 *
646 * @static
de4d58cb 647 *
648 */
d4c2aa24 649 function cache_template_file_hierarchy($regenerate_cache=FALSE,
650 $additional_files=array()) {
651
6806cbe5 652 sqGetGlobalVar('template_file_hierarchy', $template_file_hierarchy,
d4c2aa24 653 SQ_SESSION, array());
654
655
656 if ($regenerate_cache) unset($template_file_hierarchy);
657
d4c2aa24 658 if (!empty($template_file_hierarchy)) {
659
660 // have to add additional files if given before returning
661 //
662 if (!empty($additional_files)) {
6806cbe5 663 $template_file_hierarchy = array_merge($template_file_hierarchy,
d4c2aa24 664 $additional_files);
665 sqsession_register($template_file_hierarchy,
666 'template_file_hierarchy');
667 }
668
669 return $template_file_hierarchy;
670 }
671
672
673 // nothing in cache apparently, so go build it now
674 //
6806cbe5 675 // FIXME: not sure if there is any possibility that
d4c2aa24 676 // this could be called when $sTemplateID has
677 // yet to be defined... throw error for now,
678 // but if the error occurs, it's a coding error
679 // rather than a configuration error
680 //
681 global $sTemplateID;
682 if (empty($sTemplateID)) {
683
684 trigger_error('Template set ID unknown', E_USER_ERROR);
685
686 } else {
687
688 $template_file_hierarchy = Template::catalog_template_files($sTemplateID);
689
690 // additional files, if any
691 //
692 if (!empty($additional_files)) {
6806cbe5 693 $template_file_hierarchy = array_merge($template_file_hierarchy,
d4c2aa24 694 $additional_files);
695 }
696
6806cbe5 697 sqsession_register($template_file_hierarchy,
d4c2aa24 698 'template_file_hierarchy');
699
700 return $template_file_hierarchy;
701
702 }
703
704 }
705
706 /**
6806cbe5 707 * Traverse template hierarchy and catalogue all template
d4c2aa24 708 * files (for storing in cache).
6806cbe5 709 *
710 * Paths to all files in all parent, grand-parent, great grand
711 * parent, etc. template sets (including the fallback template)
712 * are catalogued; for identically named files, the file earlier
d4c2aa24 713 * in the hierarchy (closest to this template set) is used.
6806cbe5 714 *
d7917c67 715 * Refuses to traverse directories called ".svn"
716 *
d4c2aa24 717 * @param string $template_set_id The template set in which to
718 * search for files
719 * @param array $file_list The file list so far to be added
720 * to (allows recursive behavior)
721 * (optional; default empty array).
6806cbe5 722 * @param string $directory The directory in which to search for
d4c2aa24 723 * files (must be given as full path).
724 * If empty, starts at top-level template
725 * set directory (optional; default empty).
726 * NOTE! Use with care, as behavior is
727 * unpredictable if directory given is not
728 * part of correct template set.
6806cbe5 729 *
d4c2aa24 730 * @return mixed The top-level caller will have an array of template
731 * files returned to it; recursive calls to this function
732 * do not receive any return value at all. The format
733 * of the template file array is as described for the
734 * Template class attribute $template_file_cache
735 *
736 * @static
737 *
738 */
739 function catalog_template_files($template_set_id, $file_list=array(), $directory='') {
740
6806cbe5 741 $template_base_dir = SM_PATH
d4c2aa24 742 . Template::calculate_template_file_directory($template_set_id);
743
744 if (empty($directory)) {
745 $directory = $template_base_dir;
746 }
747
d7917c67 748
749 // bail if we have been asked to traverse a Subversion directory
750 //
751 if (strpos($directory, '/.svn') === strlen($directory) - 5) return $file_list;
752
753
d4c2aa24 754 $files_and_dirs = list_files($directory, '', FALSE, TRUE, FALSE, TRUE);
de4d58cb 755
d4c2aa24 756 // recurse for all the subdirectories in the template set
757 //
758 foreach ($files_and_dirs['DIRECTORIES'] as $dir) {
759 $file_list = Template::catalog_template_files($template_set_id, $file_list, $dir);
760 }
761
762 // place all found files in the cache
763 // FIXME: assuming PHP template engine may not necessarily be a good thing
764 //
6806cbe5 765 $engine = Template::get_template_config($template_set_id,
d4c2aa24 766 'template_engine', SQ_PHP_TEMPLATE);
767 foreach ($files_and_dirs['FILES'] as $file) {
768
769 // remove the part of the file path corresponding to the
770 // template set's base directory
771 //
772 $relative_file = substr($file, strlen($template_base_dir));
773
948be6d4 774 /**
775 * only put file in cache if not already found in earlier template
776 * PATH should be relative to SquirrelMail top directory
777 */
d4c2aa24 778 if (!isset($file_list[$relative_file])) {
779 $file_list[$relative_file] = array(
948be6d4 780 'PATH' => substr($file,strlen(SM_PATH)),
d4c2aa24 781 'SET_ID' => $template_set_id,
782 'ENGINE' => $engine,
783 );
784 }
785
786 }
787
788
789 // now if we are currently at the top-level of the template
6806cbe5 790 // set base directory, we need to move on to the parent
d4c2aa24 791 // template set, if any
792 //
793 if ($directory == $template_base_dir) {
794
795 // use fallback when we run out of parents
796 //
797 $fallback_id = Template::get_fallback_template_set();
6806cbe5 798 $parent_id = Template::get_template_config($template_set_id,
799 'parent_template_set',
d4c2aa24 800 $fallback_id);
801
802 // were we already all the way to the last level? just exit
803 //
804 // note that this code allows the fallback set to have
805 // a parent, too, but can result in endless loops
806 // if ($parent_id == $template_set_id) {
807 //
808 if ($fallback_id == $template_set_id) {
809 return $file_list;
810 }
811
812 $file_list = Template::catalog_template_files($parent_id, $file_list);
813
814 }
815
816 return $file_list;
de4d58cb 817
818 }
819
d4c2aa24 820 /**
821 * Look for a template file in a plugin; add to template
822 * file cache if found.
823 *
824 * The file is searched for in the following order:
825 *
826 * - A directory for the current template set within the plugin:
827 * SM_PATH/plugins/<plugin name>/templates/<template name>/
828 * - In a directory for one of the current template set's ancestor
829 * (inherited) template sets within the plugin:
830 * SM_PATH/plugins/<plugin name>/templates/<parent template name>/
831 * - In a directory for the fallback template set within the plugin:
832 * SM_PATH/plugins/<plugin name>/templates/<fallback template name>/
833 *
834 * @param string $plugin The name of the plugin
835 * @param string $file The name of the template file
836 * @param string $template_set_id The ID of the template for which
6806cbe5 837 * to start looking for the file
838 * (optional; default is current
d4c2aa24 839 * template set ID).
840 *
841 * @return boolean TRUE if the template file was found, FALSE otherwise.
842 *
843 */
844 function find_and_cache_plugin_template_file($plugin, $file, $template_set_id='') {
845
846 if (empty($template_set_id))
847 $template_set_id = $this->template_set_id;
848
849 $file_path = SM_PATH . 'plugins/' . $plugin . '/'
6806cbe5 850 . $this->calculate_template_file_directory($template_set_id)
d4c2aa24 851 . $file;
852
853 if (file_exists($file_path)) {
854 // FIXME: assuming PHP template engine may not necessarily be a good thing
6806cbe5 855 $engine = $this->get_template_config($template_set_id,
d4c2aa24 856 'template_engine', SQ_PHP_TEMPLATE);
857 $file_list = array('plugins/' . $plugin . '/' . $file => array(
eb2425ec 858 'PATH' => substr($file_path, strlen(SM_PATH)),
859 'SET_ID' => $template_set_id,
860 'ENGINE' => $engine,
d4c2aa24 861 )
862 );
6806cbe5 863 $this->template_file_cache
d4c2aa24 864 = $this->cache_template_file_hierarchy(FALSE, $file_list);
865 return TRUE;
866 }
867
868
869 // not found yet, try parent template set
870 // (use fallback when we run out of parents)
871 //
872 $fallback_id = $this->get_fallback_template_set();
6806cbe5 873 $parent_id = $this->get_template_config($template_set_id,
874 'parent_template_set',
d4c2aa24 875 $fallback_id);
876
877 // were we already all the way to the last level? just exit
878 //
879 // note that this code allows the fallback set to have
880 // a parent, too, but can result in endless loops
881 // if ($parent_id == $template_set_id) {
882 //
883 if ($fallback_id == $template_set_id) {
884 return FALSE;
885 }
886
887 return $this->find_and_cache_plugin_template_file($plugin, $file, $parent_id);
888
889 }
de4d58cb 890
891 /**
892 * Find the right template file.
893 *
d4c2aa24 894 * The template file is taken from the template file cache, thus
6806cbe5 895 * the file is taken from the current template, one of its
d4c2aa24 896 * ancestors or the fallback template.
897 *
898 * Note that it is perfectly acceptable to load template files from
6806cbe5 899 * template subdirectories. For example, JavaScript templates found
900 * in the js/ subdirectory would be loaded by passing
d4c2aa24 901 * "js/<javascript file name>" as the $filename.
902 *
903 * Note that the caller can also ask for ALL files in a directory
904 * (and those in the same directory for all ancestor template sets)
905 * by giving a $filename that is a directory name (ending with a
906 * slash).
907 *
908 * If not found and the file is a plugin template file (indicated
909 * by the presence of "plugins/" on the beginning of $filename),
910 * the target plugin is searched for a substitue template file
911 * before just returning nothing.
de4d58cb 912 *
913 * Plugin authors must note that the $filename MUST be prefaced
6806cbe5 914 * with "plugins/<plugin name>/" in order to correctly resolve the
de4d58cb 915 * template file.
916 *
de4d58cb 917 * @param string $filename The name of the template file,
6806cbe5 918 * possibly prefaced with
de4d58cb 919 * "plugins/<plugin name>/"
920 * indicating that it is a plugin
6806cbe5 921 * template, or ending with a
d4c2aa24 922 * slash, indicating that all files
923 * for that directory name should
924 * be returned.
e5d57179 925 * @param boolean $directories_ok When TRUE, directory names
926 * are acceptable search values,
927 * and when returning a list of
928 * directory contents, sub-directory
929 * names will also be included
930 * (optional; default FALSE).
6806cbe5 931 * NOTE that empty directories
e5d57179 932 * are NOT included in the cache!
933 * @param boolean $directories_only When TRUE, only directory names
934 * are included in the returned
6806cbe5 935 * results. (optional; default
e5d57179 936 * FALSE). Setting this argument
6806cbe5 937 * to TRUE forces $directories_ok
e5d57179 938 * to TRUE as well.
6806cbe5 939 * NOTE that empty directories
e5d57179 940 * are NOT included in the cache!
d4c2aa24 941 *
942 * @return mixed The full path to the template file or a list
943 * of all files in the given directory if $filename
944 * ends with a slash; if not found, an empty string
6806cbe5 945 * is returned. The caller is responsible for
946 * throwing errors or other actions if template
d4c2aa24 947 * file is not found.
de4d58cb 948 *
949 */
6806cbe5 950 function get_template_file_path($filename,
951 $directories_ok=FALSE,
e5d57179 952 $directories_only=FALSE) {
de4d58cb 953
e5d57179 954 if ($directories_only) $directories_ok = TRUE;
955
956
957 // only looking for directory listing first...
958 //
d4c2aa24 959 // return list of all files in a directory (and that
960 // of any ancestors)
de4d58cb 961 //
d4c2aa24 962 if ($filename{strlen($filename) - 1} == '/') {
963
964 $return_array = array();
965 foreach ($this->template_file_cache as $file => $file_info) {
966
967 // only want files in the requested directory
968 // (AND not in a subdirectory!)
969 //
6806cbe5 970 if (!$directories_only && strpos($file, $filename) === 0
d4c2aa24 971 && strpos($file, '/', strlen($filename)) === FALSE)
948be6d4 972 $return_array[] = SM_PATH . $file_info['PATH'];
de4d58cb 973
e5d57179 974 // directories too? detect by finding any
975 // array key that matches a file in a sub-directory
976 // of the directory being processed
977 //
978 if ($directories_ok && strpos($file, $filename) === 0
979 && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
980 && strpos($file, '/', $pos + 1) === FALSE) {
6806cbe5 981 $directory_name = SM_PATH
982 . substr($file_info['PATH'],
983 0,
e5d57179 984 strrpos($file_info['PATH'], '/'));
985 if (!in_array($directory_name, $return_array))
986 $return_array[] = $directory_name;
987 }
988
d4c2aa24 989 }
990 return $return_array;
991
992 }
993
e5d57179 994
995 // just looking for singular file or directory below...
996 //
d4c2aa24 997 // figure out what to do with files not found
998 //
e5d57179 999 if ($directories_only || empty($this->template_file_cache[$filename]['PATH'])) {
1000
1001 // if looking for directories...
1002 // have to iterate through cache and detect
1003 // directory by matching any file inside of it
1004 //
1005 if ($directories_ok) {
1006 foreach ($this->template_file_cache as $file => $file_info) {
1007 if (strpos($file, $filename) === 0
1008 && ($pos = strpos($file, '/', strlen($filename))) !== FALSE
1009 && strpos($file, '/', $pos + 1) === FALSE) {
6806cbe5 1010 return SM_PATH . substr($file_info['PATH'],
1011 0,
e5d57179 1012 strrpos($file_info['PATH'], '/'));
1013 }
1014 }
1015
1016 if ($directories_only) return '';
1017 }
d4c2aa24 1018
6806cbe5 1019 // plugins get one more chance
de4d58cb 1020 //
e5d57179 1021 if (strpos($filename, 'plugins/') === 0) {
1022
1023 $plugin_name = substr($filename, 8, strpos($filename, '/', 8) - 8);
1024 $file = substr($filename, strlen($plugin_name) + 9);
1025
1026 if (!$this->find_and_cache_plugin_template_file($plugin_name, $file))
1027 return '';
1028 //FIXME: technically I guess we should check for directories
1029 // here too, but that's overkill (no need) presently
1030 // (plugin-provided alternate stylesheet dirs?!? bah.)
de4d58cb 1031
e5d57179 1032 }
de4d58cb 1033
e5d57179 1034 // nothing... return empty string (yes, the else is intentional!)
1035 //
1036 else return '';
de4d58cb 1037
d4c2aa24 1038 }
de4d58cb 1039
948be6d4 1040 return SM_PATH . $this->template_file_cache[$filename]['PATH'];
de4d58cb 1041
d4c2aa24 1042 }
de4d58cb 1043
d4c2aa24 1044 /**
1045 * Get template engine needed to render given template file.
1046 *
1047 * If at all possible, just returns a reference to $this, but
1048 * some template files may require a different engine, thus
1049 * an object for that engine (which will subsequently be kept
1050 * in this object for future use) is returned.
1051 *
1052 * @param string $filename The name of the template file,
1053 *
1054 * @return object The needed template object to render the template.
6806cbe5 1055 *
d4c2aa24 1056 */
1057 function get_rendering_template_engine_object($filename) {
6806cbe5 1058
d4c2aa24 1059 // for files that we cannot find engine info for,
1060 // just return $this
6806cbe5 1061 //
d4c2aa24 1062 if (empty($this->template_file_cache[$filename]['ENGINE']))
1063 return $this;
1064
6806cbe5 1065
d4c2aa24 1066 // otherwise, compare $this' engine to the file's engine
1067 //
1068 $engine = $this->template_file_cache[$filename]['ENGINE'];
1069 if ($this->template_engine == $engine)
1070 return $this;
1071
1072
1073 // need to load another engine... if already instantiated,
1074 // and stored herein, return that
6806cbe5 1075 // FIXME: this assumes same engine setup in all template
d4c2aa24 1076 // set config files that have same engine in common
1077 // (but keeping a separate class object for every
6806cbe5 1078 // template set seems like overkill... for now we
d4c2aa24 1079 // won't do that unless it becomes a problem)
1080 //
1081 if (!empty($this->other_template_engine_objects[$engine])) {
1082 $rendering_engine = $this->other_template_engine_objects[$engine];
de4d58cb 1083
de4d58cb 1084
d4c2aa24 1085 // otherwise, instantiate new engine object, add to cache
1086 // and return it
1087 //
1088 } else {
1089 $template_set_id = $this->template_file_cache[$filename]['SET_ID'];
1090 $this->other_template_engine_objects[$engine]
1091 = $this->get_template_engine_subclass($template_set_id);
1092 $rendering_engine = $this->other_template_engine_objects[$engine];
1093 }
de4d58cb 1094
de4d58cb 1095
d4c2aa24 1096 // now, need to copy over all the assigned variables
1097 // from $this to the rendering engine (YUCK! -- we need
1098 // to discourage template authors from creating
1099 // situations where engine changes occur)
1100 //
1101 $rendering_engine->clear_all_assign();
1102 $rendering_engine->assign($this->get_template_vars());
de4d58cb 1103
de4d58cb 1104
d4c2aa24 1105 // finally ready to go
1106 //
1107 return $rendering_engine;
de4d58cb 1108
d4c2aa24 1109 }
de4d58cb 1110
6806cbe5 1111 /**
d4c2aa24 1112 * Return all JavaScript files provided by the template.
6806cbe5 1113 *
1114 * All files found in the template set's "js" directory (and
d4c2aa24 1115 * that of its ancestors) with the extension ".js" are returned.
1116 *
1117 * @param boolean $full_path When FALSE, only the file names
1118 * are included in the return array;
1119 * otherwise, path information is
1120 * included (relative to SM_PATH)
1121 * (OPTIONAL; default only file names)
6806cbe5 1122 *
d4c2aa24 1123 * @return array The required file names/paths.
6806cbe5 1124 *
d4c2aa24 1125 */
1126 function get_javascript_includes($full_path=FALSE) {
de4d58cb 1127
6806cbe5 1128 // since any page from a parent template set
d4c2aa24 1129 // could end up being loaded, we have to load
1130 // all js files from ancestor template sets,
1131 // not just this set
1132 //
1133 //$directory = SM_PATH . $this->get_template_file_directory() . 'js';
1134 //$js_files = list_files($directory, '.js', !$full_path);
1135 //
1136 $js_files = $this->get_template_file_path('js/');
6806cbe5 1137
1138
d4c2aa24 1139 // parse out .js files only
1140 //
1141 $return_array = array();
1142 foreach ($js_files as $file) {
6806cbe5 1143
d4c2aa24 1144 if (substr($file, strlen($file) - 3) != '.js') continue;
de4d58cb 1145
d4c2aa24 1146 if ($full_path) {
1147 $return_array[] = $file;
1148 } else {
1149 $return_array[] = basename($file);
de4d58cb 1150 }
1151
1152 }
1153
d4c2aa24 1154 return $return_array;
de4d58cb 1155
1156 }
1157
1158 /**
6806cbe5 1159 * Return all alternate stylesheets provided by template.
d4c2aa24 1160 *
6806cbe5 1161 * All (non-empty) directories found in the template set's
1162 * "css/alternates" directory (and that of its ancestors)
e5d57179 1163 * are returned.
d4c2aa24 1164 *
1165 * Note that prettified names are constructed herein by
e5d57179 1166 * taking the directory name, changing underscores to spaces
1167 * and capitalizing each word in the resultant name.
de4d58cb 1168 *
1169 * @param boolean $full_path When FALSE, only the file names
1170 * are included in the return array;
1171 * otherwise, path information is
1172 * included (relative to SM_PATH)
1173 * (OPTIONAL; default only file names)
1174 *
d4c2aa24 1175 * @return array A list of the available alternate stylesheets,
6806cbe5 1176 * where the keys are the file names (formatted
1177 * according to $full_path) for the stylesheets,
1178 * and the values are the prettified version of
d4c2aa24 1179 * the file names for display to the user.
6806cbe5 1180 *
de4d58cb 1181 */
d4c2aa24 1182 function get_alternative_stylesheets($full_path=FALSE) {
de4d58cb 1183
d4c2aa24 1184 // since any page from a parent template set
1185 // could end up being loaded, we will load
6806cbe5 1186 // all alternate css files from ancestor
d4c2aa24 1187 // template sets, not just this set
1188 //
e5d57179 1189 $css_directories = $this->get_template_file_path('css/alternates/', TRUE, TRUE);
d4c2aa24 1190
1191
e5d57179 1192 // prettify names
d4c2aa24 1193 //
1194 $return_array = array();
e5d57179 1195 foreach ($css_directories as $directory) {
d4c2aa24 1196
d7917c67 1197 // CVS and SVN directories are not wanted
e5d57179 1198 //
d7917c67 1199 if ((strpos($directory, '/CVS') === strlen($directory) - 4)
1200 || (strpos($directory, '/.svn') === strlen($directory) - 5)) continue;
d4c2aa24 1201
e5d57179 1202 $pretty_name = ucwords(str_replace('_', ' ', basename($directory)));
d4c2aa24 1203
1204 if ($full_path) {
e5d57179 1205 $return_array[$directory] = $pretty_name;
d4c2aa24 1206 } else {
e5d57179 1207 $return_array[basename($directory)] = $pretty_name;
de4d58cb 1208 }
d4c2aa24 1209
de4d58cb 1210 }
1211
d4c2aa24 1212 return $return_array;
de4d58cb 1213
1214 }
1215
1216 /**
6806cbe5 1217 * Return all standard stylsheets provided by the template.
de4d58cb 1218 *
d4c2aa24 1219 * All files found in the template set's "css" directory (and
6806cbe5 1220 * that of its ancestors) with the extension ".css" except
d4c2aa24 1221 * "rtl.css" (which is dealt with separately) are returned.
de4d58cb 1222 *
1223 * @param boolean $full_path When FALSE, only the file names
1224 * are included in the return array;
1225 * otherwise, path information is
1226 * included (relative to SM_PATH)
1227 * (OPTIONAL; default only file names)
1228 *
1229 * @return array The required file names/paths.
1230 *
1231 */
1232 function get_stylesheets($full_path=FALSE) {
1233
6806cbe5 1234 // since any page from a parent template set
d4c2aa24 1235 // could end up being loaded, we have to load
1236 // all css files from ancestor template sets,
1237 // not just this set
1238 //
1239 //$directory = SM_PATH . $this->get_template_file_directory() . 'css';
1240 //$css_files = list_files($directory, '.css', !$full_path);
1241 //
1242 $css_files = $this->get_template_file_path('css/');
1243
de4d58cb 1244
6806cbe5 1245 // need to leave out "rtl.css"
d4c2aa24 1246 //
de4d58cb 1247 $return_array = array();
d4c2aa24 1248 foreach ($css_files as $file) {
de4d58cb 1249
d4c2aa24 1250 if (substr($file, strlen($file) - 4) != '.css') continue;
1251 if (strtolower(basename($file)) == 'rtl.css') continue;
de4d58cb 1252
d4c2aa24 1253 if ($full_path) {
1254 $return_array[] = $file;
1255 } else {
1256 $return_array[] = basename($file);
1257 }
de4d58cb 1258
1259 }
1260
c404d448 1261
1262 // return sheets for the current template set
6806cbe5 1263 // last so we can enable any custom overrides
1264 // of styles in ancestor sheets
c404d448 1265 //
1266 return array_reverse($return_array);
de4d58cb 1267
1268 }
1269
1270 /**
1271 * Generate links to all this template set's standard stylesheets
1272 *
6806cbe5 1273 * Subclasses can override this function if stylesheets are
de4d58cb 1274 * created differently for the template set's target output
1275 * interface.
1276 *
1277 * @return string The stylesheet links as they should be sent
1278 * to the browser.
1279 *
1280 */
1281 function fetch_standard_stylesheet_links()
1282 {
1283
1284 $sheets = $this->get_stylesheets(TRUE);
1285 return $this->fetch_external_stylesheet_links($sheets);
1286
1287 }
1288
1289 /**
6806cbe5 1290 * Push out any other stylesheet links as provided (for
de4d58cb 1291 * stylesheets not included with the current template set)
1292 *
6806cbe5 1293 * Subclasses can override this function if stylesheets are
de4d58cb 1294 * created differently for the template set's target output
1295 * interface.
1296 *
6806cbe5 1297 * @param mixed $sheets List of the desired stylesheets
de4d58cb 1298 * (file path to be used in stylesheet
6806cbe5 1299 * href attribute) to output (or single
de4d58cb 1300 * stylesheet file path).
6806cbe5 1301FIXME: We could make the incoming array more complex so it can
de4d58cb 1302 also contain the other parameters for create_css_link()
1303 such as $name, $alt, $mtype, and $xhtml_end
1304 But do we need to?
1305 *
1306 * @return string The stylesheet links as they should be sent
1307 * to the browser.
1308 *
1309 */
1310 function fetch_external_stylesheet_links($sheets)
1311 {
1312
1313 if (!is_array($sheets)) $sheets = array($sheets);
1314 $output = '';
1315
1316 foreach ($sheets as $sheet) {
1317 $output .= create_css_link($sheet);
1318 }
1319
1320 return $output;
1321
1322 }
1323
1324 /**
1325 * Send HTTP header(s) to browser.
1326 *
1327 * Subclasses can override this function if headers are
7ca9685a 1328 * managed differently in the engine's target output
de4d58cb 1329 * interface.
1330 *
6806cbe5 1331 * @param mixed $headers A list of (or a single) header
de4d58cb 1332 * text to be sent.
1333 *
1334 */
1335 function header($headers)
1336 {
1337
1338 if (!is_array($headers)) $headers = array($headers);
1339
1340 foreach ($headers as $header) {
7ca9685a 1341 $this->assign('header', $header);
1342 header($this->fetch('header.tpl'));
de4d58cb 1343 }
1344
1345 }
1346
1347 /**
6806cbe5 1348 * Generate a link to the right-to-left stylesheet for
d4c2aa24 1349 * this template set by getting the "rtl.css" file from
1350 * this template set, its parent (or grandparent, etc.)
6806cbe5 1351 * template set, the fall-back template set, or finally,
d4c2aa24 1352 * fall back to SquirrelMail's own "rtl.css" if need be.
de4d58cb 1353 *
6806cbe5 1354 * Subclasses can override this function if stylesheets are
de4d58cb 1355 * created differently for the template set's target output
1356 * interface.
1357 *
1358 * @return string The stylesheet link as it should be sent
1359 * to the browser.
1360 *
1361 */
1362 function fetch_right_to_left_stylesheet_link()
1363 {
1364
1365 // get right template file
1366 //
1367 $sheet = $this->get_template_file_path('css/rtl.css');
1368
1369 // fall back to SquirrelMail's own default stylesheet
1370 //
1371 if (empty($sheet)) {
1372 $sheet = SM_PATH . 'css/rtl.css';
1373 }
1374
1375 return create_css_link($sheet);
1376
1377 }
1378
1379 /**
1380 * Display the template
1381 *
1382 * @param string $file The template file to use
1383 *
1384 */
1385 function display($file)
1386 {
1387
1388 echo $this->fetch($file);
1389
1390 }
1391
1392 /**
1393 * Applies the template and returns the resultant content string.
1394 *
1395 * @param string $file The template file to use
1396 *
1397 * @return string The template contents after applying the given template
1398 *
1399 */
1400 function fetch($file) {
1401
1402 // get right template file
1403 //
1404 $template = $this->get_template_file_path($file);
1405
d4c2aa24 1406
6806cbe5 1407 // special case stylesheet.tpl falls back to SquirrelMail's
de4d58cb 1408 // own default stylesheet
1409 //
1410 if (empty($template) && $file == 'css/stylesheet.tpl') {
1411 $template = SM_PATH . 'css/default.css';
1412 }
1413
d4c2aa24 1414
de4d58cb 1415 if (empty($template)) {
1416
1417 trigger_error('The template "' . htmlspecialchars($file)
1418 . '" could not be fetched!', E_USER_ERROR);
1419
1420 } else {
1421
1422 $aPluginOutput = array();
0db60ced 1423 $aPluginOutput = concat_hook_function('template_construct_' . $file,
1424 $temp=array(&$aPluginOutput, &$this),
1425 TRUE);
de4d58cb 1426 $this->assign('plugin_output', $aPluginOutput);
1427
d4c2aa24 1428 //$output = $this->apply_template($template);
1429 $rendering_engine = $this->get_rendering_template_engine_object($file);
1430 $output = $rendering_engine->apply_template($template);
de4d58cb 1431
1432 // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
1433 // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
1434 // using this hook will probably be rejected by the
1435 // SquirrelMail team.
1436 //
d849b570 1437 do_hook('template_output', $output);
de4d58cb 1438
1439 return $output;
1440
1441 }
1442
1443 }
1444
1445 /**
1446 * Assigns values to template variables
1447 *
1448 * Note: this is an abstract method that must be implemented by subclass.
1449 *
1450 * @param array|string $tpl_var the template variable name(s)
1451 * @param mixed $value the value to assign
1452 *
1453 */
1454 function assign($tpl_var, $value = NULL) {
1455
1456 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign() method.', E_USER_ERROR);
1457
1458 }
1459
1460 /**
1461 * Assigns values to template variables by reference
1462 *
1463 * Note: this is an abstract method that must be implemented by subclass.
1464 *
1465 * @param string $tpl_var the template variable name
1466 * @param mixed $value the referenced value to assign
1467 *
1468 */
1469 function assign_by_ref($tpl_var, &$value) {
1470
1471 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the assign_by_ref() method.', E_USER_ERROR);
1472
1473 }
1474
d4c2aa24 1475 /**
1476 * Clears the values of all assigned varaiables.
1477 *
1478 */
1479 function clear_all_assign() {
1480
1481 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the clear_all_assign() method.', E_USER_ERROR);
1482
1483 }
1484
1485 /**
1486 * Returns assigned variable value(s).
1487 *
1488 * @param string $varname If given, the value of that variable
1489 * is returned, assuming it has been
1490 * previously assigned. If not specified
1491 * an array of all assigned variables is
1492 * returned. (optional)
1493 *
1494 * @return mixed Desired single variable value or list of all
1495 * assigned variable values.
1496 *
1497 */
1498 function get_template_vars($varname=NULL) {
1499
1500 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the get_template_vars() method.', E_USER_ERROR);
1501
1502 }
1503
de4d58cb 1504 /**
1505 * Appends values to template variables
1506 *
1507 * Note: this is an abstract method that must be implemented by subclass.
1508 *
1509 * @param array|string $tpl_var the template variable name(s)
1510 * @param mixed $value the value to append
1511 * @param boolean $merge when $value is given as an array,
1512 * this indicates whether or not that
1513 * array itself should be appended as
1514 * a new template variable value or if
1515 * that array's values should be merged
1516 * into the existing array of template
1517 * variable values
1518 *
1519 */
1520 function append($tpl_var, $value = NULL, $merge = FALSE) {
1521
1522 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append() method.', E_USER_ERROR);
1523
1524 }
1525
1526 /**
1527 * Appends values to template variables by reference
1528 *
1529 * Note: this is an abstract method that must be implemented by subclass.
1530 *
1531 * @param string $tpl_var the template variable name
1532 * @param mixed $value the referenced value to append
1533 * @param boolean $merge when $value is given as an array,
1534 * this indicates whether or not that
1535 * array itself should be appended as
1536 * a new template variable value or if
1537 * that array's values should be merged
1538 * into the existing array of template
1539 * variable values
1540 *
1541 */
1542 function append_by_ref($tpl_var, &$value, $merge = FALSE) {
1543
1544 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the append_by_ref() method.', E_USER_ERROR);
1545
1546 }
1547
1548 /**
1549 * Applys the template and generates final output destined
1550 * for the user's browser
1551 *
1552 * Note: this is an abstract method that must be implemented by subclass.
1553 *
1554 * @param string $filepath The full file path to the template to be applied
6806cbe5 1555 *
de4d58cb 1556 * @return string The output for the given template
1557 *
1558 */
1559 function apply_template($filepath) {
1560
1561 trigger_error('Template subclass (' . $this->template_engine . 'Template.class.php) needs to implement the apply_template() method.', E_USER_ERROR);
1562
1563 }
1564
1565}
1566