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 © 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 | */ |
29 | class 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 |
245 | return 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 | } |