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
vikbooking / libraries / mvc / admin / models / overrides.php

overrides.php in VikBooking Hotel Booking Engine & PMS 1.8.16, at libraries/mvc/admin/models/overrides.php

603 lines 14.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage core
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 to this file
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 JLoader::import('adapter.mvc.models.form');
15
16 /**
17 * VikBooking plugin Overrides model.
18 * @wponly
19 *
20 * @since 1.6.5
21 * @see JModel
22 */
23 class VikBookingModelOverrides extends JModel
24 {
25 /**
26 * A list containing all the back-end views to exclude.
27 *
28 * @var array
29 */
30 public $_excludedAdminViews = ['getpro', 'gotopro', 'overrides'];
31
32 /**
33 * A list containing all the front-end views to exclude.
34 *
35 * @var array
36 */
37 public $_excludedSiteViews = [];
38
39 /**
40 * A list containing all the widgets to exclude.
41 *
42 * @var array
43 */
44 public $_excludedModules = [];
45
46 /**
47 * A list containing all the layouts to exclude.
48 *
49 * @var array
50 */
51 public $_excludedLayouts = [];
52
53 /**
54 * Checks whether the specified file supports overrides.
55 *
56 * @param mixed $tree Either the tree array or the client string.
57 * @param string $file The file to look for.
58 *
59 * @return mixed The node found on success, false otherwise.
60 */
61 public function isSupported($tree, string $file)
62 {
63 if (!is_array($tree))
64 {
65 // client given, generate tree
66 $tree = $this->getTree($tree);
67 }
68
69 // look for a leaf
70 if (isset($tree['folder']) && !$tree['folder'])
71 {
72 // leaf found, look for a match with the file path
73 if ($file == $tree['path'] || $file == $tree['override'])
74 {
75 // match found, override supported
76 return $tree;
77 }
78 else
79 {
80 // match not found, go ahead
81 return false;
82 }
83 }
84
85 // scan files if we are inside a node
86 if (isset($tree['files']))
87 {
88 $tree = $tree['files'];
89 }
90
91 // iterate nodes
92 foreach ($tree as $node)
93 {
94 // recursively check whether the node contains
95 // a file matching the specified path
96 if ($leaf = $this->isSupported($node, $file))
97 {
98 // leaf found, override supported
99 return $leaf;
100 }
101 }
102
103 // file not supported
104 return false;
105 }
106
107 /**
108 * Creates the tree containing the available overrides for the specified client.
109 *
110 * @param string $client The client to look for. Supports the following options:
111 * - administrator back-end views;
112 * - site front-end views;
113 * - layouts admin, site and core layouts;
114 * - modules plugin widgets.
115 *
116 * @return array The resulting tree.
117 */
118 public function getTree(string $client)
119 {
120 // look for administrator/site views
121 if (preg_match("/^(admin(?:istrator)?|site)$/i", $client))
122 {
123 // scan views overrides
124 $tree = $this->getViewsTree($client);
125 }
126 else if (preg_match("/^layouts?$/i", $client))
127 {
128 // scan layouts overrides
129 $tree = $this->getLayoutsTree();
130 }
131 else if (preg_match("/^(modules?|widgets?)$/i", $client))
132 {
133 // scan modules overrides
134 $tree = $this->getModulesTree();
135 }
136 else
137 {
138 // client not supported, throw exception
139 throw new Exception(sprintf('Override [%s] client not supported', $client), 500);
140 }
141
142 return $tree;
143 }
144
145 /**
146 * Creates the tree containing the available overrides for the admin/site views.
147 *
148 * @param string $client The client to look for.
149 *
150 * @return array The resulting tree.
151 */
152 protected function getViewsTree(string $client)
153 {
154 $tree = [];
155
156 // build path according to the specified client
157 if (preg_match("/^admin(?:istrator)?$/i", $client))
158 {
159 // admin path
160 $path = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'views';
161
162 // get list of excluded views
163 $excluded = $this->_excludedAdminViews;
164
165 // define client folder
166 $clientFolder = 'admin';
167 }
168 else
169 {
170 // site path
171 $path = VBO_SITE_PATH . DIRECTORY_SEPARATOR . 'views';
172
173 // get list of excluded views
174 $excluded = $this->_excludedSiteViews;
175
176 // define client folder
177 $clientFolder = 'site';
178 }
179
180 // retrieve temporary uploads path
181 $upload = wp_upload_dir();
182
183 // create base path of overrides
184 $overridesPath = JPath::clean($upload['basedir'] . '/vikbooking/overrides/' . $clientFolder . '/');
185
186 // get all folders at the specified path
187 $views = JFolder::folders($path, '.', $recursive = false, $fullPath = true);
188
189 // check whether the plugin supports specific views only for the WordPress platform
190 if (JFolder::exists(VIKBOOKING_LIBRARIES . '/mvc/' . $clientFolder . '/views'))
191 {
192 $views = array_merge($views, JFolder::folders(VIKBOOKING_LIBRARIES . '/mvc/' . $clientFolder . '/views', '.', $recursive = false, $fullPath = true));
193 }
194
195 // iterate views and extract all the supported templates
196 foreach ($views as $view)
197 {
198 $viewName = basename($view);
199
200 // make sure the view should not be excluded
201 if (!$this->isExcluded($viewName, $excluded))
202 {
203 // create node to inject within the tree
204 $node = [
205 'name' => $viewName,
206 'path' => $view,
207 'folder' => true,
208 'files' => [],
209 ];
210
211 // build path containing the view templates
212 $tmpl = JPath::clean($view . '/tmpl');
213
214 // scan files within the view
215 $files = JFolder::files($tmpl, '\.php$', $recursive = false, $fullPath = true);
216
217 // iterate files
218 foreach ($files as $file)
219 {
220 $filename = basename($file);
221
222 // create override path
223 $fileOverridePath = $overridesPath . $viewName . DIRECTORY_SEPARATOR . $filename;
224
225 $published = false;
226
227 // look for an existing override
228 if (JFile::exists($fileOverridePath))
229 {
230 // the file owns an override
231 $override = $published = true;
232 }
233 else
234 {
235 // look for an unpublished override
236 $unpublishedPath = $overridesPath . $viewName . DIRECTORY_SEPARATOR . '__' . $filename;
237
238 // check whether the file exists
239 if (JFile::exists($unpublishedPath))
240 {
241 // the file owns an unpublished override
242 $override = true;
243 }
244 else
245 {
246 // missing override
247 $override = false;
248 }
249 }
250
251 // register node within the parent
252 $node['files'][] = [
253 'name' => $filename,
254 'path' => $file,
255 'override' => $fileOverridePath,
256 'folder' => false,
257 'has' => $override,
258 'published' => $published,
259 ];
260 }
261
262 // append node to the tree
263 $tree[] = $node;
264 }
265 }
266
267 // sort folders by ascending name
268 usort($tree, function($a, $b)
269 {
270 return strcasecmp($a['name'], $b['name']);
271 });
272
273 return $tree;
274 }
275
276 /**
277 * Creates the tree containing the available overrides for the layouts.
278 * The first level will always contain these nodes: admin, site, core.
279 *
280 * @return array The resulting tree.
281 */
282 protected function getLayoutsTree()
283 {
284 $tree = [];
285
286 // administrator layouts
287 $tree[] = [
288 'name' => 'admin',
289 'path' => VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'layouts',
290 'folder' => true,
291 'files' => [],
292 ];
293
294 // site layouts
295 // $tree[] = [
296 // 'name' => 'site',
297 // 'path' => VBO_SITE_PATH . DIRECTORY_SEPARATOR . 'layouts',
298 // 'folder' => true,
299 // 'files' => [],
300 // ];
301
302 // system layouts
303 $tree[] = [
304 'name' => 'core',
305 'path' => VIKBOOKING_LIBRARIES . DIRECTORY_SEPARATOR . 'html',
306 'folder' => true,
307 'files' => [],
308 ];
309
310 // retrieve temporary uploads path
311 $upload = wp_upload_dir();
312
313 // create base path of overrides
314 $overridesPath = JPath::clean($upload['basedir'] . '/vikbooking/layouts/');
315
316 // iterate main nodes
317 foreach ($tree as &$node)
318 {
319 if ($node['name'] == 'site')
320 {
321 $op = $overridesPath . 'site' . DIRECTORY_SEPARATOR;
322 }
323 else
324 {
325 $op = $overridesPath . 'admin' . DIRECTORY_SEPARATOR;
326
327 if ($node['name'] == 'core')
328 {
329 // append HTML folder in case of core client
330 $op .= 'html' . DIRECTORY_SEPARATOR;
331 }
332 }
333
334 // scan and construct node recursively
335 $this->_scanNode($node, $op);
336 }
337
338 return $tree;
339 }
340
341 /**
342 * Creates the tree containing the available overrides for the plugin widgets.
343 *
344 * @return array The resulting tree.
345 */
346 protected function getModulesTree()
347 {
348 $tree = [];
349
350 // retrieve temporary uploads path
351 $upload = wp_upload_dir();
352
353 // create base path of overrides
354 $overridesPath = JPath::clean($upload['basedir'] . '/vikbooking/overrides/modules/');
355
356 // get all folders at the specified path
357 $modules = JFolder::folders(VIKBOOKING_BASE . '/modules', '.', $recursive = false, $fullPath = true);
358
359 // iterate modules and extract all the supported templates
360 foreach ($modules as $module)
361 {
362 $modName = basename($module);
363
364 // make sure the module should not be excluded
365 if (!$this->isExcluded($modName, $this->_excludedModules))
366 {
367 // create node to inject within the tree
368 $node = [
369 'name' => preg_replace("/^mod_vikbooking_/i", '', $modName),
370 'path' => $module,
371 'folder' => true,
372 'files' => [],
373 ];
374
375 // build path containing the module templates
376 $tmpl = JPath::clean($module . '/tmpl');
377
378 // scan files within the module
379 $files = JFolder::files($tmpl, '\.php$', $recursive = false, $fullPath = true);
380
381 if (!$files)
382 {
383 /**
384 * Do not include module within the tree in case there are not overrideable files.
385 *
386 * @since 1.8.4
387 */
388 continue;
389 }
390
391 // iterate files
392 foreach ($files as $file)
393 {
394 $filename = basename($file);
395
396 // create override path
397 $fileOverridePath = $overridesPath . $modName . DIRECTORY_SEPARATOR . $filename;
398
399 $published = false;
400
401 // look for an existing override
402 if (JFile::exists($fileOverridePath))
403 {
404 // the file owns an override
405 $override = $published = true;
406 }
407 else
408 {
409 // look for an unpublished override
410 $unpublishedPath = $overridesPath . $modName . DIRECTORY_SEPARATOR . '__' . $filename;
411
412 // check whether the file exists
413 if (JFile::exists($unpublishedPath))
414 {
415 // the file owns an unpublished override
416 $override = true;
417 }
418 else
419 {
420 // missing override
421 $override = false;
422 }
423 }
424
425 // register node within the parent
426 $node['files'][] = [
427 'name' => $filename,
428 'path' => $file,
429 'override' => $fileOverridePath,
430 'folder' => false,
431 'has' => $override,
432 'published' => $published,
433 ];
434 }
435
436 // append node to the tree
437 $tree[] = $node;
438 }
439 }
440
441 return $tree;
442 }
443
444 /**
445 * Recursive function used to scan the folder and files within the specified node.
446 *
447 * @param array &$node The node to scan.
448 * @param string $base The base path in which the overrides should locate.
449 *
450 * @return void
451 */
452 protected function _scanNode(array &$node, string $base)
453 {
454 // get all folders at the node path
455 $folders = JFolder::folders($node['path'], '.', $recursive = false, $fullPath = true);
456
457 // get all files at the node path
458 $files = JFolder::files($node['path'], '\.php$', $recursive = false, $fullPath = true);
459
460 // iterate folders
461 foreach ($folders as $folder)
462 {
463 $folderName = basename($folder);
464
465 // create folder node
466 $tmp = [
467 'name' => basename($folder),
468 'path' => $folder,
469 'folder' => true,
470 'files' => [],
471 ];
472
473 // folder recursive scan
474 $this->_scanNode($tmp, $base . $folderName . DIRECTORY_SEPARATOR);
475
476 // append folder to main node
477 $node['files'][] = $tmp;
478 }
479
480 // iterate files
481 foreach ($files as $file)
482 {
483 // make sure the file should not be excluded
484 if (!$this->isExcluded($file, $this->_excludedLayouts))
485 {
486 $filename = basename($file);
487
488 // create override path
489 $fileOverridePath = $base . $filename;
490
491 $published = false;
492
493 // look for an existing override
494 if (JFile::exists($fileOverridePath))
495 {
496 // the file owns an override
497 $override = $published = true;
498 }
499 else
500 {
501 // look for an unpublished override
502 $unpublishedPath = $base . DIRECTORY_SEPARATOR . '__' . $filename;
503
504 // check whether the file exists
505 if (JFile::exists($unpublishedPath))
506 {
507 // the file owns an unpublished override
508 $override = true;
509 }
510 else
511 {
512 // missing override
513 $override = false;
514 }
515 }
516
517 // create file node
518 $tmp = [
519 'name' => $filename,
520 'path' => $file,
521 'override' => $fileOverridePath,
522 'folder' => false,
523 'has' => $override,
524 'published' => $published,
525 ];
526
527 // append folder to main node
528 $node['files'][] = $tmp;
529 }
530 }
531 }
532
533 /**
534 * Check whether the specified path matches one of
535 * the specified regex.
536 *
537 * @param string $path The path to look for.
538 * @param array $pool An array of regex.
539 *
540 * @return bool True if excluded, false otherwise.
541 */
542 protected function isExcluded(string $path, array $pool)
543 {
544 $path = preg_replace("/[\\\\]/", '/', $path);
545
546 // iterate pool
547 foreach ($pool as $match)
548 {
549 // exec regex
550 if (preg_match("/$match/i", $path))
551 {
552 // inside the pool, exclude file
553 return true;
554 }
555 }
556
557 // can include file
558 return false;
559 }
560
561 /**
562 * Returns a lookup containing all the existing (and published) overrides,
563 * categorized by override group (admin, site, layouts, modules).
564 *
565 * Only the files that are an actual override of the core files will be taken here.
566 * In example, default_foo_bar.php is not part of VikBooking and therefore it
567 * will be discarded.
568 *
569 * @return array
570 */
571 public function getAllOverrides()
572 {
573 // retrieve temporary uploads path
574 $upload = wp_upload_dir();
575
576 // fetch all the files created on each section
577 $lookup = [
578 'admin' => JFolder::files($upload['basedir'] . '/vikbooking/overrides/admin/', '\.php$', true, true),
579 'site' => JFolder::files($upload['basedir'] . '/vikbooking/overrides/site/', '\.php$', true, true),
580 'layouts' => JFolder::files($upload['basedir'] . '/vikbooking/layouts/', '\.php$', true, true),
581 'modules' => JFolder::files($upload['basedir'] . '/vikbooking/overrides/modules/', '\.php$', true, true),
582 ];
583
584 // exclude all the overrides that are actually unpublished
585 foreach ($lookup as &$files)
586 {
587 if (!is_array($files))
588 {
589 $files = [];
590 }
591
592 $files = array_values(array_filter($files, function($file)
593 {
594 // the override file name must not start with 2 underscores
595 return strpos(basename($file), '__') !== 0;
596 }));
597 }
598
599 // get rid of the sections without files
600 return array_filter($lookup);
601 }
602 }
603