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