aboutsummaryrefslogtreecommitdiffstats
path: root/phpBB/includes/template/template.php
blob: 2f360e4df452a69a9e9dc408251ca8707019963e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
<?php
/**
*
* @package phpBB3
* @copyright (c) 2005 phpBB Group, sections (c) 2001 ispi of Lincoln Inc
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
	exit;
}

/**
* @todo
* IMG_ for image substitution?
* {IMG_[key]:[alt]:[type]}
* {IMG_ICON_CONTACT:CONTACT:full} -> $user->img('icon_contact', 'CONTACT', 'full');
*
* More in-depth...
* yadayada
*/

/**
* Base Template class.
* @package phpBB3
*/
class phpbb_template
{
	/**
	* @var phpbb_template_context Template context.
	* Stores template data used during template rendering.
	*/
	private $context;

	/**
	* @var string Path of the cache directory for the template
	*/
	public $cachepath = '';

	public $orig_tpl_inherits_id;

	/**
	* @var string phpBB root path
	*/
	private $phpbb_root_path;

	/**
	* @var phpEx PHP file extension
	*/
	private $phpEx;

	/**
	* @var phpbb_config phpBB config instance
	*/
	private $config;

	/**
	* @var user current user
	*/
	private $user;

	/**
	* @var locator template locator
	*/
	private $locator;

	/**
	* Constructor.
	*
	* @param string $phpbb_root_path phpBB root path
	* @param user $user current user
	*/
	public function __construct($phpbb_root_path, $phpEx, $config, $user)
	{
		$this->phpbb_root_path = $phpbb_root_path;
		$this->phpEx = $phpEx;
		$this->config = $config;
		$this->user = $user;
		$this->locator = new phpbb_template_locator($phpbb_root_path, $user);
	}

	/**
	* Set template location
	*/
	public function set_template()
	{
		$template_path = $style_name = $this->user->theme['template_path'];
		$this->locator->set_template_path($style_name);

		if (file_exists($this->phpbb_root_path . 'styles/' . $template_path . '/template'))
		{
			$this->cachepath = $this->phpbb_root_path . 'cache/tpl_' . str_replace('_', '-', $template_path) . '_';
		}
		else
		{
			trigger_error('Template path could not be found: styles/' . $template_path . '/template', E_USER_ERROR);
		}

		$this->context = new phpbb_template_context();

		return true;
	}

	/**
	* Set custom template location (able to use directory outside of phpBB).
	*
	* Note: Templates are still compiled to phpBB's cache directory.
	*
	* @param string $template_path Path to template directory
	* @param string $template_name Name of template
	* @param string $fallback_template_path Path to fallback template
	*/
	public function set_custom_template($template_path, $style_name, $fallback_template_path = false)
	{
		$this->locator->set_custom_template($template_path, $style_name, $fallback_template_path);
		$template_name = $style_name;

		$this->cachepath = $this->phpbb_root_path . 'cache/ctpl_' . str_replace('_', '-', $template_name) . '_';

		$this->context = new phpbb_template_context();

		return true;
	}

	/**
	* Sets the template filenames for handles. $filename_array
	* should be a hash of handle => filename pairs.
	* @param array $filname_array Should be a hash of handle => filename pairs.
	*/
	public function set_filenames(array $filename_array)
	{
		$this->locator->set_filenames($filename_array);

		return true;
	}

	/**
	* Clears all variables and blocks assigned to this template.
	*/
	public function destroy()
	{
		$this->context->clear();
	}

	/**
	* Reset/empty complete block
	* @param string $blockname Name of block to destroy
	*/
	public function destroy_block_vars($blockname)
	{
		$this->context->destroy_block_vars($blockname);
	}

	/**
	* Display handle
	* @param string $handle Handle to display
	* @return bool True on success, false on failure
	*/
	public function display($handle)
	{
		$result = $this->call_hook($handle);
		if ($result !== false)
		{
			return $result[0];
		}

		$renderer = $this->_tpl_load($handle);

		if ($renderer)
		{
			$renderer->render($this->context, $this->get_lang());
			return true;
		}
		else
		{
			return false;
		}
	}

