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