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