PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | libraries/adapter/layout/file.php +571 -0 1.6.0 → 1.8.16 View file →
@@ -1,0 +1,571 @@
1 +<?php
2 +/**
3 + * @package VikWP - Libraries
4 + * @subpackage adapter.layout
5 + * @author E4J s.r.l.
6 + * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved.
7 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 + * @link https://vikwp.com
9 + */
10 +
11 +// No direct access
12 +defined('ABSPATH') or die('No script kiddies please!');
13 +
14 +JLoader::import('adapter.layout.base');
15 +JLoader::import('adapter.filesystem.path');
16 +
17 +/**
18 + * Base class for rendering a display layout loaded from from a layout file.
19 + *
20 + * It is possible to create theme overrides for the layouts by adding the specific
21 + * file into a path built as follows:
22 + * /wp-content/uploads/[PLUGIN_NAME]/layouts/[CLIENT]/[LAYOUT_PATH].php
23 + *
24 + * For example, in case we need to override the site 'html.user.login' layout
25 + * that belong to the 'vik' plugin, the path will look like:
26 + * /wp-content/uploads/vik/layouts/site/html/user/login.php
27 + *
28 + * The client can assume only 2 values: site or admin.
29 + *
30 + * @since 10.0
31 + */
32 +class JLayoutFile extends JLayoutBase
33 +{
34 + /**
35 + * A list containing the cached layout paths.
36 + *
37 + * @var array
38 + */
39 + protected static $cache = array();
40 +
41 + /**
42 + * The identifier of the layout file to render.
43 + *
44 + * @var string
45 + */
46 + protected $layoutId = '';
47 +
48 + /**
49 + * The base path of the files to include.
50 + *
51 + * @var string
52 + */
53 + protected $basePath = null;
54 +
55 + /**
56 + * Full path to actual layout files, after possible template override check.
57 + *
58 + * @var string
59 + * @since 10.1.18
60 + */
61 + protected $fullPath = null;
62 +
63 + /**
64 + * Paths to search for layouts.
65 + *
66 + * @var array
67 + * @since 10.1.18
68 + */
69 + protected $includePaths = array();
70 +
71 + /**
72 + * Method to instantiate the file-based layout.
73 + *
74 + * @param string $layoutId Dot separated path to the layout file, relative to base path.
75 + * @param string $basePath Base path to use when loading layout files.
76 + * @param mixed $options Optional custom options to load. Registry or array format.
77 + */
78 + public function __construct($layoutId, $basePath = null, $options = null)
79 + {
80 + // initialise / load options
81 + $this->setOptions($options);
82 +
83 + // main properties
84 + $this->setLayoutId($layoutId);
85 + $this->basePath = $basePath;
86 +
87 + // init enviroment
88 + $this->setComponent($this->options->get('component', 'auto'));
89 + $this->setClient($this->options->get('client', 'auto'));
90 + }
91 +
92 + /**
93 + * Method to render the layout.
94 + *
95 + * @param array $displayData Array of properties available for use inside
96 + * the layout file to build the displayed output.
97 + *
98 + * @return string The necessary HTML to display the layout.
99 + *
100 + * @uses clearDebugMessages()
101 + * @uses getPath()
102 + * @uses isDebugEnabled()
103 + * @uses renderDebugMessages()
104 + */
105 + public function render($displayData = array())
106 + {
107 + $this->clearDebugMessages();
108 +
109 + // inherit base output from parent class
110 + $layoutOutput = '';
111 +
112 + // automatically merge any previously data set if $displayData is an array
113 + if (is_array($displayData))
114 + {
115 + $displayData = array_merge($this->data, $displayData);
116 + }
117 +
118 + // check possible overrides, and build the full path to layout file
119 + $path = $this->getPath();
120 +
121 + if ($this->isDebugEnabled())
122 + {
123 + echo '<pre>' . $this->renderDebugMessages() . '</pre>';
124 + }
125 +
126 + // nothing to show
127 + if (empty($path))
128 + {
129 + return $layoutOutput;
130 + }
131 +
132 + // start buffer
133 + ob_start();
134 + // include the file
135 + include $path;
136 + // push the buffer data in a variable
137 + $layoutOutput = ob_get_contents();
138 + // end buffer end clean
139 + ob_end_clean();
140 +
141 + return $layoutOutput;
142 + }
143 +
144 + /**
145 + * Method to find the full real file path, checking possible overrides.
146 + *
147 + * @return string The full path to the layout file.
148 + *
149 + * @uses getLayoutId()
150 + * @uses getIncludePaths()
151 + * @uses addDebugMessage()
152 + */
153 + protected function getPath()
154 + {
155 + $layoutId = $this->getLayoutId();
156 + $includePaths = $this->getIncludePaths();
157 +
158 + $this->addDebugMessage('<strong>Layout:</strong> ' . $this->layoutId);
159 +
160 + // make sure we have something to render
161 + if (!$layoutId)
162 + {
163 + $this->addDebugMessage('<strong>There is no active layout</strong>');
164 +
165 + return;
166 + }
167 +
168 + // make sure some paths have been specified
169 + if (!$includePaths)
170 + {
171 + $this->addDebugMessage('<strong>There are no folders to search for layouts:</strong> ' . $layoutId);
172 +
173 + return;
174 + }
175 +
176 + // create signature key
177 + $hash = md5(json_encode($includePaths));
178 +
179 + // check if the same layout has been already cached
180 + if (!empty(static::$cache[$layoutId][$hash]))
181 + {
182 + $this->addDebugMessage('<strong>Cached path:</strong> ' . static::$cache[$layoutId][$hash]);
183 +
184 + return static::$cache[$layoutId][$hash];
185 + }
186 +
187 + $this->addDebugMessage('<strong>Include Paths:</strong> ' . print_r($includePaths, true));
188 +
189 + // standard version
190 + $rawPath = str_replace('.', '/', $this->layoutId) . '.php';
191 + $this->addDebugMessage('<strong>Searching layout for:</strong> ' . $rawPath);
192 +
193 + // search for a layout file
194 + $foundLayout = JPath::find($this->includePaths, $rawPath);
195 +
196 + if (!$foundLayout)
197 + {
198 + // impossible to find any layouts
199 + $this->addDebugMessage('<strong>Unable to find layout: </strong> ' . $layoutId);
200 +
201 + return;
202 + }
203 +
204 + $this->addDebugMessage('<strong>Found layout:</strong> ' . $foundLayout);
205 +
206 + // cache the layout found for later uses
207 + static::$cache[$layoutId][$hash] = $foundLayout;
208 +
209 + return static::$cache[$layoutId][$hash];
210 + }
211 +
212 + /**
213 + * Adds one path to include in layout search.
214 + * Proxy of addIncludePaths().
215 + *
216 + * @param string $path The path to search for layouts.
217 + *
218 + * @return self This object to support chaining.
219 + *
220 + * @since 10.1.18
221 + *
222 + * @uses addIncludePaths()
223 + */
224 + public function addIncludePath($path)
225 + {
226 + $this->addIncludePaths($path);
227 +
228 + return $this;
229 + }
230 +
231 + /**
232 + * Adds one or more paths to include in layout search.
233 + *
234 + * @param mixed $paths The path or array of paths to search for layouts.
235 + *
236 + * @return self This object to support chaining.
237 + *
238 + * @since 10.1.18
239 + *
240 + * @uses getIncludePaths()
241 + * @uses setIncludePaths()
242 + */
243 + public function addIncludePaths($paths)
244 + {
245 + if (empty($paths))
246 + {
247 + return $this;
248 + }
249 +
250 + $includePaths = $this->getIncludePaths();
251 +
252 + // in case the path is an array, merge all the paths and make sure we have no duplicated
253 + if (is_array($paths))
254 + {
255 + $includePaths = array_unique(array_merge($paths, $includePaths));
256 + }
257 + // otherwise add the path as first element
258 + else
259 + {
260 + array_unshift($includePaths, $paths);
261 + }
262 +
263 + // update include paths
264 + $this->setIncludePaths($includePaths);
265 +
266 + return $this;
267 + }
268 +
269 + /**
270 + * Clears the include paths.
271 + *
272 + * @return self This object to support chaining.
273 + *
274 + * @since 10.1.18
275 + */
276 + public function clearIncludePaths()
277 + {
278 + $this->includePaths = array();
279 +
280 + return $this;
281 + }
282 +
283 + /**
284 + * Gets the active include paths.
285 + *
286 + * @return array
287 + *
288 + * @since 10.1.18
289 + *
290 + * @uses getDefaultIncludePaths()
291 + */
292 + public function getIncludePaths()
293 + {
294 + if (empty($this->includePaths))
295 + {
296 + $this->includePaths = $this->getDefaultIncludePaths();
297 + }
298 +
299 + return $this->includePaths;
300 + }
301 +
302 + /**
303 + * Gets the active layout id.
304 + *
305 + * @return string
306 + *
307 + * @since 10.1.18
308 + */
309 + public function getLayoutId()
310 + {
311 + return $this->layoutId;
312 + }
313 +
314 + /**
315 + * Removes one path from the layout search.
316 + * Proxy of removeIncludePaths().
317 + *
318 + * @param string $path The path to remove from the layout search.
319 + *
320 + * @return self This object to support chaining.
321 + *
322 + * @since 10.1.18
323 + *
324 + * @uses removeIncludePaths()
325 + */
326 + public function removeIncludePath($path)
327 + {
328 + $this->removeIncludePaths($path);
329 +
330 + return $this;
331 + }
332 +
333 + /**
334 + * Removes one or more paths to exclude in layout search.
335 + *
336 + * @param mixed $paths The path or array of paths to remove for the layout search.
337 + *
338 + * @return self This object to support chaining.
339 + *
340 + * @since 10.1.18
341 + */
342 + public function removeIncludePaths($paths)
343 + {
344 + if (!empty($paths))
345 + {
346 + // always use an array of paths
347 + $paths = (array) $paths;
348 +
349 + // obtain a list of paths by excluding the specified ones
350 + $this->includePaths = array_diff($this->includePaths, $paths);
351 + }
352 +
353 + return $this;
354 + }
355 +
356 + /**
357 + * Validates that the active component is valid.
358 + *
359 + * @param string $option URL Option of the component (e.g. com_xxx).
360 + *
361 + * @return boolean True if valid, false otherwise.
362 + *
363 + * @since 10.1.18
364 + */
365 + protected function validComponent($option = null)
366 + {
367 + // by default we will validate the active component
368 + $component = ($option !== null) ? $option : $this->options->get('component', null);
369 +
370 + // validate option format
371 + return !empty($component) && preg_match("/^com_/", $component);
372 + }
373 +
374 + /**
375 + * Method to change the component where search for layouts.
376 + *
377 + * @param string $option URL Option of the component (e.g. com_xxx).
378 + *
379 + * @return void
380 + *
381 + * @since 10.1.18
382 + *
383 + * @uses validComponent()
384 + * @uses clearIncludePaths()
385 + */
386 + public function setComponent($option)
387 + {
388 + $component = null;
389 +
390 + switch ((string) $option)
391 + {
392 + case 'none':
393 + $component = null;
394 + break;
395 +
396 + case 'auto':
397 + // recover component name from request
398 + $component = JFactory::getApplication()->input->get('option', null);
399 + break;
400 +
401 + default:
402 + $component = $option;
403 + break;
404 + }
405 +
406 + // extra checks
407 + if (!$this->validComponent($component))
408 + {
409 + $component = null;
410 + }
411 + else
412 + {
413 + $component = preg_replace("/^com_/", '', $component);
414 + }
415 +
416 + // update component option
417 + $this->options->set('component', $component);
418 +
419 + // refresh include paths
420 + $this->clearIncludePaths();
421 + }
422 +
423 + /**
424 + * Function to initialise the application client.
425 + *
426 + * @param mixed $client The application client:
427 + * - 0 or 'site': front-end;
428 + * - 1 or 'admin': back-end.
429 + *
430 + * @return void
431 + *
432 + * @since 10.1.18
433 + *
434 + * @uses clearIncludePaths()
435 + */
436 + public function setClient($client)
437 + {
438 + // force string conversion to avoid unexpected states
439 + switch ((string) $client)
440 + {
441 + case 'site':
442 + case '0':
443 + $client = 0;
444 + break;
445 +
446 + case 'admin':
447 + case '1':
448 + $client = 1;
449 + break;
450 +
451 + default:
452 + $client = (int) JFactory::getApplication()->isAdmin();
453 + break;
454 + }
455 +
456 + // update client option
457 + $this->options->set('client', $client);
458 +
459 + // refresh include paths
460 + $this->clearIncludePaths();
461 + }
462 +
463 + /**
464 + * Sets the active layout id.
465 + *
466 + * @param string $layoutId Layout identifier.
467 + *
468 + * @return self This object to support chaining.
469 + *
470 + * @since 10.1.18
471 + */
472 + public function setLayoutId($layoutId)
473 + {
474 + $this->layoutId = $layoutId;
475 + $this->fullPath = null;
476 +
477 + return $this;
478 + }
479 +
480 + /**
481 + * Gets the default array of include paths.
482 + *
483 + * @return array
484 + *
485 + * @since 10.1.18
486 + */
487 + public function getDefaultIncludePaths()
488 + {
489 + $paths = array();
490 +
491 + // (1 - highest priority) Received a custom high priority path
492 + if ($this->basePath !== null)
493 + {
494 + $paths[] = rtrim($this->basePath, DIRECTORY_SEPARATOR);
495 + }
496 +
497 + // component layouts & overrides if exist
498 + $component = $this->options->get('component', null);
499 +
500 + if (!empty($component))
501 + {
502 + // get upload dir
503 + $uploads = wp_upload_dir();
504 +
505 + if ($this->options->get('client') == 0)
506 + {
507 + // (2) build component override site layouts path
508 + $paths[] = rtrim($uploads['basedir'], DIRECTORY_SEPARATOR) . '/' . $component . '/layouts/site';
509 +
510 + // (3) build component site layouts path
511 + $paths[] = WP_PLUGIN_DIR . '/' . $component . '/site/layouts';
512 + }
513 + else
514 + {
515 + // (2) build component override admin layouts path
516 + $paths[] = rtrim($uploads['basedir'], DIRECTORY_SEPARATOR) . '/' . $component . '/layouts/admin';
517 +
518 + // (3) build component admin layouts path
519 + $paths[] = WP_PLUGIN_DIR . '/' . $component . '/admin/layouts';
520 + }
521 +
522 + // (4) build libraries layouts path
523 + $paths[] = WP_PLUGIN_DIR . '/' . $component . '/libraries';
524 + }
525 +
526 + return $paths;
527 + }
528 +
529 + /**
530 + * Sets the include paths to search for layouts.
531 + *
532 + * @param array $paths Array with paths to search in.
533 + *
534 + * @return self This object to support chaining.
535 + *
536 + * @since 10.1.18
537 + */
538 + public function setIncludePaths($paths)
539 + {
540 + $this->includePaths = (array) $paths;
541 +
542 + return $this;
543 + }
544 +
545 + /**
546 + * Render a layout with the same include paths & options.
547 + *
548 + * @param string $layoutId The identifier for the sublayout to be searched in a
549 + * subfolder with the name of the current layout.
550 + * @param mixed $displayData Data to be rendered.
551 + *
552 + * @return string The necessary HTML to display the layout.
553 + *
554 + * @since 10.1.18
555 + */
556 + public function sublayout($layoutId, $displayData = array())
557 + {
558 + // sublayouts are searched in a subfolder with the name of the current layout
559 + if (!empty($this->layoutId))
560 + {
561 + $layoutId = $this->layoutId . '.' . $layoutId;
562 + }
563 +
564 + // instantiate new layout file
565 + $sublayout = new static($layoutId, $this->basePath, $this->options);
566 + $sublayout->includePaths = $this->includePaths;
567 +
568 + // render sublayout
569 + return $sublayout->render($displayData);
570 + }
571 +}