Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.95% |
94 / 99 |
|
50.00% |
4 / 8 |
CRAP | |
0.00% |
0 / 1 |
twig | |
94.95% |
94 / 99 |
|
50.00% |
4 / 8 |
38.19 | |
0.00% |
0 / 1 |
__construct | |
93.75% |
15 / 16 |
|
0.00% |
0 / 1 |
5.01 | |||
get_user_style | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
set_style | |
95.65% |
22 / 23 |
|
0.00% |
0 / 1 |
12 | |||
set_custom_style | |
93.55% |
29 / 31 |
|
0.00% |
0 / 1 |
11.03 | |||
display | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
assign_display | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
get_template_vars | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
3 | |||
get_source_file_for_handle | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | namespace phpbb\template\twig; |
15 | |
16 | use phpbb\template\exception\user_object_not_available; |
17 | |
18 | /** |
19 | * Twig Template class. |
20 | */ |
21 | class twig extends \phpbb\template\base |
22 | { |
23 | /** |
24 | * Path of the cache directory for the template |
25 | * Cannot be changed during runtime. |
26 | * |
27 | * @var string |
28 | */ |
29 | private $cachepath = ''; |
30 | |
31 | /** @var \phpbb\path_helper */ |
32 | protected $path_helper; |
33 | |
34 | /** @var string phpBB root path */ |
35 | protected $phpbb_root_path; |
36 | |
37 | /** @var string php File extension */ |
38 | protected $php_ext; |
39 | |
40 | /** @var \phpbb\config\config */ |
41 | protected $config; |
42 | |
43 | /** @var \phpbb\user */ |
44 | protected $user; |
45 | |
46 | /** @var \phpbb\extension\manager */ |
47 | protected $extension_manager; |
48 | |
49 | /** @var environment */ |
50 | protected $twig; |
51 | |
52 | /** @var loader */ |
53 | protected $loader; |
54 | |
55 | /** |
56 | * Constructor. |
57 | * |
58 | * @param \phpbb\path_helper $path_helper Path helper object |
59 | * @param \phpbb\config\config $config Config object |
60 | * @param \phpbb\template\context $context Template context |
61 | * @param environment $twig_environment Twig environment |
62 | * @param string $cache_path Template's cache directory path |
63 | * @param null|\phpbb\user $user User object |
64 | * @param array|\ArrayAccess $extensions Template extensions |
65 | * @param null|\phpbb\extension\manager $extension_manager If null then template events will not be invoked |
66 | */ |
67 | public function __construct( |
68 | \phpbb\path_helper $path_helper, |
69 | \phpbb\config\config $config, |
70 | \phpbb\template\context $context, |
71 | environment $twig_environment, |
72 | $cache_path, |
73 | \phpbb\user $user = null, |
74 | $extensions = [], |
75 | \phpbb\extension\manager $extension_manager = null |
76 | ) |
77 | { |
78 | $this->path_helper = $path_helper; |
79 | $this->phpbb_root_path = $path_helper->get_phpbb_root_path(); |
80 | $this->php_ext = $path_helper->get_php_ext(); |
81 | $this->config = $config; |
82 | $this->user = $user; |
83 | $this->context = $context; |
84 | $this->extension_manager = $extension_manager; |
85 | $this->cachepath = $cache_path; |
86 | $this->twig = $twig_environment; |
87 | $this->loader = $twig_environment->getLoader(); |
88 | |
89 | foreach ($extensions as $extension) |
90 | { |
91 | $this->twig->addExtension($extension); |
92 | } |
93 | |
94 | // Add admin namespace |
95 | if ($this->path_helper->get_adm_relative_path() !== null |
96 | && is_dir($this->phpbb_root_path . $this->path_helper->get_adm_relative_path() . 'style/') |
97 | && $this->loader instanceof \Twig\Loader\FilesystemLoader) |
98 | { |
99 | $this->loader->setPaths($this->phpbb_root_path . $this->path_helper->get_adm_relative_path() . 'style/', 'admin'); |
100 | } |
101 | } |
102 | |
103 | /** |
104 | * Get the style tree of the style preferred by the current user |
105 | * |
106 | * @return array Style tree, most specific first |
107 | * |
108 | * @throws user_object_not_available When user service was not set |
109 | */ |
110 | public function get_user_style() |
111 | { |
112 | if ($this->user === null) |
113 | { |
114 | throw new user_object_not_available(); |
115 | } |
116 | |
117 | $style_list = [ |
118 | $this->user->style['style_path'], |
119 | ]; |
120 | |
121 | if ($this->user->style['style_parent_id']) |
122 | { |
123 | $style_list = array_merge($style_list, array_reverse(explode('/', $this->user->style['style_parent_tree']))); |
124 | } |
125 | |
126 | return $style_list; |
127 | } |
128 | |
129 | /** |
130 | * Set style location based on (current) user's chosen style. |
131 | * |
132 | * @param array $style_directories The directories to add style paths for |
133 | * E.g. array('ext/foo/bar/styles', 'styles') |
134 | * Default: array('styles') (phpBB's style directory) |
135 | * @return \phpbb\template\template $this |
136 | */ |
137 | public function set_style($style_directories = ['styles']) |
138 | { |
139 | if ($style_directories !== ['styles'] && $this->loader->getPaths('core') === []) |
140 | { |
141 | // We should set up the core styles path since not already setup |
142 | $this->set_style(); |
143 | } |
144 | |
145 | $paths = []; |
146 | |
147 | // Add 'all' folder to $names array |
148 | // It allows extensions to load a template file from 'all' folder, |
149 | // if a style doesn't include it. |
150 | $names = $this->get_user_style(); |
151 | $names[] = 'all'; |
152 | |
153 | foreach ($style_directories as $directory) |
154 | { |
155 | foreach ($names as $name) |
156 | { |
157 | $path = $this->phpbb_root_path . trim($directory, '/') . "/{$name}/"; |
158 | $handle = @opendir($path); |
159 | $valid = false; |
160 | |
161 | if ($handle) |
162 | { |
163 | while (($file = readdir($handle)) !== false) |
164 | { |
165 | $dir = $path . $file; |
166 | |
167 | if ($file[0] !== '.' && is_dir($dir)) |
168 | { |
169 | $paths[] = $dir; |
170 | |
171 | $valid = true; |
172 | } |
173 | } |
174 | |
175 | closedir($handle); |
176 | } |
177 | |
178 | if ($valid) |
179 | { |
180 | // Add the base style directory as a safe directory |
181 | $this->loader->addSafeDirectory($path); |
182 | } |
183 | } |
184 | } |
185 | |
186 | // If we're setting up the main phpBB styles directory and the core |
187 | // namespace isn't setup yet, we will set it up now |
188 | if ($style_directories === ['styles'] && $this->loader->getPaths('core') === []) |
189 | { |
190 | // Set up the core style paths namespace |
191 | $this->loader->setPaths($paths, 'core'); |
192 | } |
193 | |
194 | $this->set_custom_style($names, $paths); |
195 | |
196 | return $this; |
197 | } |
198 | |
199 | /** |
200 | * Set custom style location (able to use directory outside of phpBB). |
201 | * |
202 | * Note: Templates are still compiled to phpBB's cache directory. |
203 | * |
204 | * @param string|array $names Array of names (or detailed names) or string of name of template(s) in inheritance tree order, used by extensions. |
205 | * E.g. array( |
206 | * 'name' => 'adm', |
207 | * 'ext_path' => 'adm/style/', |
208 | * ) |
209 | * @param string|array $paths Array of style paths, relative to current root directory |
210 | * @return \phpbb\template\template $this |
211 | */ |
212 | public function set_custom_style($names, $paths) |
213 | { |
214 | $paths = (is_string($paths)) ? [$paths] : $paths; |
215 | $names = (is_string($names)) ? [$names] : $names; |
216 | |
217 | // Set as __main__ namespace |
218 | $this->loader->setPaths($paths); |
219 | |
220 | // Add all namespaces for all extensions |
221 | if ($this->extension_manager instanceof \phpbb\extension\manager) |
222 | { |
223 | $names[] = 'all'; |
224 | |
225 | foreach ($this->extension_manager->all_enabled() as $ext_namespace => $ext_path) |
226 | { |
227 | // namespaces cannot contain / |
228 | $namespace = str_replace('/', '_', $ext_namespace); |
229 | $paths = []; |
230 | |
231 | foreach ($names as $template_dir) |
232 | { |
233 | if (is_array($template_dir)) |
234 | { |
235 | if (isset($template_dir['ext_path'])) |
236 | { |
237 | $ext_style_template_path = $ext_path . $template_dir['ext_path']; |
238 | $ext_style_path = dirname($ext_style_template_path); |
239 | $ext_style_theme_path = $ext_style_path . 'theme/'; |
240 | } |
241 | else |
242 | { |
243 | $ext_style_path = $ext_path . 'styles/' . $template_dir['name'] . '/'; |
244 | $ext_style_template_path = $ext_style_path . 'template/'; |
245 | $ext_style_theme_path = $ext_style_path . 'theme/'; |
246 | } |
247 | } |
248 | else |
249 | { |
250 | $ext_style_path = $ext_path . 'styles/' . $template_dir . '/'; |
251 | $ext_style_template_path = $ext_style_path . 'template/'; |
252 | $ext_style_theme_path = $ext_style_path . 'theme/'; |
253 | } |
254 | |
255 | $is_valid_dir = false; |
256 | if (is_dir($ext_style_template_path)) |
257 | { |
258 | $is_valid_dir = true; |
259 | $paths[] = $ext_style_template_path; |
260 | } |
261 | if (is_dir($ext_style_theme_path)) |
262 | { |
263 | $is_valid_dir = true; |
264 | $paths[] = $ext_style_theme_path; |
265 | } |
266 | |
267 | if ($is_valid_dir) |
268 | { |
269 | // Add the base style directory as a safe directory |
270 | $this->loader->addSafeDirectory($ext_style_path); |
271 | } |
272 | } |
273 | |
274 | $this->loader->setPaths($paths, $namespace); |
275 | } |
276 | } |
277 | |
278 | return $this; |
279 | } |
280 | |
281 | /** |
282 | * Display a template for provided handle. |
283 | * |
284 | * The template will be loaded and compiled, if necessary, first. |
285 | * |
286 | * This function calls hooks. |
287 | * |
288 | * @param string $handle Handle to display |
289 | * @return \phpbb\template\template $this |
290 | */ |
291 | public function display($handle) |
292 | { |
293 | $this->twig->display($this->get_filename_from_handle($handle), $this->get_template_vars()); |
294 | |
295 | return $this; |
296 | } |
297 | |
298 | /** |
299 | * Display the handle and assign the output to a template variable |
300 | * or return the compiled result. |
301 | * |
302 | * @param string $handle Handle to operate on |
303 | * @param string $template_var Template variable to assign compiled handle to |
304 | * @param bool $return_content If true return compiled handle, otherwise assign to $template_var |
305 | * @return \phpbb\template\template|string if $return_content is true return string of the compiled handle, otherwise return $this |
306 | */ |
307 | public function assign_display($handle, $template_var = '', $return_content = true) |
308 | { |
309 | if ($return_content) |
310 | { |
311 | return $this->twig->render($this->get_filename_from_handle($handle), $this->get_template_vars()); |
312 | } |
313 | |
314 | $this->assign_var($template_var, $this->twig->render($this->get_filename_from_handle($handle), $this->get_template_vars())); |
315 | |
316 | return $this; |
317 | } |
318 | |
319 | /** |
320 | * Get template vars in a format Twig will use (from the context) |
321 | * |
322 | * @return array |
323 | */ |
324 | protected function get_template_vars() |
325 | { |
326 | $context_vars = $this->context->get_data_ref(); |
327 | |
328 | $vars = array_merge( |
329 | $context_vars['.'][0], // To get normal vars |
330 | [ |
331 | 'definition' => new \phpbb\template\twig\definition(), |
332 | 'loops' => $context_vars, // To get loops |
333 | ] |
334 | ); |
335 | |
336 | if ($this->user instanceof \phpbb\user) |
337 | { |
338 | $vars['user'] = $this->user; |
339 | } |
340 | |
341 | // cleanup |
342 | unset($vars['loops']['.']); |
343 | |
344 | // Inject in the main context the value added by assign_block_vars() to be able to use directly the Twig loops. |
345 | foreach ($vars['loops'] as $key => &$value) |
346 | { |
347 | $vars[$key] = $value; |
348 | } |
349 | |
350 | return $vars; |
351 | } |
352 | |
353 | /** |
354 | * {@inheritdoc} |
355 | */ |
356 | public function get_source_file_for_handle($handle) |
357 | { |
358 | return $this->loader->getCacheKey($this->get_filename_from_handle($handle)); |
359 | } |
360 | } |