PluginProbe
VikBooking Hotel Booking Engine & PMS / trunk
VikBooking Hotel Booking Engine & PMS vtrunk
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 1.7.3 All 36 releases
vikbooking / libraries / adapter / application / document.php

document.php in VikBooking Hotel Booking Engine & PMS trunk, at libraries/adapter/application/document.php

666 lines 16.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikWP - Libraries
4 * @subpackage adapter.application
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 /**
15 * Document class, provides an easy interface to parse and display a document
16 * using the Joomla standard functions.
17 *
18 * @since 10.0
19 */
20 class JDocument
21 {
22 /**
23 * An array to cache the meta data set using this class.
24 *
25 * @var array
26 */
27 protected $data = [];
28
29 /**
30 * A list containing all the style declarations loaded.
31 *
32 * @var array
33 */
34 protected $styleDeclarations = [];
35
36 /**
37 * Array of scripts options.
38 *
39 * @var array
40 * @since 10.1.14
41 */
42 protected $scriptOptions = [];
43
44 /**
45 * Array of scripts declarations to be
46 * appended after the body.
47 *
48 * @var array
49 * @since 10.1.29
50 */
51 protected $ajaxScripts = [];
52
53 /**
54 * Adds the possibility to override the callback used to register
55 * something within the head.
56 *
57 * @param callable
58 * @since 10.1.51
59 */
60 public $attachToHeadCustomCallback = null;
61
62 /**
63 * Sets a meta tag in the front-end only.
64 *
65 * @param string $name Name of the meta HTML tag.
66 * @param mixed $content Value of the meta HTML tag as array or string.
67 * @param string $attribute Attribute to use in the meta HTML tag.
68 *
69 * @return self This object to support chaining.
70 *
71 * @since 10.1.66 Robots are now treated differently.
72 *
73 * @uses attachToHead()
74 */
75 public function setMetaData($name, $content, $attribute = 'name')
76 {
77 if ($name === 'robots')
78 {
79 add_filter('wp_robots', function($robots) use ($content) {
80 if (!$content)
81 {
82 return $robots;
83 }
84
85 $robots = [];
86
87 // obtain all the directives from the content (separated by a comma)
88 $directives = preg_split("/\s*,\s*/", $content);
89
90 foreach ($directives as $directive)
91 {
92 // obtain key-val pairs (separated by a colon)
93 $directive = preg_split("/\s*:\s*/", $directive);
94
95 $key = strtolower(array_shift($directive));
96
97 if (count($directive) == 0)
98 {
99 // attribute only, use true as value
100 $val = true;
101 }
102 else
103 {
104 $val = implode(':', $directive);
105 }
106
107 $robots[$key] = $val;
108 }
109
110 return $robots;
111 });
112 }
113 else
114 {
115 $attribute = empty($attribute) || !is_string($attribute) ? 'name' : $attribute;
116
117 if (is_scalar($content))
118 {
119 $content = array($content);
120 }
121
122 $this->attachToHead(function() use ($name, $content, $attribute)
123 {
124 foreach ($content as $cont)
125 {
126 echo "<meta {$attribute}=\"{$name}\" content=\"" . esc_attr($cont) . "\" />\n";
127 }
128 }, true);
129 }
130
131 $this->data[$name] = $content;
132
133 return $this;
134 }
135
136 /**
137 * Gets a meta tag.
138 * Since Wordpress doesn't own a system to handle meta data,
139 * we can only return a cached version of the data set using this class.
140 *
141 * @param string $name Name of the meta HTML tag.
142 *
143 * @return string
144 */
145 public function getMetaData($name)
146 {
147 if (isset($this->data[$name]))
148 {
149 return $this->data[$name];
150 }
151
152 return '';
153 }
154
155 /**
156 * Sets the title of the document in the front-end only.
157 *
158 * @param string $title The title to be set.
159 *
160 * @return self This object to support chaining.
161 *
162 * @link https://developer.wordpress.org/reference/hooks/wp_title/ (Wordpress < 4.4)
163 * @link https://developer.wordpress.org/reference/functions/wp_get_document_title/ (Wordpress 4.4+)
164 */
165 public function setTitle($title)
166 {
167 /**
168 * We should use the hook 'pre_get_document_title' (available from WP >= v4.4),
169 * instead of 'wp_title', or the title won't actually be set.
170 *
171 * @since 10.1.23
172 */
173 add_filter('pre_get_document_title', function() use ($title)
174 {
175 return $title;
176 });
177
178 return $this;
179 }
180
181 /**
182 * Return the title of the document.
183 *
184 * @return string
185 *
186 * @link https://developer.wordpress.org/reference/hooks/wp_title/ (Wordpress < 4.4)
187 * @link https://developer.wordpress.org/reference/functions/wp_get_document_title/ (Wordpress 4.4+)
188 */
189 public function getTitle()
190 {
191 global $wp_version;
192
193 if (version_compare($wp_version, '4.4', '>='))
194 {
195 return wp_get_document_title();
196 }
197
198 return wp_title('&raquo;', false);
199 }
200
201 /**
202 * Sets the description of the document.
203 *
204 * @param string $desc The description to be set.
205 *
206 * @return self This object to support chaining.
207 *
208 * @uses setMetaData()
209 */
210 public function setDescription($desc)
211 {
212 return $this->setMetaData('description', (string) $desc);
213 }
214
215 /**
216 * Return the description of the document.
217 *
218 * @return string
219 *
220 * @uses getMetaData()
221 */
222 public function getDescription()
223 {
224 return $this->getMetaData('description');
225 }
226
227 /**
228 * Adds a linked script to the page.
229 *
230 * @param string $url URL to the linked script.
231 * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9')
232 * @param array $attribs Array of attributes. Example: array('id' => 'scriptid', 'async' => 'async', 'data-test' => 1)
233 *
234 * @return self This object to support chaining.
235 *
236 * @link https://developer.wordpress.org/reference/functions/wp_register_script/
237 */
238 public function addScript($url, $options = [], $attribs = [])
239 {
240 // check if the script should be loaded using WP native libs
241 $version = array_key_exists('version', $options) ? $options['version'] : null;
242 $footer = array_key_exists('footer', $options) ? (bool) $options['footer'] : false;
243 $id = empty($attribs['id']) ? md5($url) : $attribs['id'];
244
245 /**
246 * Use filter to approve/deny the loading of the given script.
247 *
248 * @param boolean $load The recursive filtered value (default 'true').
249 * @param string $url The resource URL.
250 * @param string $id The script ID attribute.
251 * @param string $version The script version, if specified.
252 * @param boolean $footer True whether the script is going to be loaded in the footer.
253 *
254 * @return boolean True to load the resource, false to ignore it.
255 *
256 * @since 10.1.25
257 */
258 $load = apply_filters('vik_before_include_script', true, $url, $id, $version, $footer);
259
260 if ($load)
261 {
262 // make sure this is not an AJAX call
263 if (!wp_doing_ajax())
264 {
265 // if the headers have been sent, the script must be registered in the footer.
266 if (headers_sent())
267 {
268 $footer = true;
269 }
270
271 // the default array of dependencies
272 $deps = [
273 'jquery-core',
274 'jquery-ui-core',
275 ];
276
277 /**
278 * Added support to custom dependencies.
279 *
280 * @since 10.1.38
281 */
282 if (!empty($options['dependencies']))
283 {
284 // join default dependencies with the given ones
285 $deps = array_merge($deps, (array) $options['dependencies']);
286 // get rid of duplicates
287 $deps = array_values(array_unique($deps));
288 }
289
290 // loads scripts always after jQuery Core (included by Wordpress)
291 wp_register_script($id, $url, $deps, $version, $footer);
292 wp_enqueue_script($id);
293 }
294 // since the footer is already printed, we need to each our script directly
295 else if ($url)
296 {
297 if ($version)
298 {
299 $url .= '?ver=' . $version;
300 }
301
302 echo '<script type="text/javascript" src="' . $url . '"></script>';
303 }
304 }
305
306 return $this;
307 }
308
309 /**
310 * Adds a script to the page.
311 *
312 * @param string $content Script snippet.
313 * @param string $type Scripting mime (defaults to 'text/javascript').
314 *
315 * @return self This object to support chaining.
316 *
317 * @uses attachToHead()
318 */
319 public function addScriptDeclaration($content, $type = 'text/javascript')
320 {
321 /**
322 * Always use the script declaration without checking
323 * if it has been already loaded.
324 *
325 * @since 10.1.17
326 */
327 $this->attachToHead(function() use ($content, $type)
328 {
329 /**
330 * Use "joomla-options new" class in case we are attaching a JSON string.
331 * The "new" word means that the options has to be loaded.
332 *
333 * @since 10.1.14
334 */
335 $class = $type == 'application/json' ? ' class="joomla-options new"' : '';
336
337 echo "<script type=\"{$type}\"{$class}>\n{$content}\n</script>\n";
338 });
339
340 return $this;
341 }
342
343 /**
344 * Adds a linked stylesheet to the page.
345 *
346 * @param string $url URL to the linked style sheet.
347 * @param array $options Array of options. Example: array('version' => 'auto', 'conditional' => 'lt IE 9')
348 * @param array $attribs Array of attributes. Example: array('id' => 'stylesheet', 'data-test' => 1)
349 *
350 * @return self This object to support chaining.
351 *
352 * @link https://codex.wordpress.org/Function_Reference/wp_register_style
353 */
354 public function addStyleSheet($url, $options = [], $attribs = [])
355 {
356 $version = array_key_exists('version', $options) ? $options['version'] : null;
357 $media = array_key_exists('media', $options) ? (string) $options['media'] : 'all';
358 $id = empty($attribs['id']) ? md5($url) : $attribs['id'];
359
360 /**
361 * Use filter to approve/deny the loading of the given stylesheet.
362 *
363 * @param boolean $load The recursive filtered value (default 'true').
364 * @param string $url The resource URL.
365 * @param string $id The stylesheet ID attribute.
366 * @param string $version The stylesheet version, if specified.
367 *
368 * @return boolean True to load the resource, false to ignore it.
369 *
370 * @since 10.1.25
371 */
372 $load = apply_filters('vik_before_include_style', true, $url, $id, $version);
373
374 if ($load)
375 {
376 // attach the style to the <head> only if the headers haven't been sent
377 // and if we are not doing an AJAX call
378 if (!headers_sent() && !wp_doing_ajax())
379 {
380 $deps = [];
381
382 /**
383 * Added support to custom dependencies.
384 *
385 * @since 10.1.38
386 */
387 if (!empty($options['dependencies']))
388 {
389 // join default dependencies with the given ones
390 $deps = array_merge($deps, (array) $options['dependencies']);
391 // get rid of duplicates
392 $deps = array_values(array_unique($deps));
393 }
394
395 wp_register_style($id, $url, $deps, $version, $media);
396 wp_enqueue_style($id);
397 }
398 // otherwise print the style in the document <body>
399 else
400 {
401 if ($version)
402 {
403 $url .= '?ver=' . $version;
404 }
405
406 echo '<link rel="stylesheet" id="' . $id . '" href="' . $url . '" type="text/css" media="' . $media . '">';
407 }
408 }
409
410 return $this;
411 }
412
413 /**
414 * Adds a stylesheet declaration to the page.
415 *
416 * @param string $content Style declaration.
417 * @param string $type Type of stylesheet (defaults to 'text/css').
418 *
419 * @return self This object to support chaining.
420 *
421 * @uses attachToHead()
422 */
423 public function addStyleDeclaration($content, $type = 'text/css')
424 {
425 if (!in_array($content, $this->styleDeclarations))
426 {
427 $this->attachToHead(function() use ($content, $type)
428 {
429 echo "<style type=\"{$type}\">\n{$content}\n</style>\n";
430 });
431
432 $this->styleDeclarations[] = $content;
433 }
434
435 return $this;
436 }
437
438 /**
439 * Internal method execute a callback within the <head> tags.
440 * The callback will be attached to the wp_head or admin_head hooks
441 * depending on the section we are currently using.
442 *
443 * @param mixed $callback The callback or the function name to attach.
444 * @param boolean $frontOnly True to attach the method only in the front-end.
445 *
446 * @return void
447 *
448 * @link https://codex.wordpress.org/Plugin_API/Action_Reference/wp_head
449 * @link https://codex.wordpress.org/Plugin_API/Action_Reference/admin_head
450 * @link https://developer.wordpress.org/reference/hooks/wp_print_footer_scripts/
451 * @link https://developer.wordpress.org/reference/hooks/admin_print_footer_scripts/
452 */
453 protected function attachToHead($callback, $frontOnly = false)
454 {
455 $app = JFactory::getApplication();
456
457 // make sure we are in the front-end or the back-end is allowed
458 if ($app->isSite() || !$frontOnly)
459 {
460 /**
461 * If provided, use the custom callback instead.
462 *
463 * @since 10.1.51
464 */
465 if (is_callable($this->attachToHeadCustomCallback))
466 {
467 // invoke callback and teminate
468 return call_user_func_array($this->attachToHeadCustomCallback, [$callback]);
469 }
470
471 // make sure we are not doing an AJAX call
472 if (!wp_doing_ajax())
473 {
474 $head_hook = $app->isAdmin() ? 'admin_head' : 'wp_head';
475
476 // make sure that the head hook that we are using haven't been called yet,
477 // otherwise our script will never be executed
478 if (!headers_sent() && !did_action($head_hook))
479 {
480 // admin_head hook for the back-end <head>
481 // wp_head hook for the front-end <head>
482 add_action($head_hook, $callback);
483 }
484 else
485 {
486 // admin_print_footer_scripts hook for the back-end <footer>
487 // wp_footer wp_print_footer_scripts for the front-end <footer>
488 add_action($app->isAdmin() ? 'admin_print_footer_scripts' : 'wp_print_footer_scripts', $callback);
489
490 /**
491 * NOTE: do not use 'wp_footer' hook because it prints the script declarations
492 * always before the <script> tags, causing errors for missing resources.
493 */
494 }
495 }
496 // we need to invoke our callback directly
497 else
498 {
499 // push scripts within the AJAX callbacks
500 $this->ajaxScripts[] = $callback;
501 }
502 }
503 }
504
505 /**
506 * Returns the AJAX scripts that should be included after
507 * the body in order to properly access all the needed elements.
508 *
509 * @return string
510 *
511 * @since 10.1.29
512 */
513 public function getAjaxScripts()
514 {
515 // start buffering
516 ob_start();
517
518 // iterate registered scripts
519 foreach ($this->ajaxScripts as $callback)
520 {
521 // invoke the callback to print the script
522 call_user_func($callback);
523 }
524
525 // catch buffer
526 $js = ob_get_contents();
527 // clear buffer
528 ob_end_clean();
529
530 // empty list
531 $this->ajaxScripts = [];
532
533 return $js;
534 }
535
536 /**
537 * Add options for script.
538 *
539 * @param string $key Name in Storage.
540 * @param mixed $options Scrip options as array or string.
541 * @param boolean $merge Whether merge with existing (true) or replace (false).
542 *
543 * @return self This object to support chaining.
544 *
545 * @since 10.1.14
546 */
547 public function addScriptOptions($key, $options, $merge = true)
548 {
549 if (empty($this->scriptOptions[$key]))
550 {
551 $this->scriptOptions[$key] = [];
552 }
553
554 if ($merge && is_array($options))
555 {
556 $this->scriptOptions[$key] = array_replace_recursive($this->scriptOptions[$key], $options);
557 }
558 else
559 {
560 $this->scriptOptions[$key] = $options;
561 }
562
563 return $this;
564 }
565
566 /**
567 * Get script(s) options.
568 *
569 * @param string $key Name in Storage.
570 *
571 * @return array Options for given $key, or all script options.
572 *
573 * @since 10.1.14
574 */
575 public function getScriptOptions($key = null)
576 {
577 if ($key)
578 {
579 return (empty($this->scriptOptions[$key])) ? [] : $this->scriptOptions[$key];
580 }
581 else
582 {
583 return $this->scriptOptions;
584 }
585 }
586
587 /**
588 * Returns the document charset encoding.
589 *
590 * @return string
591 *
592 * @since 10.1.20
593 */
594 public function getCharset()
595 {
596 $output = get_option('blog_charset');
597
598 if (!$output)
599 {
600 $output = 'UTF-8';
601 }
602
603 return $output;
604 }
605
606 /**
607 * Returns the document language.
608 *
609 * @return string
610 *
611 * @since 10.1.20
612 */
613 public function getLanguage()
614 {
615 return strtolower(JFactory::getLanguage()->getTag());
616 }
617
618 /**
619 * Returns the document direction declaration.
620 *
621 * @return string
622 *
623 * @since 10.1.20
624 */
625 public function getDirection()
626 {
627 if (function_exists('is_rtl'))
628 {
629 $output = is_rtl() ? 'rtl' : 'ltr';
630 }
631 else
632 {
633 $output = 'ltr';
634 }
635
636 return $output;
637 }
638
639 /**
640 * Adds `<link>` tags to the head of the document.
641 *
642 * $relType defaults to 'rel' as it is the most common relation type used
643 * ('rev' refers to reverse relation, 'rel' indicates normal, forward relation).
644 * Typical tag: `<link href="index.php" rel="Start">`.
645 *
646 * @param string $href The link that is being related.
647 * @param string $relation Relation of link.
648 * @param string $relType Relation type attribute. Either rel or rev (default: 'rel').
649 * @param array $attribs Associative array of remaining attributes.
650 *
651 * @return self This object to support chaining.
652 *
653 * @since 10.1.48
654 */
655 public function addHeadLink(string $href, string $relation, string $relType = 'rel', array $attribs = [])
656 {
657 $this->attachToHead(function() use ($href, $relation, $relType, $attribs)
658 {
659 JLoader::import('adapter.utilities.array');
660 echo "<link href=\"{$href}\" {$relType}=\"$relation\" " . ArrayHelper::toString($attribs) . " />\n";
661 });
662
663 return $this;
664 }
665 }
666