	/**
	* Calls hook if any is defined.
	* @param string $handle Template handle being displayed.
	*/
	private function call_hook($handle)
	{
		global $phpbb_hook;

		if (!empty($phpbb_hook) && $phpbb_hook->call_hook(array(__CLASS__, __FUNCTION__), $handle, $this))
		{
			if ($phpbb_hook->hook_return(array(__CLASS__, __FUNCTION__)))
			{
				$result = $phpbb_hook->hook_return_result(array(__CLASS__, __FUNCTION__));
				return array($result);
			}
		}

		return false;
	}

	/**
	* Obtains language array.
	* This is either lang property of $user property, or if
	* it is not set an empty array.
	* @return array language entries
	*/
	public function get_lang()
	{
		if (isset($this->user->lang))
		{
			$lang = $this->user->lang;
		}
		else
		{
			$lang = array();
		}
		return $lang;
	}

	/**
	* Display the handle and assign the output to a template variable or return the compiled result.
	* @param string $handle Handle to operate on
	* @param string $template_var Template variable to assign compiled handle to
	* @param bool $return_content If true return compiled handle, otherwise assign to $template_var
	* @return bool|string false on failure, otherwise if $return_content is true return string of the compiled handle, otherwise return true
	*/
	public function assign_display($handle, $template_var = '', $return_content = true)
	{
		ob_start();
		$result = $this->display($handle);
		$contents = ob_get_clean();
		if ($result === false)
		{
			return false;
		}

		if ($return_content)
		{
			return $contents;
		}

		$this->assign_var($template_var, $contents);

		return true;
	}

	/**
	* Obtains a template renderer for a template identified by specified
	* handle. The template renderer can display the template later.
	*
	* Template source will first be compiled into php code.
	* If template cache is writable the compiled php code will be stored
	* on filesystem and template will not be subsequently recompiled.
	* If template cache is not writable template source will be recompiled
	* every time it is needed. DEBUG_EXTRA define and load_tplcompile
	* configuration setting may be used to force templates to be always
	* recompiled.
	*
	* Returns an object implementing phpbb_template_renderer, or null
	* if template loading or compilation failed. Call render() on the
	* renderer to display the template. This will result in template
	* contents sent to the output stream (unless, of course, output
	* buffering is in effect).
	*
	* @param string $handle Handle of the template to load
	* @return phpbb_template_renderer Template renderer object, or null on failure
	* @uses template_compile is used to compile template source
	*/
	private function _tpl_load($handle)
	{
		$virtual_source_file = $this->locator->get_virtual_source_file_for_handle($handle);
		$source_file = null;

		// reload this setting to have the values they had when this object was initialised
		// using set_template or set_custom_template, they might otherwise have been overwritten
		// by other template class instances in between.
		$this->user->theme['template_inherits_id'] = $this->orig_tpl_inherits_id;

		$compiled_path = $this->cachepath . str_replace('/', '.', $virtual_source_file) . '.' . $this->phpEx;

		$recompile = defined('DEBUG_EXTRA') ||
			!file_exists($compiled_path) ||
			@filesize($compiled_path) === 0 ||
			($this->config['load_tplcompile'] && @filemtime($compiled_path) < @filemtime($source_file));

		if (!$recompile && $this->config['load_tplcompile'])
		{
			$source_file = $this->locator->get_source_file_for_handle($handle);
			$recompile = (@filemtime($compiled_path) < @filemtime($source_file)) ? true : false;
		}

		// Recompile page if the original template is newer, otherwise load the compiled version
		if (!$recompile)
		{
			return new phpbb_template_renderer_include($compiled_path, $this);
		}

		if ($source_file === null)
		{
			$source_file = $this->locator->get_source_file_for_handle($handle);
		}

		$compile = new phpbb_template_compile($this->config['tpl_allow_php']);

		$output_file = $this->_compiled_file_for_handle($handle);
		if ($compile->compile_file_to_file($source_file, $output_file) !== false)
		{
			$renderer = new phpbb_template_renderer_include($output_file, $this);
		}
		else if (($code = $compile->compile_file($source_file)) !== false)
		{
			$renderer = new phpbb_template_renderer_eval($code, $this);
		}
		else
		{
			$renderer = null;
		}

		return $renderer;
	}

