PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.7.2
VikBooking Hotel Booking Engine & PMS v1.7.2
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 / adapter / application / route.php

route.php in VikBooking Hotel Booking Engine & PMS 1.7.2, at libraries/adapter/application/route.php

470 lines 11.8 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 * Route adapter class.
16 *
17 * @since 10.0
18 */
19 class JRoute
20 {
21 /**
22 * A list of component shortcodes.
23 *
24 * @var array
25 */
26 protected static $shortcodes = array();
27
28 /**
29 * Translates an internal URL to a human-readable URL.
30 *
31 * @param string $url Absolute or Relative URI.
32 * @param boolean $xhtml Replace & by &amp; for XML compliance.
33 * @param integer $ssl Secure state for the resolved URI.
34 * 0: (default) No change, use the protocol currently used in the request.
35 * 1: Make URI secure using global secure site URI.
36 * 2: Make URI unsecure using the global unsecure site URI.
37 *
38 * @return string The translated humanly readable URL.
39 *
40 * @uses getPermalink()
41 * @uses replace()
42 */
43 public static function _($url, $xhtml = true, $ssl = null)
44 {
45 // parse URL to obtain query args (true: as array)
46 $args = JUri::getInstance($url)->getQuery(true);
47
48 // prepend current URI if starts with 'index.php'
49 if (strpos($url, 'index.php') === 0)
50 {
51 $url = static::getPermalink();
52 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($args);
53 }
54
55 // if 'option' and 'view' arguments are not empty, try to route URLs
56 if (!empty($args['option']) && (!empty($args['view']) || !empty($args['Itemid'])))
57 {
58 $url = static::replace($args, $url);
59 }
60
61 /**
62 * Prepend base URL in case the system
63 * was not able to find a permalink.
64 *
65 * @since 10.1.29
66 */
67 if (preg_match("/^\?/", $url))
68 {
69 $url = JUri::root() . $url;
70 }
71
72 // replace & with &amp; for XML compliance
73 if ($xhtml)
74 {
75 $url = str_replace('&', '&amp;', $url);
76 }
77
78 if ($ssl == 1)
79 {
80 // force HTTPS
81 $url = str_replace('http://', 'https://', $url);
82 }
83 else if ($ssl == 2)
84 {
85 // force HTTP
86 $url = str_replace('https://', 'http://', $url);
87 }
88
89 return $url;
90 }
91
92 /**
93 * Proxy for underscore method.
94 * Needed to bypass the issue reported here:
95 * @link https://meta.trac.wordpress.org/ticket/3601
96 *
97 * @since 10.1.32 Renamed from `u`.
98 */
99 public static function rewrite($url, $xhtml = true, $ssl = null)
100 {
101 return static::_($url, $xhtml, $ssl);
102 }
103
104 /**
105 * Replaces the plain URL with the rewritten one.
106 *
107 * @param array $args The URL parts.
108 * @param string $url The URL to rewrite.
109 *
110 * @return string The rewritten URL, if possible.
111 *
112 * @uses getPermalink()
113 * @uses matchShortcode()
114 * @uses withQueryString()
115 */
116 protected static function replace(array $args, $url)
117 {
118 // make safe by replacing initial 'com_' (if any)
119 $option = preg_replace("/^com_/", "", $args['option']);
120 unset($args['option']);
121
122 // check if the shortcodes have been already cached
123 if (!isset(static::$shortcodes[$option]))
124 {
125 // get model to access shortcodes
126 $model = JModel::getInstance($option, 'shortcodes', 'admin');
127
128 /**
129 * Make sure the model exists for the requested option.
130 *
131 * @since 10.1.19
132 */
133 if ($model)
134 {
135 // cache the component shortcodes
136 static::$shortcodes[$option] = $model->all();
137 }
138 else
139 {
140 // create an empty list
141 static::$shortcodes[$option] = array();
142 }
143 }
144
145 $post_id = false;
146
147 if (!empty($args['view']))
148 {
149 // try to rewrite the URL with a matching shortcode
150 $post_id = static::matchShortcode(static::$shortcodes[$option], $args);
151 }
152
153 if (!$post_id && !empty($args['Itemid']))
154 {
155 // if the Itemid is set, get the POST permalink directly
156 $post_id = (int) $args['Itemid'];
157 }
158
159 if ($post_id)
160 {
161 $url = static::getPermalink($post_id);
162 $url = static::withQueryString($url, $post_id, static::$shortcodes[$option], $args);
163 }
164
165 /**
166 * Try to instantiate an external router.
167 *
168 * @since 10.1.19
169 */
170 $router = JFactory::getApplication()->getRouter($option);
171
172 if ($router)
173 {
174 // route URL using the specified arguments
175 $url = $router->build($url);
176 }
177
178 return $url;
179 }
180
181 /**
182 * Wrapper method to extend the function used to obtain
183 * the current permalink or the post link.
184 *
185 * @param mixed $post_id The post ID. Null to obtain the current link.
186 *
187 * @return string The permalink.
188 */
189 protected static function getPermalink($post_id = null)
190 {
191 // if the post ID is set, get the permalink directly
192 if ($post_id)
193 {
194 /**
195 * Added support to WPML, since it is unable to properly obtain the
196 * correct permalink depending the requested language tag.
197 *
198 * @since 10.1.55
199 */
200 $wpml_post_lang = apply_filters('wpml_post_language_details', null, $post_id);
201
202 if ($wpml_post_lang)
203 {
204 // WPML supported, go ahead
205 return apply_filters('wpml_permalink', get_permalink($post_id), $wpml_post_lang['language_code']);
206 }
207
208 // WPML not supported, fallback to the default behavior
209 return get_permalink($post_id);
210 }
211
212 // otherwise get the current permalink
213 $link = get_permalink();
214
215 // return the permalink only if it is not empty
216 if ($link)
217 {
218 return $link;
219 }
220
221 // obtain the post ID from the current URL
222 $post_id = url_to_postid(JUri::current());
223
224 // if the current URL doesn't match any post, return an empty string
225 if (!$post_id)
226 {
227 return '';
228 }
229
230 // recursive call to obtain the permalink of the post found
231 return static::getPermalink($post_id);
232 }
233
234 /**
235 * Returns a URL containing the specified query string.
236 *
237 * @param string $url The URL to which append the query string.
238 * @param integer $post_id The post ID.
239 * @param array $shortcodes The shortcodes list.
240 * @param array $args The args to build the query string.
241 *
242 * @return string The URL with the query string.
243 */
244 protected static function withQueryString($url, $post_id, array $shortcodes, array $args = array())
245 {
246 // unset always the Item ID
247 unset($args['Itemid']);
248
249 // iterate the shortcodes
250 foreach ($shortcodes as $shortcode)
251 {
252 // process only if the given post ID matches the one assigned to the shortcode
253 if ($post_id == $shortcode->post_id)
254 {
255 // get shortcodes parameters, view and language
256 $json = (array) json_decode($shortcode->json, true);
257 $json['view'] = $shortcode->type;
258 $json['lang'] = $shortcode->lang;
259
260 // get the keys that the shortcode doesn't contain
261 $diff = self::diff($args, $json);
262
263 // if there are differences between the args, append them to the URL
264 if (count($diff))
265 {
266 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($diff);
267 }
268
269 // return the URL with the query string (do not proceed)
270 return $url;
271 }
272 }
273
274 if (count($args))
275 {
276 /**
277 * if no shortcodes found, attach the whole query string
278 *
279 * @since 10.1.8
280 */
281 $url .= (strpos($url, '?') !== false ? '&' : '?') . http_build_query($args);
282 }
283
284 // nothing found, return plain URL
285 return $url;
286 }
287
288 /**
289 * Checks if there is a shortcode object that matches the given URL parts.
290 * In addition, the shortcode MUST own an existing post ID.
291 *
292 * @param array $shortcodes The shortcode objects list.
293 * @param array $args The URL query parts.
294 *
295 * @return mixed The post ID found, false on failure.
296 */
297 protected static function matchShortcode($shortcodes, array $args)
298 {
299 $post_id = false;
300 $last_count = 0;
301
302 if (empty($args['lang']))
303 {
304 // inject current langtag within URL arguments, only if not specified
305 // by query string
306 $args['lang'] = JFactory::getLanguage()->getTag();
307 }
308
309 foreach ($shortcodes as $shortcode)
310 {
311 $count = 0;
312
313 // decode JSON data
314 $json = json_decode($shortcode->json, true);
315 // inject VIEW name
316 $json['view'] = $shortcode->type;
317 // inject shortcode LANGTAG
318 $json['lang'] = $shortcode->lang;
319
320 // proceed only if the view is the same
321 if ($json['view'] == $args['view'])
322 {
323 // iterate the URL arguments
324 foreach ($args as $k => $v)
325 {
326 // the property should be matched only if the shortcode
327 // owns a parameter with the same name (defined in xml view)
328 // and value.
329 if (isset($json[$k]))
330 {
331 /**
332 * Improved the comparison of the language tag, which might be
333 * handled in different ways from different sections of WP.
334 *
335 * @since 10.1.38
336 */
337 if ($k === 'lang')
338 {
339 // check whether at least a language code doesn't mention the country code
340 if (!preg_match("/[_\-]/", $json[$k]) || !preg_match("/[_\-]/", $v))
341 {
342 // remove country code from locale assigned to the shortcode
343 $json[$k] = preg_split("/[_\-]/", $json[$k]);
344 $json[$k] = array_shift($json[$k]);
345
346 // remove country code from URL locale
347 $v = preg_split("/[_\-]/", $v);
348 $v = array_shift($v);
349 }
350
351 // normalize locale separators
352 $json[$k] = str_replace('-', '_', $json[$k]);
353 $v = str_replace('-', '_', $v);
354 }
355
356 // make sure both the values are scalar
357 if (is_scalar($json[$k]) && is_scalar($v))
358 {
359 if (!strcmp($json[$k], $v))
360 {
361 // if the shortcode owns the parameter and it is equals
362 // to the one specified in the URL, increase the counter
363 $count++;
364 }
365 }
366 /**
367 * Added support for arrays in query string, such as cid[].
368 *
369 * @since 10.1.27
370 */
371 else
372 {
373 // treat both the arguments as arrays
374 $json[$k] = (array) $json[$k];
375 $v = (array) $v;
376
377 // sort them to maximize the possibility of having matching URLs
378 sort($json[$k]);
379 sort($v);
380
381 if ($json[$k] == $v)
382 {
383 // increase the counter as we have a matching parameter
384 $count++;
385 }
386 }
387 }
388 }
389
390 // if the count of matches is higher than the last one,
391 // update the flags with the shortcode post ID and the current count
392 if ($count > $last_count)
393 {
394 $post_id = $shortcode->post_id;
395 $last_count = $count;
396 }
397 }
398 }
399
400 return $post_id;
401 }
402
403 /**
404 * Implementation of array_diff_assoc with recursion.
405 *
406 * @param array array1 The array to compare from.
407 * @param array array2 An array to compare against.
408 *
409 * @return array Returns an array containing all the values from array1
410 * that are not present in the second array.
411 *
412 * @since 10.1.27
413 */
414 private static function diff($array1, $array2)
415 {
416 $difference = array();
417
418 foreach ($array1 as $key => $value)
419 {
420 /**
421 * Improved the comparison of the language tag, which might be
422 * handled in different ways from different sections of WP.
423 *
424 * @since 10.1.49
425 */
426 if ($key === 'lang')
427 {
428 // check whether at least a language code doesn't mention the country code
429 if (!preg_match("/[_\-]/", $value) || !preg_match("/[_\-]/", $array2[$key]))
430 {
431 // remove country code from locale assigned to the shortcode
432 $array2[$key] = preg_split("/[_\-]/", $array2[$key]);
433 $array2[$key] = array_shift($array2[$key]);
434
435 // remove country code from URL locale
436 $value = preg_split("/[_\-]/", $value);
437 $value = array_shift($value);
438 }
439
440 // normalize locale separators
441 $array2[$key] = str_replace('-', '_', $array2[$key]);
442 $value = str_replace('-', '_', $value);
443 }
444
445 if (is_array($value))
446 {
447 if (!isset($array2[$key]) || !is_array($array2[$key]))
448 {
449 $difference[$key] = $value;
450 }
451 else
452 {
453 $new_diff = self::diff($value, $array2[$key]);
454
455 if (!empty($new_diff))
456 {
457 $difference[$key] = $new_diff;
458 }
459 }
460 }
461 else if (!array_key_exists($key, $array2) || $array2[$key] !== $value)
462 {
463 $difference[$key] = $value;
464 }
465 }
466
467 return $difference;
468 }
469 }
470