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