Solved recursion problem and increased efficiency of template_construct hook by addin...
[squirrelmail.git] / class / template / template.class.php
CommitLineData
94b47c30 1<?php
4b4abf93 2
94b47c30 3/**
4 * Copyright 2003, Paul James
94b47c30 5 *
6 * This file contains some methods from the Smarty templating engine version
7 * 2.5.0 by Monte Ohrt <monte@ispi.net> and Andrei Zmievski <andrei@php.net>.
8 *
94b47c30 9 * The SquirrelMail (Foowd) template implementation.
10 * Derived from the foowd template implementation and adapted
11 * for squirrelmail
47ccfad4 12 * @copyright &copy; 2005-2006 The SquirrelMail Project Team
4b4abf93 13 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
9f7d2fa9 14 * @version $Id$
e045ce66 15 * @package squirrelmail
94b47c30 16 */
17
18/**
19 * The SquirrelMail (Foowd) template class.
20 *
21 * Basic template class for capturing values and pluging them into a template.
22 * This class uses a similar API to Smarty.
23 *
24 * @author Paul James
4b4abf93 25 * @author Monte Ohrt <monte at ispi.net>
26 * @author Andrei Zmievski <andrei at php.net>
9f7d2fa9 27 * @package squirrelmail
94b47c30 28 */
29class Template
30{
31 /**
32 * The templates values array
33 *
34 * @var array
35 */
36 var $values = array();
37
38 /**
39 * The template directory to use
40 *
41 * @var string
42 */
49ea2d20 43 var $template_dir = '';
94b47c30 44
7186732c 45 /**
46 * The default template directory
47 *
48 * @var string
49 */
50 var $default_template_dir = 'templates/default/';
51
44452136 52 /**
53 * Template files provided by this template set
7bc3ddff 54 *
44452136 55 * @var array
56 */
57 var $templates_provided = array();
7bc3ddff 58
44452136 59 /**
60 * Javascript files required by the template
7bc3ddff 61 *
44452136 62 * @var array
63 */
64 var $required_js_files = array();
7bc3ddff 65
44452136 66 /**
67 * Javascript files provided by the template. If a JS file is required, but
7bc3ddff 68 * not provided, the js file by the same name will be included from the
44452136 69 * default template directory.
7bc3ddff 70 *
44452136 71 * @var array
72 */
73 var $provided_js_files = array();
7bc3ddff 74
3cecf1cd 75 /**
76 * Additional stylesheets provided by the template. This allows template
b28e7303 77 * authors to provide additional CSS sheets to templates while using the
78 * default template set stylesheet for other definitions.
3cecf1cd 79 */
7bc3ddff 80 var $additional_css_sheets = array();
81
4407b2e4 82 /**
83 * Constructor
84 *
85 * @param string $sTplDir where the template set is located
86 */
87 function Template($sTplDir) {
88 $this->template_dir = $sTplDir;
7bc3ddff 89
4407b2e4 90 // Pull in the tempalte config file
91 if (!file_exists($this->template_dir . 'template.php')) {
92 trigger_error('No template.php could be found in the requested template directory ("'.$this->template_dir.'")', E_USER_ERROR);
93 } else {
94 include ($this->template_dir . 'template.php');
95 $this->templates_provided = is_array($templates_provided) ? $templates_provided : array();
96 $this->required_js_files = is_array($required_js_files) ? $required_js_files : array();
97 $this->provided_js_files = is_array($provided_js_files) ? $provided_js_files: array();
98 $this->additional_css_sheets = is_array($additional_css_sheets) ? $additional_css_sheets : array();
99 }
94b47c30 100 }
101
102
103 /**
104 * Assigns values to template variables
105 *
106 * @param array|string $tpl_var the template variable name(s)
107 * @param mixed $value the value to assign
108 */
109 function assign($tpl_var, $value = NULL) {
110 if (is_array($tpl_var))
111 {
112 foreach ($tpl_var as $key => $val)
113 {
114 if ($key != '')
115 $this->values[$key] = $val;
116 }
117 }
118 else
119 {
120 if ($tpl_var != '')
121 $this->values[$tpl_var] = $value;
122 }
123 }
124
125 /**
126 * Assigns values to template variables by reference
127 *
128 * @param string $tpl_var the template variable name
129 * @param mixed $value the referenced value to assign
130 */
131 function assign_by_ref($tpl_var, &$value)
132 {
133 if ($tpl_var != '')
134 $this->values[$tpl_var] = &$value;
135 }
136
137 /**
138 * Appends values to template variables
139 *
140 * @param array|string $tpl_var the template variable name(s)
141 * @param mixed $value the value to append
b28e7303 142 * @param boolean $merge when $value is given as an array,
143 * this indicates whether or not that
144 * array itself should be appended as
145 * a new template variable value or if
146 * that array's values should be merged
147 * into the existing array of template
148 * variable values
94b47c30 149 */
150 function append($tpl_var, $value = NULL, $merge = FALSE)
151 {
152 if (is_array($tpl_var))
153 {
b28e7303 154 //FIXME: $tpl_var is supposed to be a list of template var names,
155 // so we should be looking at the values NOT the keys!
94b47c30 156 foreach ($tpl_var as $_key => $_val)
157 {
158 if ($_key != '')
159 {
160 if(isset($this->values[$_key]) && !is_array($this->values[$_key]))
161 settype($this->values[$_key],'array');
162
b28e7303 163 //FIXME: we should be iterating the $value array here not the values of the
164 // list of template variable names! I think this is totally broken
165 // This might just be a matter of needing to clarify the method's API;
166 // values may have been meant to be passed in $tpl_var in the case that
167 // $tpl_var is an array. Ugly and non-intuitive.
168 // PROPOSAL: API should be as such:
169 // if (is_string($tpl_var)) then $values are added/merged as already done
170 // if (is_array($tpl_var)) then $values is required to be an array whose
171 // keys must match up with $tpl_var keys and
172 // whose values are then what is added to
173 // each template variable value (array or
174 // strings, doesn't matter)
94b47c30 175 if($merge && is_array($_val))
176 {
177 foreach($_val as $_mkey => $_mval)
178 $this->values[$_key][$_mkey] = $_mval;
179 }
180 else
181 $this->values[$_key][] = $_val;
182 }
183 }
184 }
185 else
186 {
187 if ($tpl_var != '' && isset($value))
188 {
189 if(isset($this->values[$tpl_var]) && !is_array($this->values[$tpl_var]))
190 settype($this->values[$tpl_var],'array');
191
192 if($merge && is_array($value))
193 {
194 foreach($value as $_mkey => $_mval)
195 $this->values[$tpl_var][$_mkey] = $_mval;
196 }
197 else
198 $this->values[$tpl_var][] = $value;
199 }
200 }
201 }
202
203 /**
204 * Appends values to template variables by reference
205 *
206 * @param string $tpl_var the template variable name
207 * @param mixed $value the referenced value to append
b28e7303 208 * @param boolean $merge when $value is given as an array,
209 * this indicates whether or not that
210 * array itself should be appended as
211 * a new template variable value or if
212 * that array's values should be merged
213 * into the existing array of template
214 * variable values
94b47c30 215 */
216 function append_by_ref($tpl_var, &$value, $merge = FALSE)
217 {
218 if ($tpl_var != '' && isset($value))
219 {
220 if(!@is_array($this->values[$tpl_var]))
221 settype($this->values[$tpl_var],'array');
222
223 if ($merge && is_array($value))
224 {
225 foreach($value as $_key => $_val)
226 $this->values[$tpl_var][$_key] = &$value[$_key];
227 }
228 else
229 $this->values[$tpl_var][] = &$value;
230 }
231 }
232
7186732c 233
234 /**
235 *
236 * Return the relative template directory path for this template set.
237 *
238 * @return string The relative path to the template directory based
239 * from the main SquirrelMail directory (SM_PATH).
240 *
241 */
242 function get_template_file_directory() {
243
244//FIXME: temporarily parse off SM_PATH from the template dir class attribute until we can change the entire template subsystem such that the template dir is derived internally in this class from the template ID/name/attributes
245return substr($this->template_dir, strlen(SM_PATH));
246 return $this->template_dir;
247
4407b2e4 248 }
94b47c30 249
7186732c 250
251 /**
252 *
253 * Return the relative template directory path for the DEFAULT template set.
254 *
255 * @return string The relative path to the default template directory based
256 * from the main SquirrelMail directory (SM_PATH).
257 *
258 */
259 function get_default_template_file_directory() {
260
261 return $this->default_template_dir;
262
263 }
264
265
266 /**
267 *
268 * Find the right template file.
269 *
0606cc10 270 * Templates are expected to be found in the template set directory,
271 * for example:
272 * SM_PATH/templates/<template name>/
273 * or, in the case of plugin templates, in a plugin directory in the
274 * template set directory, for example:
275 * SM_PATH/templates/<template name>/plugins/<plugin name>/
276 * *OR* in a template directory in the plugin as a fallback, for example:
277 * SM_PATH/plugins/<plugin name>/templates/<template name>/
278 * If the correct file is not found for the current template set, a
7186732c 279 * default template is loaded, which is expected to be found in the
0606cc10 280 * default template directory, for example:
281 * SM_PATH/templates/default/
282 * or for plugins, in a plugin directory in the default template set,
283 * for example:
284 * SM_PATH/templates/default/plugins/<plugin name>/
285 * *OR* in a default template directory in the plugin as a fallback,
286 * for example:
287 * SM_PATH/plugins/<plugin name>/templates/default/
288 *
289 * Plugin authors must note that the $filename MUST be prefaced
290 * with "plugins/<plugin name>/" in order to correctly resolve the
291 * template file.
7186732c 292 *
293 * @param string $filename The name of the template file,
294 * possibly prefaced with
295 * "plugins/<plugin name>/"
296 * indicating that it is a plugin
297 * template.
298 *
299 * @return string The full path to the template file; if
300 * not found, an empty string. The caller
301 * is responsible for throwing erros or
302 * other actions if template file is not found.
303 *
304 */
305 function get_template_file_path($filename) {
306
307 // is the template found in the normal template directory?
308 //
309 $filepath = SM_PATH . $this->get_template_file_directory() . $filename;
310 if (!file_exists($filepath)) {
311
312 // no, so now we have to get the default template...
313 // however, in the case of a plugin template, let's
314 // give one more try to find the right template as
315 // provided by the plugin
316 //
317 if (strpos($filename, 'plugins/') === 0) {
318
319 $plugin_name = substr($filename, 8, strpos($filename, '/', 8) - 8);
320 $filepath = SM_PATH . 'plugins/' . $plugin_name . '/'
321 . $this->get_template_file_directory()
322 . substr($filename, strlen($plugin_name) + 9);
323
0606cc10 324 // no go, we have to get the default template,
325 // first try the default SM template
7186732c 326 //
327 if (!file_exists($filepath)) {
328
0606cc10 329 $filepath = SM_PATH
7186732c 330 . $this->get_default_template_file_directory()
0606cc10 331 . $filename;
7186732c 332
0606cc10 333 // still no luck? get default template from the plugin
7186732c 334 //
335 if (!file_exists($filepath)) {
0606cc10 336
337 $filepath = SM_PATH . 'plugins/' . $plugin_name . '/'
338 . $this->get_default_template_file_directory()
339 . substr($filename, strlen($plugin_name) + 9);
340
341 // no dice whatsoever, return empty string
342 //
343 if (!file_exists($filepath)) {
344 $filepath = '';
345 }
346
7186732c 347 }
348
349 }
350
351
352 // get default template for non-plugin templates
353 //
354 } else {
355
356 $filepath = SM_PATH . $this->get_default_template_file_directory()
357 . $filename;
358
359 // no dice whatsoever, return empty string
360 //
361 if (!file_exists($filepath)) {
362 $filepath = '';
363 }
364
365 }
366
367 }
368
369 return $filepath;
370
371 }
372
373
374 /**
375 * Display the template
376 *
377 * @param string $file The template file to use
378 */
379 function display($file)
380 {
7186732c 381
bc079f70 382 // get right template file
383 //
7186732c 384 $template = $this->get_template_file_path($file);
385 if (empty($template)) {
bc079f70 386
387 trigger_error('The template "' . htmlspecialchars($file)
388 . '" could not be displayed!', E_USER_ERROR);
389
7186732c 390 } else {
bc079f70 391
392 $aPluginOutput = array();
43751c9d 393 $aPluginOutput = concat_hook_function('template_construct_' . $file,
394 array($aPluginOutput, $this));
bc079f70 395 $this->assign('plugin_output', $aPluginOutput);
396
397 // pull in our config file ($t? let's try to be more verbose please :-) )
398 //
399 $t = &$this->values; // place values array directly in scope
400
7186732c 401 ob_start();
402 include($template);
bc079f70 403
404 // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
405 // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
406 // using this hook will probably be rejected by the
407 // SquirrelMail team.
408 //
409 // Anyone hooking in here that wants to manipulate the output
410 // buffer has to get the buffer, clean it and then echo the
411 // new buffer like this:
412 // $buffer = ob_get_contents(); ob_clean(); .... echo $new_buffer;
413 //
414 // Don't need to pass buffer contents ourselves
415 // do_hook_function('template_output', array(ob_get_contents()));
416 //
417 do_hook('template_output');
418
7186732c 419 ob_end_flush();
bc079f70 420
7186732c 421 }
422 }
423
424 /**
425 * Return the results of applying a template.
426 *
427 * @param string $file The template file to use
428 * @return string A string of the results
429 */
430 function fetch($file) {
7186732c 431
bc079f70 432 // get right template file
433 //
7186732c 434 $template = $this->get_template_file_path($file);
fc03e8b1 435 if (empty($template)) {
bc079f70 436
437 trigger_error('The template "' . htmlspecialchars($file)
438 . '" could not be fetched!', E_USER_ERROR);
439
7186732c 440 } else {
bc079f70 441
442 $aPluginOutput = array();
43751c9d 443 $aPluginOutput = concat_hook_function('template_construct_' . $file,
444 array($aPluginOutput, $this));
bc079f70 445 $this->assign('plugin_output', $aPluginOutput);
446
447 // pull in our config file ($t? let's try to be more verbose please :-) )
448 //
449 $t = &$this->values; // place values array directly in scope
450
7186732c 451 ob_start();
452 include($template);
bc079f70 453
454 // CAUTION: USE OF THIS HOOK IS HIGHLY DISCOURAGED AND CAN
455 // RESULT IN NOTICABLE PERFORMANCE DEGREDATION. Plugins
456 // using this hook will probably be rejected by the
457 // SquirrelMail team.
458 //
459 // Anyone hooking in here that wants to manipulate the output
460 // buffer has to get the buffer, clean it and then echo the
461 // new buffer like this:
462 // $buffer = ob_get_contents(); ob_clean(); .... echo $new_buffer;
463 //
464 // Don't need to pass buffer contents ourselves
465 // do_hook_function('template_output', array(ob_get_contents()));
466 //
467 do_hook('template_output');
468
7186732c 469 $contents = ob_get_contents();
470 ob_end_clean();
471 return $contents;
bc079f70 472
7186732c 473 }
4407b2e4 474 }
94b47c30 475
44452136 476 /**
7bc3ddff 477 * Return paths to the required javascript files. Used when generating page
44452136 478 * header.
7bc3ddff 479 *
44452136 480 * @return array $paths
481 */
482 function getJavascriptIncludes () {
483 $paths = array();
484 foreach ($this->required_js_files as $file) {
485 if (in_array($file, $this->provided_js_files))
486 $paths[] = './'.$this->template_dir.'js/'.basename($file);
487 else $paths[] = SM_PATH .'templates/default/js/'.basename($file);
488 }
7bc3ddff 489
44452136 490 return $paths;
491 }
3cecf1cd 492
493 /**
494 * Return any additional stylsheets provided by the template. Used when
495 * generating page headers.
7bc3ddff 496 *
3cecf1cd 497 * @return array $paths
498 */
499 function getAdditionalStyleSheets () {
500 $paths = array();
501 foreach ($this->additional_css_sheets as $css) {
502 $css = basename($css);
503 if (strtolower($css) == 'stylesheet.tpl') {
504 continue;
505 }
506 $paths[] = $css;
507 }
508 return $paths;
509 }
94b47c30 510}