1 <?php
2 3 4 5 6 7 8
9
10 defined('JPATH_PLATFORM') or die;
11
12 use Joomla\String\StringHelper;
13
14 15 16 17 18
19 class JRouterSite extends JRouter
20 {
21 22 23 24 25 26
27 protected $componentRouters = array();
28
29 30 31 32 33 34
35 protected $app;
36
37 38 39 40 41 42
43 protected ;
44
45 46 47 48 49 50 51 52 53
54 public function __construct($options = array(), JApplicationCms $app = null, JMenu $menu = null)
55 {
56 parent::__construct($options);
57
58 $this->app = $app ?: JApplicationCms::getInstance('site');
59 $this->menu = $menu ?: $this->app->getMenu();
60 }
61
62 63 64 65 66 67 68 69 70
71 public function parse(&$uri)
72 {
73 $vars = array();
74
75 if ($this->app->get('force_ssl') == 2 && strtolower($uri->getScheme()) !== 'https')
76 {
77
78 $uri->setScheme('https');
79 $this->app->redirect((string) $uri, 301);
80 }
81
82
83
84 $path = urldecode($uri->getPath());
85
86
87 $path = substr_replace($path, '', 0, strlen(JUri::base(true)));
88
89
90 if (preg_match("#.*?\.php#u", $path, $matches))
91 {
92
93 $scriptPath = realpath($_SERVER['SCRIPT_FILENAME'] ?: str_replace('\\\\', '\\', $_SERVER['PATH_TRANSLATED']));
94 $relativeScriptPath = str_replace('\\', '/', str_replace(JPATH_SITE, '', $scriptPath));
95
96
97
98 if (file_exists(JPATH_SITE . $matches[0]) && ($matches[0] === $relativeScriptPath))
99 {
100
101 $path = str_replace($matches[0], '', $path);
102 }
103 }
104
105
106 if ($this->_mode == JROUTER_MODE_SEF)
107 {
108 if ($this->app->get('sef_suffix') && !(substr($path, -9) === 'index.php' || substr($path, -1) === '/'))
109 {
110 if ($suffix = pathinfo($path, PATHINFO_EXTENSION))
111 {
112 $vars['format'] = $suffix;
113 }
114 }
115 }
116
117
118 $uri->setPath(trim($path, '/'));
119
120
121 $components = JComponentHelper::getComponents();
122
123 foreach ($components as $component)
124 {
125 $componentRouter = $this->getComponentRouter($component->option);
126
127 if (method_exists($componentRouter, 'parsepreprocess'))
128 {
129 $this->attachParseRule(array($componentRouter, 'parsepreprocess'), static::PROCESS_BEFORE);
130 }
131 }
132
133 $vars += parent::parse($uri);
134
135 return $vars;
136 }
137
138 139 140 141 142 143 144 145 146
147 public function build($url)
148 {
149 $uri = parent::build($url);
150
151
152 $route = $uri->getPath();
153
154
155 if ($this->_mode == JROUTER_MODE_SEF && $route)
156 {
157 if ($this->app->get('sef_suffix') && !(substr($route, -9) === 'index.php' || substr($route, -1) === '/'))
158 {
159 if ($format = $uri->getVar('format', 'html'))
160 {
161 $route .= '.' . $format;
162 $uri->delVar('format');
163 }
164 }
165
166 if ($this->app->get('sef_rewrite'))
167 {
168
169 if ($route === 'index.php')
170 {
171 $route = '';
172 }
173 else
174 {
175 $route = str_replace('index.php/', '', $route);
176 }
177 }
178 }
179
180
181 $uri->setPath(JUri::base(true) . '/' . $route);
182
183 return $uri;
184 }
185
186 187 188 189 190 191 192 193 194 195
196 protected function parseRawRoute(&$uri)
197 {
198 $vars = array();
199
200
201 if (!$uri->getVar('Itemid') && !$uri->getVar('option'))
202 {
203 $item = $this->menu->getDefault($this->app->getLanguage()->getTag());
204
205 if (!is_object($item))
206 {
207
208 return $vars;
209 }
210
211
212 $vars = $item->query;
213
214
215 $vars['Itemid'] = $item->id;
216
217
218 $this->menu->setActive($vars['Itemid']);
219
220 return $vars;
221 }
222
223
224 $this->setVars($uri->getQuery(true));
225
226
227 $this->setVar('Itemid', $this->app->input->getInt('Itemid', null));
228
229
230 if (count($this->getVars()) === 1 || ($this->app->getLanguageFilter() && count($this->getVars()) === 2))
231 {
232 $item = $this->menu->getItem($this->getVar('Itemid'));
233
234 if ($item && $item->type == 'alias')
235 {
236 $newItem = $this->menu->getItem($item->params->get('aliasoptions'));
237
238 if ($newItem)
239 {
240 $item->query = array_merge($item->query, $newItem->query);
241 $item->component = $newItem->component;
242 }
243 }
244
245 if ($item !== null && is_array($item->query))
246 {
247 $vars += $item->query;
248 }
249 }
250
251
252 $this->menu->setActive($this->getVar('Itemid'));
253
254 return $vars;
255 }
256
257 258 259 260 261 262 263 264 265 266
267 protected function parseSefRoute(&$uri)
268 {
269 $route = $uri->getPath();
270
271
272 if ($this->app->get('sef_suffix'))
273 {
274 if ($suffix = pathinfo($route, PATHINFO_EXTENSION))
275 {
276 $route = str_replace('.' . $suffix, '', $route);
277 }
278 }
279
280
281 $vars = $uri->getQuery(true);
282
283
284 if (empty($route))
285 {
286
287 if (isset($vars['option']) || isset($vars['Itemid']))
288 {
289 return $this->parseRawRoute($uri);
290 }
291
292 $item = $this->menu->getDefault($this->app->getLanguage()->getTag());
293
294
295 if (is_object($item))
296 {
297
298 $vars = $item->query;
299
300
301 $vars['Itemid'] = $item->id;
302
303
304 $this->menu->setActive($vars['Itemid']);
305
306 $this->setVars($vars);
307 }
308
309 return $vars;
310 }
311
312
313 $segments = explode('/', $route);
314
315 if (count($segments) > 1 && $segments[0] === 'component')
316 {
317 $vars['option'] = 'com_' . $segments[1];
318 $vars['Itemid'] = null;
319 $route = implode('/', array_slice($segments, 2));
320 }
321 else
322 {
323
324 $items = $this->menu->getMenu();
325
326 $found = false;
327 $route_lowercase = StringHelper::strtolower($route);
328 $lang_tag = $this->app->getLanguage()->getTag();
329
330
331 foreach ($items as $item)
332 {
333 if ($item->route && StringHelper::strpos($route_lowercase . '/', $item->route . '/') === 0 && $item->type !== 'menulink')
334 {
335
336 if (!$this->app->getLanguageFilter())
337 {
338
339 if ($item->route === $route_lowercase)
340 {
341 $found = $item;
342 break;
343 }
344
345
346 if (!$found || $found->level < $item->level)
347 {
348 $found = $item;
349 }
350 }
351
352 elseif ($item->language === '*' || $item->language === $lang_tag)
353 {
354
355 if ($item->route === $route_lowercase)
356 {
357 $found = $item;
358
359
360 if ($item->language === $lang_tag)
361 {
362 break;
363 }
364 }
365
366
367 if (!$found || $found->level < $item->level || $item->language === $lang_tag)
368 {
369 $found = $item;
370 }
371 }
372 }
373 }
374
375 if (!$found)
376 {
377 $found = $this->menu->getDefault($lang_tag);
378 }
379 else
380 {
381 $route = substr($route, strlen($found->route));
382
383 if ($route)
384 {
385 $route = substr($route, 1);
386 }
387 }
388
389 if ($found)
390 {
391 if ($found->type == 'alias')
392 {
393 $newItem = $this->menu->getItem($found->params->get('aliasoptions'));
394
395 if ($newItem)
396 {
397 $found->query = array_merge($found->query, $newItem->query);
398 $found->component = $newItem->component;
399 }
400 }
401
402 $vars['Itemid'] = $found->id;
403 $vars['option'] = $found->component;
404 }
405 }
406
407
408 if (isset($vars['Itemid']))
409 {
410 $this->menu->setActive($vars['Itemid']);
411 }
412
413
414 $this->setVars($vars);
415
416
417 if (!empty($route) && isset($this->_vars['option']))
418 {
419 $segments = explode('/', $route);
420
421 if (empty($segments[0]))
422 {
423 array_shift($segments);
424 }
425
426
427 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $this->_vars['option']);
428
429 if (count($segments))
430 {
431 $crouter = $this->getComponentRouter($component);
432 $vars = $crouter->parse($segments);
433
434 $this->setVars($vars);
435 }
436 }
437 else
438 {
439
440 if ($item = $this->menu->getActive())
441 {
442 $vars = $item->query;
443 }
444 }
445
446 return $vars;
447 }
448
449 450 451 452 453 454 455 456 457 458
459 protected function buildRawRoute(&$uri)
460 {
461
462 $query = $uri->getQuery(true);
463
464 if (!isset($query['option']))
465 {
466 return;
467 }
468
469 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']);
470 $crouter = $this->getComponentRouter($component);
471 $query = $crouter->preprocess($query);
472
473 $uri->setQuery($query);
474 }
475
476 477 478 479 480 481 482 483 484 485 486
487 protected function _buildSefRoute(&$uri)
488 {
489 $this->buildSefRoute($uri);
490 }
491
492 493 494 495 496 497 498 499 500 501
502 protected function buildSefRoute(&$uri)
503 {
504
505 $route = $uri->getPath();
506
507
508 $query = $uri->getQuery(true);
509
510 if (!isset($query['option']))
511 {
512 return;
513 }
514
515
516 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']);
517 $itemID = !empty($query['Itemid']) ? $query['Itemid'] : null;
518 $crouter = $this->getComponentRouter($component);
519 $parts = $crouter->build($query);
520 $result = implode('/', $parts);
521 $tmp = ($result !== '') ? $result : '';
522
523
524 $built = false;
525
526 if (!empty($query['Itemid']))
527 {
528 $item = $this->menu->getItem($query['Itemid']);
529
530 if (is_object($item) && $query['option'] === $item->component)
531 {
532 if (!$item->home)
533 {
534 $tmp = !empty($tmp) ? $item->route . '/' . $tmp : $item->route;
535 }
536
537 $built = true;
538 }
539 }
540
541 if (empty($query['Itemid']) && !empty($itemID))
542 {
543 $query['Itemid'] = $itemID;
544 }
545
546 if (!$built)
547 {
548 $tmp = 'component/' . substr($query['option'], 4) . '/' . $tmp;
549 }
550
551 if ($tmp)
552 {
553 $route .= '/' . $tmp;
554 }
555
556
557 if (isset($item) && $query['option'] === $item->component)
558 {
559 unset($query['Itemid']);
560 }
561
562 unset($query['option']);
563
564
565 $uri->setQuery($query);
566 $uri->setPath($route);
567 }
568
569 570 571 572 573 574 575 576 577 578 579 580
581 protected function processParseRules(&$uri, $stage = self::PROCESS_DURING)
582 {
583
584 $vars = parent::processParseRules($uri, $stage);
585
586 if ($stage === self::PROCESS_DURING)
587 {
588
589 if ($this->_mode == JROUTER_MODE_SEF)
590 {
591 if ($start = $uri->getVar('start'))
592 {
593 $uri->delVar('start');
594 $vars['limitstart'] = $start;
595 }
596 }
597 }
598
599 return $vars;
600 }
601
602 603 604 605 606 607 608 609 610 611 612 613 614
615 protected function processBuildRules(&$uri, $stage = self::PROCESS_DURING)
616 {
617 if ($stage === self::PROCESS_DURING)
618 {
619
620 $query = $uri->getQuery(true);
621 if ($this->_mode != 1
622 && isset($query['Itemid'])
623 && (count($query) === 2 || (count($query) === 3 && isset($query['lang']))))
624 {
625
626 $itemid = $uri->getVar('Itemid');
627 $lang = $uri->getVar('lang');
628 $item = $this->menu->getItem($itemid);
629
630 if ($item)
631 {
632 $uri->setQuery($item->query);
633 }
634
635 $uri->setVar('Itemid', $itemid);
636
637 if ($lang)
638 {
639 $uri->setVar('lang', $lang);
640 }
641 }
642 }
643
644
645 parent::processBuildRules($uri, $stage);
646
647 if ($stage === self::PROCESS_BEFORE)
648 {
649
650 $query = $uri->getQuery(true);
651
652 if (!isset($query['option']))
653 {
654 return;
655 }
656
657
658 $component = preg_replace('/[^A-Z0-9_\.-]/i', '', $query['option']);
659 $router = $this->getComponentRouter($component);
660 $query = $router->preprocess($query);
661 $uri->setQuery($query);
662 }
663
664 if ($stage === self::PROCESS_DURING)
665 {
666
667 $route = $uri->getPath();
668
669 if ($this->_mode == JROUTER_MODE_SEF && $route)
670 {
671 if ($limitstart = $uri->getVar('limitstart'))
672 {
673 $uri->setVar('start', (int) $limitstart);
674 $uri->delVar('limitstart');
675 }
676 }
677
678 $uri->setPath($route);
679 }
680 }
681
682 683 684 685 686 687 688 689 690
691 protected function createUri($url)
692 {
693
694 $uri = parent::createUri($url);
695
696
697 $itemid = $uri->getVar('Itemid');
698
699 if ($itemid === null)
700 {
701 if ($option = $uri->getVar('option'))
702 {
703 $item = $this->menu->getItem($this->getVar('Itemid'));
704
705 if (isset($item) && $item->component === $option)
706 {
707 $uri->setVar('Itemid', $item->id);
708 }
709 }
710 else
711 {
712 if ($option = $this->getVar('option'))
713 {
714 $uri->setVar('option', $option);
715 }
716
717 if ($itemid = $this->getVar('Itemid'))
718 {
719 $uri->setVar('Itemid', $itemid);
720 }
721 }
722 }
723 else
724 {
725 if (!$uri->getVar('option'))
726 {
727 if ($item = $this->menu->getItem($itemid))
728 {
729 $uri->setVar('option', $item->component);
730 }
731 }
732 }
733
734 return $uri;
735 }
736
737 738 739 740 741 742 743 744 745
746 public function getComponentRouter($component)
747 {
748 if (!isset($this->componentRouters[$component]))
749 {
750 $compname = ucfirst(substr($component, 4));
751 $class = $compname . 'Router';
752
753 if (!class_exists($class))
754 {
755
756 $path = JPATH_SITE . '/components/' . $component . '/router.php';
757
758
759 if (file_exists($path))
760 {
761 require_once $path;
762 }
763 }
764
765 if (class_exists($class))
766 {
767 $reflection = new ReflectionClass($class);
768
769 if (in_array('JComponentRouterInterface', $reflection->getInterfaceNames()))
770 {
771 $this->componentRouters[$component] = new $class($this->app, $this->menu);
772 }
773 }
774
775 if (!isset($this->componentRouters[$component]))
776 {
777 $this->componentRouters[$component] = new JComponentRouterLegacy($compname);
778 }
779 }
780
781 return $this->componentRouters[$component];
782 }
783
784 785 786 787 788 789 790 791 792 793
794 public function setComponentRouter($component, $router)
795 {
796 $reflection = new ReflectionClass($router);
797
798 if (in_array('JComponentRouterInterface', $reflection->getInterfaceNames()))
799 {
800 $this->componentRouters[$component] = $router;
801
802 return true;
803 }
804 else
805 {
806 return false;
807 }
808 }
809 }
810