source: trunk/forum/includes/template.php

Last change on this file was 702, checked in by george, 15 years ago
  • Upraveno: Aktualizace fóra.
File size: 18.4 KB
Line 
1<?php
2/**
3*
4* @package phpBB3
5* @version $Id$
6* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
7* @license http://opensource.org/licenses/gpl-license.php GNU Public License
8*
9*/
10
11/**
12* @ignore
13*/
14if (!defined('IN_PHPBB'))
15{
16 exit;
17}
18
19/**
20* Base Template class.
21* @package phpBB3
22*/
23class template
24{
25 /** variable that holds all the data we'll be substituting into
26 * the compiled templates. Takes form:
27 * --> $this->_tpldata[block][iteration#][child][iteration#][child2][iteration#][variablename] == value
28 * if it's a root-level variable, it'll be like this:
29 * --> $this->_tpldata[.][0][varname] == value
30 */
31 var $_tpldata = array('.' => array(0 => array()));
32 var $_rootref;
33
34 // Root dir and hash of filenames for each template handle.
35 var $root = '';
36 var $cachepath = '';
37 var $files = array();
38 var $filename = array();
39 var $files_inherit = array();
40 var $files_template = array();
41 var $inherit_root = '';
42 var $orig_tpl_storedb;
43 var $orig_tpl_inherits_id;
44
45 // this will hash handle names to the compiled/uncompiled code for that handle.
46 var $compiled_code = array();
47
48 /**
49 * Set template location
50 * @access public
51 */
52 function set_template()
53 {
54 global $phpbb_root_path, $user;
55
56 if (file_exists($phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template'))
57 {
58 $this->root = $phpbb_root_path . 'styles/' . $user->theme['template_path'] . '/template';
59 $this->cachepath = $phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $user->theme['template_path']) . '_';
60
61 if ($this->orig_tpl_storedb === null)
62 {
63 $this->orig_tpl_storedb = $user->theme['template_storedb'];
64 }
65
66 if ($this->orig_tpl_inherits_id === null)
67 {
68 $this->orig_tpl_inherits_id = $user->theme['template_inherits_id'];
69 }
70
71 $user->theme['template_storedb'] = $this->orig_tpl_storedb;
72 $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id;
73
74 if ($user->theme['template_inherits_id'])
75 {
76 $this->inherit_root = $phpbb_root_path . 'styles/' . $user->theme['template_inherit_path'] . '/template';
77 }
78 }
79 else
80 {
81 trigger_error('Template path could not be found: styles/' . $user->theme['template_path'] . '/template', E_USER_ERROR);
82 }
83
84 $this->_rootref = &$this->_tpldata['.'][0];
85
86 return true;
87 }
88
89 /**
90 * Set custom template location (able to use directory outside of phpBB)
91 * @access public
92 */
93 function set_custom_template($template_path, $template_name, $fallback_template_path = false)
94 {
95 global $phpbb_root_path, $user;
96
97 // Make sure $template_path has no ending slash
98 if (substr($template_path, -1) == '/')
99 {
100 $template_path = substr($template_path, 0, -1);
101 }
102
103 $this->root = $template_path;
104 $this->cachepath = $phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_';
105
106 if ($fallback_template_path !== false)
107 {
108 if (substr($fallback_template_path, -1) == '/')
109 {
110 $fallback_template_path = substr($fallback_template_path, 0, -1);
111 }
112
113 $this->inherit_root = $fallback_template_path;
114 $this->orig_tpl_inherits_id = true;
115 }
116 else
117 {
118 $this->orig_tpl_inherits_id = false;
119 }
120
121 // the database does not store the path or name of a custom template
122 // so there is no way we can properly store custom templates there
123 $this->orig_tpl_storedb = false;
124
125 $this->_rootref = &$this->_tpldata['.'][0];
126
127 return true;
128 }
129
130 /**
131 * Sets the template filenames for handles. $filename_array
132 * should be a hash of handle => filename pairs.
133 * @access public
134 */
135 function set_filenames($filename_array)
136 {
137 if (!is_array($filename_array))
138 {
139 return false;
140 }
141 foreach ($filename_array as $handle => $filename)
142 {
143 if (empty($filename))
144 {
145 trigger_error("template->set_filenames: Empty filename specified for $handle", E_USER_ERROR);
146 }
147
148 $this->filename[$handle] = $filename;
149 $this->files[$handle] = $this->root . '/' . $filename;
150
151 if ($this->inherit_root)
152 {
153 $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename;
154 }
155 }
156
157 return true;
158 }
159
160 /**
161 * Destroy template data set
162 * @access public
163 */
164 function destroy()
165 {
166 $this->_tpldata = array('.' => array(0 => array()));
167 $this->_rootref = &$this->_tpldata['.'][0];
168 }
169
170 /**
171 * Reset/empty complete block
172 * @access public
173 */
174 function destroy_block_vars($blockname)
175 {
176 if (strpos($blockname, '.') !== false)
177 {
178 // Nested block.
179 $blocks = explode('.', $blockname);
180 $blockcount = sizeof($blocks) - 1;
181
182 $str = &$this->_tpldata;
183 for ($i = 0; $i < $blockcount; $i++)
184 {
185 $str = &$str[$blocks[$i]];
186 $str = &$str[sizeof($str) - 1];
187 }
188
189 unset($str[$blocks[$blockcount]]);
190 }
191 else
192 {
193 // Top-level block.
194 unset($this->_tpldata[$blockname]);
195 }
196
197 return true;
198 }
199
200 /**
201 * Display handle
202 * @access public
203 */
204 function display($handle, $include_once = true)
205 {
206 global $user, $phpbb_hook;
207
208 if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $include_once))
209 {
210 if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__)))
211 {
212 return $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__));
213 }
214 }
215
216 if (defined('IN_ERROR_HANDLER'))
217 {
218 if ((E_NOTICE & error_reporting()) == E_NOTICE)
219 {
220 error_reporting(error_reporting() ^ E_NOTICE);
221 }
222 }
223
224 if ($filename = $this->_tpl_load($handle))
225 {
226 ($include_once) ? include_once($filename) : include($filename);
227 }
228 else
229 {
230 eval(' ?>' . $this->compiled_code[$handle] . '<?php ');
231 }
232
233 return true;
234 }
235
236 /**
237 * Display the handle and assign the output to a template variable or return the compiled result.
238 * @access public
239 */
240 function assign_display($handle, $template_var = '', $return_content = true, $include_once = false)
241 {
242 ob_start();
243 $this->display($handle, $include_once);
244 $contents = ob_get_clean();
245
246 if ($return_content)
247 {
248 return $contents;
249 }
250
251 $this->assign_var($template_var, $contents);
252
253 return true;
254 }
255
256 /**
257 * Load a compiled template if possible, if not, recompile it
258 * @access private
259 */
260 function _tpl_load(&$handle)
261 {
262 global $user, $phpEx, $config;
263
264 if (!isset($this->filename[$handle]))
265 {
266 trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR);
267 }
268
269 // reload these settings to have the values they had when this object was initialised
270 // using set_template or set_custom_template, they might otherwise have been overwritten
271 // by other template class instances in between.
272 $user->theme['template_storedb'] = $this->orig_tpl_storedb;
273 $user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id;
274
275 $filename = $this->cachepath . str_replace('/', '.', $this->filename[$handle]) . '.' . $phpEx;
276 $this->files_template[$handle] = (isset($user->theme['template_id'])) ? $user->theme['template_id'] : 0;
277
278 $recompile = false;
279 if (!file_exists($filename) || @filesize($filename) === 0)
280 {
281 $recompile = true;
282 }
283 else if ($config['load_tplcompile'])
284 {
285 // No way around it: we need to check inheritance here
286 if ($user->theme['template_inherits_id'] && !file_exists($this->files[$handle]))
287 {
288 $this->files[$handle] = $this->files_inherit[$handle];
289 $this->files_template[$handle] = $user->theme['template_inherits_id'];
290 }
291 $recompile = (@filemtime($filename) < filemtime($this->files[$handle])) ? true : false;
292 }
293
294 // Recompile page if the original template is newer, otherwise load the compiled version
295 if (!$recompile)
296 {
297 return $filename;
298 }
299
300 global $db, $phpbb_root_path;
301
302 if (!class_exists('template_compile'))
303 {
304 include($phpbb_root_path . 'includes/functions_template.' . $phpEx);
305 }
306
307 // Inheritance - we point to another template file for this one. Equality is also used for store_db
308 if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($this->files[$handle]))
309 {
310 $this->files[$handle] = $this->files_inherit[$handle];
311 $this->files_template[$handle] = $user->theme['template_inherits_id'];
312 }
313
314 $compile = new template_compile($this);
315
316 // If we don't have a file assigned to this handle, die.
317 if (!isset($this->files[$handle]))
318 {
319 trigger_error("template->_tpl_load(): No file specified for handle $handle", E_USER_ERROR);
320 }
321
322 // Just compile if no user object is present (happens within the installer)
323 if (!$user)
324 {
325 $compile->_tpl_load_file($handle);
326 return false;
327 }
328
329 if (isset($user->theme['template_storedb']) && $user->theme['template_storedb'])
330 {
331 $rows = array();
332 $ids = array();
333 // Inheritance
334 if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'])
335 {
336 $ids[] = $user->theme['template_inherits_id'];
337 }
338 $ids[] = $user->theme['template_id'];
339
340 foreach ($ids as $id)
341 {
342 $sql = 'SELECT *
343 FROM ' . STYLES_TEMPLATE_DATA_TABLE . '
344 WHERE template_id = ' . $id . "
345 AND (template_filename = '" . $db->sql_escape($this->filename[$handle]) . "'
346 OR template_included " . $db->sql_like_expression($db->any_char . $this->filename[$handle] . ':' . $db->any_char) . ')';
347
348 $result = $db->sql_query($sql);
349 while ($row = $db->sql_fetchrow($result))
350 {
351 $rows[$row['template_filename']] = $row;
352 }
353 $db->sql_freeresult($result);
354 }
355
356 if (sizeof($rows))
357 {
358 foreach ($rows as $row)
359 {
360 $file = $this->root . '/' . $row['template_filename'];
361 $force_reload = false;
362 if ($row['template_id'] != $user->theme['template_id'])
363 {
364 // make sure that we are not overlooking a file not in the db yet
365 if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file))
366 {
367 $file = $this->inherit_root . '/' . $row['template_filename'];
368 $this->files[$row['template_filename']] = $file;
369 $this->files_inherit[$row['template_filename']] = $file;
370 $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];
371 }
372 else if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'])
373 {
374 // Ok, we have a situation. There is a file in the subtemplate, but nothing in the DB. We have to fix that.
375 $force_reload = true;
376 $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];
377 }
378 }
379 else
380 {
381 $this->files_template[$row['template_filename']] = $user->theme['template_id'];
382 }
383
384 if ($force_reload || $row['template_mtime'] < filemtime($file))
385 {
386 if ($row['template_filename'] == $this->filename[$handle])
387 {
388 $compile->_tpl_load_file($handle, true);
389 }
390 else
391 {
392 $this->files[$row['template_filename']] = $file;
393 $this->filename[$row['template_filename']] = $row['template_filename'];
394 $compile->_tpl_load_file($row['template_filename'], true);
395 unset($this->compiled_code[$row['template_filename']]);
396 unset($this->files[$row['template_filename']]);
397 unset($this->filename[$row['template_filename']]);
398 }
399 }
400
401 if ($row['template_filename'] == $this->filename[$handle])
402 {
403 $this->compiled_code[$handle] = $compile->compile(trim($row['template_data']));
404 $compile->compile_write($handle, $this->compiled_code[$handle]);
405 }
406 else
407 {
408 // Only bother compiling if it doesn't already exist
409 if (!file_exists($this->cachepath . str_replace('/', '.', $row['template_filename']) . '.' . $phpEx))
410 {
411 $this->filename[$row['template_filename']] = $row['template_filename'];
412 $compile->compile_write($row['template_filename'], $compile->compile(trim($row['template_data'])));
413 unset($this->filename[$row['template_filename']]);
414 }
415 }
416 }
417 }
418 else
419 {
420 $file = $this->root . '/' . $row['template_filename'];
421
422 if (isset($user->theme['template_inherits_id']) && $user->theme['template_inherits_id'] && !file_exists($file))
423 {
424 $file = $this->inherit_root . '/' . $row['template_filename'];
425 $this->files[$row['template_filename']] = $file;
426 $this->files_inherit[$row['template_filename']] = $file;
427 $this->files_template[$row['template_filename']] = $user->theme['template_inherits_id'];
428 }
429 // Try to load from filesystem and instruct to insert into the styles table...
430 $compile->_tpl_load_file($handle, true);
431 return false;
432 }
433
434 return false;
435 }
436
437 $compile->_tpl_load_file($handle);
438 return false;
439 }
440
441 /**
442 * Assign key variable pairs from an array
443 * @access public
444 */
445 function assign_vars($vararray)
446 {
447 foreach ($vararray as $key => $val)
448 {
449 $this->_rootref[$key] = $val;
450 }
451
452 return true;
453 }
454
455 /**
456 * Assign a single variable to a single key
457 * @access public
458 */
459 function assign_var($varname, $varval)
460 {
461 $this->_rootref[$varname] = $varval;
462
463 return true;
464 }
465
466 /**
467 * Assign key variable pairs from an array to a specified block
468 * @access public
469 */
470 function assign_block_vars($blockname, $vararray)
471 {
472 if (strpos($blockname, '.') !== false)
473 {
474 // Nested block.
475 $blocks = explode('.', $blockname);
476 $blockcount = sizeof($blocks) - 1;
477
478 $str = &$this->_tpldata;
479 for ($i = 0; $i < $blockcount; $i++)
480 {
481 $str = &$str[$blocks[$i]];
482 $str = &$str[sizeof($str) - 1];
483 }
484
485 $s_row_count = isset($str[$blocks[$blockcount]]) ? sizeof($str[$blocks[$blockcount]]) : 0;
486 $vararray['S_ROW_COUNT'] = $s_row_count;
487
488 // Assign S_FIRST_ROW
489 if (!$s_row_count)
490 {
491 $vararray['S_FIRST_ROW'] = true;
492 }
493
494 // Now the tricky part, we always assign S_LAST_ROW and remove the entry before
495 // This is much more clever than going through the complete template data on display (phew)
496 $vararray['S_LAST_ROW'] = true;
497 if ($s_row_count > 0)
498 {
499 unset($str[$blocks[$blockcount]][($s_row_count - 1)]['S_LAST_ROW']);
500 }
501
502 // Now we add the block that we're actually assigning to.
503 // We're adding a new iteration to this block with the given
504 // variable assignments.
505 $str[$blocks[$blockcount]][] = $vararray;
506 }
507 else
508 {
509 // Top-level block.
510 $s_row_count = (isset($this->_tpldata[$blockname])) ? sizeof($this->_tpldata[$blockname]) : 0;
511 $vararray['S_ROW_COUNT'] = $s_row_count;
512
513 // Assign S_FIRST_ROW
514 if (!$s_row_count)
515 {
516 $vararray['S_FIRST_ROW'] = true;
517 }
518
519 // We always assign S_LAST_ROW and remove the entry before
520 $vararray['S_LAST_ROW'] = true;
521 if ($s_row_count > 0)
522 {
523 unset($this->_tpldata[$blockname][($s_row_count - 1)]['S_LAST_ROW']);
524 }
525
526 // Add a new iteration to this block with the variable assignments we were given.
527 $this->_tpldata[$blockname][] = $vararray;
528 }
529
530 return true;
531 }
532
533 /**
534 * Change already assigned key variable pair (one-dimensional - single loop entry)
535 *
536 * An example of how to use this function:
537 * {@example alter_block_array.php}
538 *
539 * @param string $blockname the blockname, for example 'loop'
540 * @param array $vararray the var array to insert/add or merge
541 * @param mixed $key Key to search for
542 *
543 * array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position]
544 *
545 * int: Position [the position to change or insert at directly given]
546 *
547 * If key is false the position is set to 0
548 * If key is true the position is set to the last entry
549 *
550 * @param string $mode Mode to execute (valid modes are 'insert' and 'change')
551 *
552 * If insert, the vararray is inserted at the given position (position counting from zero).
553 * If change, the current block gets merged with the vararray (resulting in new key/value pairs be added and existing keys be replaced by the new value).
554 *
555 * Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array)
556 * and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars)
557 *
558 * @return bool false on error, true on success
559 * @access public
560 */
561 function alter_block_array($blockname, $vararray, $key = false, $mode = 'insert')
562 {
563 if (strpos($blockname, '.') !== false)
564 {
565 // Nested blocks are not supported
566 return false;
567 }
568
569 // Change key to zero (change first position) if false and to last position if true
570 if ($key === false || $key === true)
571 {
572 $key = ($key === false) ? 0 : sizeof($this->_tpldata[$blockname]);
573 }
574
575 // Get correct position if array given
576 if (is_array($key))
577 {
578 // Search array to get correct position
579 list($search_key, $search_value) = @each($key);
580
581 $key = NULL;
582 foreach ($this->_tpldata[$blockname] as $i => $val_ary)
583 {
584 if ($val_ary[$search_key] === $search_value)
585 {
586 $key = $i;
587 break;
588 }
589 }
590
591 // key/value pair not found
592 if ($key === NULL)
593 {
594 return false;
595 }
596 }
597
598 // Insert Block
599 if ($mode == 'insert')
600 {
601 // Make sure we are not exceeding the last iteration
602 if ($key >= sizeof($this->_tpldata[$blockname]))
603 {
604 $key = sizeof($this->_tpldata[$blockname]);
605 unset($this->_tpldata[$blockname][($key - 1)]['S_LAST_ROW']);
606 $vararray['S_LAST_ROW'] = true;
607 }
608 else if ($key === 0)
609 {
610 unset($this->_tpldata[$blockname][0]['S_FIRST_ROW']);
611 $vararray['S_FIRST_ROW'] = true;
612 }
613
614 // Re-position template blocks
615 for ($i = sizeof($this->_tpldata[$blockname]); $i > $key; $i--)
616 {
617 $this->_tpldata[$blockname][$i] = $this->_tpldata[$blockname][$i-1];
618 $this->_tpldata[$blockname][$i]['S_ROW_COUNT'] = $i;
619 }
620
621 // Insert vararray at given position
622 $vararray['S_ROW_COUNT'] = $key;
623 $this->_tpldata[$blockname][$key] = $vararray;
624
625 return true;
626 }
627
628 // Which block to change?
629 if ($mode == 'change')
630 {
631 if ($key == sizeof($this->_tpldata[$blockname]))
632 {
633 $key--;
634 }
635
636 $this->_tpldata[$blockname][$key] = array_merge($this->_tpldata[$blockname][$key], $vararray);
637 return true;
638 }
639
640 return false;
641 }
642
643 /**
644 * Include a separate template
645 * @access private
646 */
647 function _tpl_include($filename, $include = true)
648 {
649 $handle = $filename;
650 $this->filename[$handle] = $filename;
651 $this->files[$handle] = $this->root . '/' . $filename;
652 if ($this->inherit_root)
653 {
654 $this->files_inherit[$handle] = $this->inherit_root . '/' . $filename;
655 }
656
657 $filename = $this->_tpl_load($handle);
658
659 if ($include)
660 {
661 global $user;
662
663 if ($filename)
664 {
665 include($filename);
666 return;
667 }
668 eval(' ?>' . $this->compiled_code[$handle] . '<?php ');
669 }
670 }
671
672 /**
673 * Include a php-file
674 * @access private
675 */
676 function _php_include($filename)
677 {
678 global $phpbb_root_path;
679
680 $file = $phpbb_root_path . $filename;
681
682 if (!file_exists($file))
683 {
684 // trigger_error cannot be used here, as the output already started
685 echo 'template->_php_include(): File ' . htmlspecialchars($file) . ' does not exist or is empty';
686 return;
687 }
688 include($file);
689 }
690}
691
692?>
Note: See TracBrowser for help on using the repository browser.