	/**
	* Determines compiled file path for handle $handle.
	* @param string $handle Template handle (i.e. "friendly" template name)
	* @return string Compiled file path
	*/
	private function _compiled_file_for_handle($handle)
	{
		$source_file = $this->locator->get_filename_for_handle($handle);
		$compiled_file = $this->cachepath . str_replace('/', '.', $source_file) . '.' . $this->phpEx;
		return $compiled_file;
	}

	/**
	* Assign key variable pairs from an array
	* @param array $vararray A hash of variable name => value pairs
	*/
	public function assign_vars(array $vararray)
	{
		foreach ($vararray as $key => $val)
		{
			$this->assign_var($key, $val);
		}
	}

	/**
	* Assign a single variable to a single key
	* @param string $varname Variable name
	* @param string $varval Value to assign to variable
	*/
	public function assign_var($varname, $varval)
	{
		$this->context->assign_var($varname, $varval);
	}

	// Docstring is copied from phpbb_template_context method with the same name.
	/**
	* Assign key variable pairs from an array to a specified block
	* @param string $blockname Name of block to assign $vararray to
	* @param array $vararray A hash of variable name => value pairs
	*/
	public function assign_block_vars($blockname, array $vararray)
	{
		return $this->context->assign_block_vars($blockname, $vararray);
	}

	// Docstring is copied from phpbb_template_context method with the same name.
	/**
	* Change already assigned key variable pair (one-dimensional - single loop entry)
	*
	* An example of how to use this function:
	* {@example alter_block_array.php}
	*
	* @param	string	$blockname	the blockname, for example 'loop'
	* @param	array	$vararray	the var array to insert/add or merge
	* @param	mixed	$key		Key to search for
	*
	* array: KEY => VALUE [the key/value pair to search for within the loop to determine the correct position]
	*
	* int: Position [the position to change or insert at directly given]
	*
	* If key is false the position is set to 0
	* If key is true the position is set to the last entry
	*
	* @param	string	$mode		Mode to execute (valid modes are 'insert' and 'change')
	*
	*	If insert, the vararray is inserted at the given position (position counting from zero).
	*	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).
	*
	* Since counting begins by zero, inserting at the last position will result in this array: array(vararray, last positioned array)
	* and inserting at position 1 will result in this array: array(first positioned array, vararray, following vars)
	*
	* @return bool false on error, true on success
	*/
	public function alter_block_array($blockname, array $vararray, $key = false, $mode = 'insert')
	{
		return $this->context->alter_block_array($blockname, $vararray, $key, $mode);
	}

	/**
	* Include a separate template
	* @param string $filename Template filename to include
	* @param bool $include True to include the file, false to just load it
	* @uses template_compile is used to compile uncached templates
	*/
	public function _tpl_include($filename, $include = true)
	{
		$this->locator->set_filenames(array($filename => $filename));

		$renderer = $this->_tpl_load($filename);

		if ($renderer)
		{
			$renderer->render($this->context, $this->get_lang());
		}
		else
		{
			// trigger_error cannot be used here, as the output already started
			echo 'template->_tpl_include(): Failed including ' . htmlspecialchars($handle) . "\n";
		}
	}

	/**
	* Include a php-file
	*/
	public function _php_include($filename)
	{
		if (phpbb_is_absolute($filename))
		{
			$file = $filename;
		}
		else
		{
			$file = $this->phpbb_root_path . $filename;
		}

		if (!file_exists($file))
		{
			// trigger_error cannot be used here, as the output already started
			echo 'template->_php_include(): File ' . htmlspecialchars($file) . " does not exist\n";
			return;
		}
		include($file);
	}
}