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/application/document.php +665 -0 1.6.3 → 1.8.16 View file →
@@ -1,0 +1,665 @@
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 +}