PageRenderTime 56ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

/e107_handlers/application.php

https://github.com/CasperGemini/e107
PHP | 4498 lines | 2653 code | 548 blank | 1297 comment | 437 complexity | 6bafe22ce7e315584503a185db6cb42e MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /*
  3. * e107 website system
  4. *
  5. * Copyright (C) 2008-2012 e107 Inc (e107.org)
  6. * Released under the terms and conditions of the
  7. * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
  8. *
  9. * $URL$
  10. * $Id$
  11. */
  12. /**
  13. * @package e107
  14. * @subpackage e107_handlers
  15. * @version $Id$
  16. * @author SecretR
  17. *
  18. * e107 Single Entry Point handling
  19. *
  20. * Currently this file contains all classes required for single entry point functionallity
  21. * They will be separated in different files in a proper way (soon)
  22. */
  23. /**
  24. * e107 Front controller
  25. */
  26. class eFront
  27. {
  28. /**
  29. * Singleton instance
  30. * @var eFront
  31. */
  32. private static $_instance;
  33. /**
  34. * @var eDispatcher
  35. */
  36. protected $_dispatcher;
  37. /**
  38. * @var eRequest
  39. */
  40. protected $_request;
  41. /**
  42. * @var eRouter
  43. */
  44. protected $_router;
  45. /**
  46. * @var string path to file to include - the old deprecated way of delivering content
  47. */
  48. protected static $_legacy = '';
  49. /**
  50. * Constructor
  51. */
  52. private function __construct()
  53. {
  54. }
  55. /**
  56. * Cloning not allowed
  57. *
  58. */
  59. private function __clone()
  60. {
  61. }
  62. /**
  63. * Singleton implementation
  64. * @return eFront
  65. */
  66. public static function instance()
  67. {
  68. if(null == self::$_instance)
  69. {
  70. self::$_instance = new self();
  71. }
  72. return self::$_instance;
  73. }
  74. /**
  75. * Dispatch
  76. */
  77. public function dispatch(eRequest $request = null, eResponse $response = null, eDispatcher $dispatcher = null)
  78. {
  79. if(null === $request)
  80. {
  81. if(null === $this->getRequest())
  82. {
  83. $request = new eRequest();
  84. $this->setRequest($request);
  85. }
  86. else $request = $this->getRequest();
  87. }
  88. elseif(null === $this->getRequest()) $this->setRequest($request);
  89. if(null === $response)
  90. {
  91. if(null === $this->getResponse())
  92. {
  93. $response = new eResponse();
  94. $this->setResponse($response);
  95. }
  96. else $response = $this->getResponse();
  97. }
  98. elseif(null === $this->getRequest()) $this->setRequest($request);
  99. if(null === $dispatcher)
  100. {
  101. if(null === $this->getDispatcher())
  102. {
  103. $dispatcher = new eDispatcher();
  104. $this->setDispatcher($dispatcher);
  105. }
  106. else $dispatcher = $this->getDispatcher();
  107. }
  108. elseif(null === $this->getDispatcher()) $this->setDispatcher($dispatcher);
  109. // set dispatched status true, required for checkLegacy()
  110. $request->setDispatched(true);
  111. $router = $this->getRouter();
  112. // If current request not already routed outside the dispatch method, route it
  113. if(!$request->routed) $router->route($request);
  114. $c = 0;
  115. // dispatch loop
  116. do
  117. {
  118. $c++;
  119. if($c > 100)
  120. {
  121. throw new eException("Too much dispatch loops", 1);
  122. }
  123. // dispatched status true on first loop
  124. $router->checkLegacy($request);
  125. // dispatched by default - don't allow legacy to alter dispatch status
  126. $request->setDispatched(true);
  127. // legacy mod - return control to the bootstrap
  128. if(self::isLegacy())
  129. {
  130. return;
  131. }
  132. // for the good players - dispatch loop - no more BC!
  133. try
  134. {
  135. $dispatcher->dispatch($request, $response);
  136. }
  137. catch(eException $e)
  138. {
  139. echo $request->getRoute().' - '.$e->getMessage();
  140. exit;
  141. }
  142. } while (!$request->isDispatched());
  143. }
  144. /**
  145. * Init all objects required for request dispatching
  146. * @return eFront
  147. */
  148. public function init()
  149. {
  150. $request = new eRequest();
  151. $this->setRequest($request);
  152. $dispatcher = new eDispatcher();
  153. $this->setDispatcher($dispatcher);
  154. $router = new eRouter();
  155. $this->setRouter($router);
  156. $response = new eResponse();
  157. $this->setResponse($response);
  158. return $this;
  159. }
  160. /**
  161. * Dispatch
  162. * @param string|eRequest $route
  163. */
  164. public function run($route = null)
  165. {
  166. if($route)
  167. {
  168. if(is_object($route) && ($route instanceof eRequest)) $this->setRequest($route);
  169. elseif(null !== $route && null !== $this->getRequest()) $this->getRequest()->setRoute($route);
  170. }
  171. try
  172. {
  173. $this->dispatch();
  174. }
  175. catch(eException $e)
  176. {
  177. echo $e->getMessage();
  178. exit;
  179. }
  180. }
  181. /**
  182. * Application instance (e107 class)
  183. * @return e107
  184. */
  185. public static function app()
  186. {
  187. return e107::getInstance();
  188. }
  189. /**
  190. * Get dispatcher instance
  191. * @return eDispatcher
  192. */
  193. public function getDispatcher()
  194. {
  195. return $this->_dispatcher;
  196. }
  197. /**
  198. * Set dispatcher
  199. * @param eDispatcher $dispatcher
  200. * @return eFront
  201. */
  202. public function setDispatcher(eDispatcher $dispatcher)
  203. {
  204. $this->_dispatcher = $dispatcher;
  205. return $this;
  206. }
  207. /**
  208. * Get request instance
  209. * @return eRequest
  210. */
  211. public function getRequest()
  212. {
  213. return $this->_request;
  214. }
  215. /**
  216. * Set request
  217. * @param eRequest $request
  218. * @return eFront
  219. */
  220. public function setRequest(eRequest $request)
  221. {
  222. $this->_request = $request;
  223. return $this;
  224. }
  225. /**
  226. * Get response instance
  227. * @return eResponse
  228. */
  229. public function getResponse()
  230. {
  231. return $this->_response;
  232. }
  233. /**
  234. * Set response
  235. * @param eResponse $response
  236. * @return eFront
  237. */
  238. public function setResponse(eResponse $response)
  239. {
  240. $this->_response = $response;
  241. return $this;
  242. }
  243. /**
  244. * Get router instance
  245. * @return eRouter
  246. */
  247. public function getRouter()
  248. {
  249. return $this->_router;
  250. }
  251. /**
  252. * Set router instance
  253. * @return eFront
  254. */
  255. public function setRouter(eRouter $router)
  256. {
  257. $this->_router = $router;
  258. return $this;
  259. }
  260. /**
  261. * Set/get legacy status of the current request
  262. * @param boolean $status
  263. * @return boolean
  264. */
  265. public static function isLegacy($status = null)
  266. {
  267. if(null !== $status)
  268. {
  269. if($status[0] === '{')
  270. {
  271. $status = e107::getParser()->replaceConstants($status);
  272. }
  273. self::$_legacy = $status;
  274. }
  275. return self::$_legacy;
  276. }
  277. }
  278. /**
  279. * e107 Dispatcher
  280. * It decides how to dispatch the request.
  281. */
  282. class eDispatcher
  283. {
  284. protected static $_configObjects = array();
  285. public function dispatch(eRequest $request = null, eResponse $response = null)
  286. {
  287. $controllerName = $request->getControllerName();
  288. $moduleName = $request->getModuleName();
  289. $className = $this->isDispatchable($request, false);
  290. // dispatch based on rule settings
  291. if(!$className)
  292. {
  293. if($controllerName == 'index') // v2.x upgrade has not been run yet.
  294. {
  295. e107::getRedirect()->redirect(e_ADMIN."admin.php");
  296. }
  297. throw new eException("Invalid controller '".$controllerName."'");
  298. }
  299. $controller = new $className($request, $response);
  300. if(!($controller instanceof eController))
  301. {
  302. throw new eException("Controller $controller is not an instance of eController");
  303. }
  304. $actionName = $request->getActionMethodName();
  305. ob_start();
  306. $controller->dispatch($actionName);
  307. $content = ob_get_contents();
  308. ob_end_clean();
  309. $response->appendBody($content);
  310. unset($controller);
  311. }
  312. /**
  313. * Get path to the e_url handler
  314. * @param string $module
  315. * @param string $location plugin|core|override
  316. * @param boolean $sc
  317. * @return string path
  318. */
  319. public static function getConfigPath($module, $location, $sc = false)
  320. {
  321. $tmp = explode('/', $location);
  322. $custom = '';
  323. $location = $tmp[0];
  324. if(isset($tmp[1]) && !empty($tmp[1]))
  325. {
  326. $custom = $tmp[1].'_';
  327. }
  328. unset($tmp);
  329. if($module !== '*') $module .= '/';
  330. switch ($location)
  331. {
  332. case 'plugin':
  333. //if($custom) $custom = 'url/'.$custom;
  334. return $sc ? '{e_PLUGIN}'.$module.'url/'.$custom.'url.php' : e_PLUGIN.$module.'url/'.$custom.'url.php';
  335. break;
  336. case 'core':
  337. if($module === '*') return $sc ? '{e_CORE}url/' : e_CORE.'url/';
  338. return $sc ? '{e_CORE}url/'.$module.$custom.'url.php' : e_CORE.'url/'.$module.$custom.'url.php';
  339. break;
  340. case 'override':
  341. if($module === '*') return $sc ? '{e_CORE}override/url/' : e_CORE.'override/url/' ;
  342. return $sc ? '{e_CORE}override/url/'.$module.$custom.'url.php' : e_CORE.'override/url/'.$module.$custom.'url.php' ;
  343. break;
  344. default:
  345. return null;
  346. break;
  347. }
  348. }
  349. /**
  350. * Get path to url configuration subfolders
  351. * @param string $module
  352. * @param string $location plugin|core|override
  353. * @param boolean $sc
  354. * @return string path
  355. */
  356. public static function getConfigLocationPath($module, $location, $sc = false)
  357. {
  358. switch ($location)
  359. {
  360. case 'plugin':
  361. return $sc ? '{e_PLUGIN}'.$module.'/url/' : e_PLUGIN.$module.'/url/';
  362. break;
  363. case 'core':
  364. return $sc ? '{e_CORE}url/'.$module.'/' : e_CORE.'url/'.$module.'/';
  365. break;
  366. case 'override':
  367. return $sc ? '{e_CORE}override/url/'.$module.'/' : e_CORE.'override/url/'.$module.'/';
  368. break;
  369. default:
  370. return null;
  371. break;
  372. }
  373. }
  374. /**
  375. * Get dispatch system path
  376. * @param string $location plugin|core|override
  377. * @param string $plugin required only when $location is plugin
  378. * @param boolean $sc
  379. * @return string path
  380. */
  381. public static function getDispatchLocationPath($location, $plugin = null, $sc = false)
  382. {
  383. switch ($location)
  384. {
  385. case 'plugin':
  386. if(!$plugin) return null;
  387. return $sc ? '{e_PLUGIN}'.$plugin.'/controllers/' : e_PLUGIN.$plugin.'/controllers/';
  388. break;
  389. case 'core':
  390. return $sc ? '{e_CORE}controllers/' : e_CORE.'controllers/';
  391. break;
  392. case 'override':
  393. return $sc ? '{e_CORE}override/controllers/' : e_CORE.'override/controllers/';
  394. break;
  395. default:
  396. return null;
  397. break;
  398. }
  399. }
  400. /**
  401. * Get full dispatch system path
  402. * @param string $module
  403. * @param string $location plugin|core|override
  404. * @param boolean $sc
  405. * @return string path
  406. */
  407. public static function getDispatchPath($module, $location, $sc = false)
  408. {
  409. switch ($location)
  410. {
  411. case 'plugin':
  412. return $sc ? '{e_PLUGIN}'.$module.'/controllers/' : e_PLUGIN.$module.'/controllers/';
  413. break;
  414. case 'core':
  415. return $sc ? '{e_CORE}controllers/'.$module.'/' : e_CORE.'controllers/'.$module.'/';
  416. break;
  417. case 'override':
  418. return $sc ? '{e_CORE}override/controllers/'.$module.'/' : e_CORE.'override/controllers/'.$module.'/';
  419. break;
  420. default:
  421. return null;
  422. break;
  423. }
  424. }
  425. /**
  426. * Get include path to a given module/controller
  427. *
  428. * @param string $module valid module name
  429. * @param string $controller controller name
  430. * @param string $location core|plugin|override
  431. * @param boolean $sc return relative (false) OR shortcode (true) path
  432. * @return string controller path
  433. */
  434. public static function getControllerPath($module, $controller, $location = null, $sc = false)
  435. {
  436. if(null === $location) $location = self::getDispatchLocation($module);
  437. return ($location ? self::getDispatchPath($module, $location, $sc).$controller.'.php': null);
  438. }
  439. /**
  440. * Get class name of a given module/controller
  441. *
  442. * @param string $module valid module name
  443. * @param string $controllerName controller name
  444. * @param string $location core|plugin|override
  445. * @return string controller path
  446. */
  447. public static function getControllerClass($module, $controllerName, $location = null)
  448. {
  449. if(null === $location) $location = self::getDispatchLocation($module);
  450. return ($location ? $location.'_'.$module.'_'.$controllerName.'_controller' : null);
  451. }
  452. /**
  453. * Get controller object
  454. *
  455. * @param eRequest $request
  456. * @param boolean $checkOverride whether to check the override location
  457. * @return eController null if not dispatchable
  458. */
  459. public function getController(eRequest $request, $checkOverride = true)
  460. {
  461. $class_name = $this->isDispatchable($request, true, $checkOverride);
  462. if(!$class_name) return null;
  463. return new $class_name();
  464. }
  465. /**
  466. * Check if given module/controller is dispatchable
  467. * @param string $module valid module name
  468. * @param string $controllerName controller name
  469. * @param string $location core|plugin|override
  470. * @param boolean $checkOverride whether to check the override location
  471. * @return string class name OR false if not dispatchable
  472. */
  473. public function isDispatchableModule($module, $controllerName, $location, $checkOverride = false)
  474. {
  475. if($checkOverride || $location == 'override')
  476. {
  477. $path = self::getControllerPath($module, $controllerName, 'override', false);
  478. $class_name = self::getControllerClass($module, $controllerName, 'override');
  479. if($class_name && !class_exists($class_name, false) && is_readable($path)) include_once($path);
  480. if($class_name && class_exists($class_name, false)) return $class_name;
  481. }
  482. // fallback to original dispatch location if any
  483. if($location === 'override')
  484. {
  485. // check for real location
  486. if(($location = eDispatcher::getModuleRealLocation($module)) === null) return false;
  487. }
  488. if($location !== 'override')
  489. {
  490. $path = self::getControllerPath($module, $controllerName, $location, false);
  491. $class_name = self::getControllerClass($module, $controllerName, $location);
  492. if(!$class_name) return false;
  493. if(!class_exists($class_name, false) && is_readable($path)) include_once($path);
  494. if(class_exists($class_name, false)) return $class_name;
  495. }
  496. return false;
  497. }
  498. /**
  499. * Automated version of self::isDispatchableModule()
  500. * @param eRequest $request
  501. * @param boolean $checkReflection deep check - proper subclassing, action
  502. * @param boolean $checkOverride try override controller folder first
  503. * @return mixed class name OR false if not dispatchable
  504. */
  505. public function isDispatchable(eRequest $request, $checkReflection = false, $checkOverride = true)
  506. {
  507. $location = self::getDispatchLocation($request->getModuleName());
  508. $controllerName = $request->getControllerName();
  509. $moduleName = $request->getModuleName();
  510. $className = false;
  511. // dispatch based on url_config preference value, if config location is override and there is no
  512. // override controller, additional check against real controller location will be made
  513. if($location)
  514. {
  515. $className = $this->isDispatchableModule($moduleName, $controllerName, $location, $checkOverride);
  516. }
  517. //else
  518. //{
  519. # Disable plugin check for routes with no config info - prevent calling of non-installed plugins
  520. # We may allow this for plugins which don't have plugin.xml in the future
  521. // $className = $this->isDispatchableModule($moduleName, $controllerName, 'plugin', $checkOverride);
  522. // if(!$className)
  523. //$className = $this->isDispatchableModule($moduleName, $controllerName, 'core', $checkOverride);
  524. //}
  525. if(empty($className)) return false;
  526. elseif(!$checkReflection) return $className;
  527. $rfl = new ReflectionClass($className);
  528. $method = $request->getActionMethodName();
  529. if($rfl->isSubclassOf('eController') && $rfl->hasMethod($method) && $rfl->getMethod($method)->isPublic() && !$rfl->getMethod($method)->isStatic())
  530. return $className;
  531. return false;
  532. }
  533. /**
  534. * Get class name of a given module config
  535. *
  536. * @param string $module valid module name
  537. * @param string $location core|plugin|override[/custom]
  538. * @return string controller path
  539. */
  540. public static function getConfigClassName($module, $location)
  541. {
  542. $tmp = explode('/', $location);
  543. $custom = '';
  544. $location = $tmp[0];
  545. if(isset($tmp[1]) && !empty($tmp[1]))
  546. {
  547. $custom = $tmp[1].'_';
  548. }
  549. unset($tmp);
  550. $module .= '_';
  551. // we need to prepend location to avoid namespace colisions
  552. return $location.'_'.$module.$custom.'url';
  553. }
  554. /**
  555. * Get config object for a module
  556. *
  557. * @param string $module valid module name
  558. * @param string $location core|plugin|override[/custom]
  559. * @return eUrlConfig
  560. */
  561. public static function getConfigObject($module, $location = null)
  562. {
  563. if(null === $location)
  564. {
  565. $location = self::getModuleConfigLocation($module);
  566. if(!$location) return null;
  567. }
  568. $reg = $module.'/'.$location;
  569. if(isset(self::$_configObjects[$reg])) return self::$_configObjects[$reg];
  570. $className = self::getConfigClassName($module, $location);
  571. if(!class_exists($className, false))
  572. {
  573. $path = self::getConfigPath($module, $location, false);
  574. if(!is_readable($path)) return null;
  575. include_once($path);
  576. if(!class_exists($className, false)) return null;
  577. }
  578. $obj = new $className();
  579. $obj->init();
  580. self::$_configObjects[$reg] = $obj;
  581. $obj = null;
  582. return self::$_configObjects[$reg];
  583. }
  584. /**
  585. * Auto discover module location from stored in core prefs data
  586. * @param string $module
  587. */
  588. public static function getModuleConfigLocation($module)
  589. {
  590. //retrieve from config prefs
  591. return e107::findPref('url_config/'.$module, '');
  592. }
  593. /**
  594. * Auto discover module location from stored in core prefs data
  595. * @param string $module
  596. */
  597. public static function getDispatchLocation($module)
  598. {
  599. //retrieve from prefs
  600. $location = self::getModuleConfigLocation($module);
  601. if(!$location) return null;
  602. if(($pos = strpos($location, '/'))) //can't be 0
  603. {
  604. return substr($location, 0, $pos);
  605. }
  606. return $location;
  607. }
  608. /**
  609. * Auto discover module real location (and not currently set from url adminsitration) from stored in core prefs data
  610. * @param string $module
  611. */
  612. public static function getModuleRealLocation($module)
  613. {
  614. //retrieve from prefs
  615. $searchArray = e107::findPref('url_modules');
  616. if(!$searchArray) return null;
  617. $search = array('core', 'plugin', 'override');
  618. foreach ($search as $location)
  619. {
  620. $_searchArray = vartrue($searchArray[$location], array());
  621. if(in_array($module, $_searchArray)) return $location;
  622. }
  623. return null;
  624. }
  625. }
  626. /**
  627. * URL manager - parse and create URLs based on rules set
  628. * Inspired by Yii Framework UrlManager <www.yiiframework.com>
  629. */
  630. class eRouter
  631. {
  632. /**
  633. * Configuration array containing all available syste routes and route object configuration values
  634. * @var array
  635. */
  636. protected $_rules = array();
  637. /**
  638. * List of all system wide available aliases
  639. * This includes multi-lingual configurations as well
  640. * @var array
  641. */
  642. protected $_aliases = array();
  643. /**
  644. * Cache for rule objects
  645. * @var array
  646. */
  647. protected $_parsedRules = array(); // array of rule objects
  648. /**
  649. * Global config values per rule set
  650. * @var array
  651. */
  652. protected $_globalConfig = array();
  653. /**
  654. * Module name which is used for site main namespace
  655. * Example mysite.com/news/News Item => converted to mysite.com/News Item
  656. * NOTE: Could be moved to rules config
  657. *
  658. * @var string
  659. */
  660. protected $_mainNsModule = '';
  661. /**
  662. * Default URL suffix - to be added to end of all urls (e.g. '.html')
  663. * This value can be overridden per rule item
  664. * NOTE could be moved to rules config only
  665. * @var string
  666. */
  667. public $urlSuffix = '';
  668. /**
  669. * @var string GET variable name for route. Defaults to 'route'.
  670. */
  671. public $routeVar = 'route';
  672. /**
  673. * @var string
  674. */
  675. const FORMAT_GET = 'get';
  676. /**
  677. * @var string
  678. */
  679. const FORMAT_PATH = 'path';
  680. /**
  681. * @var string
  682. */
  683. private $_urlFormat = self::FORMAT_PATH;
  684. /**
  685. * Not found route
  686. * @var string
  687. */
  688. public $notFoundRoute = 'system/error/notfound';
  689. protected $_defaultAssembleOptions = array('full' => false, 'amp' => '&amp;', 'equal' => '=', 'encode' => true);
  690. /**
  691. * Not found URL - used when system route not found and 'url_error_redirect' core pref is true
  692. * TODO - user friendly URL ('/system/404') when system config is ready ('/system/404')
  693. * @var string
  694. */
  695. public $notFoundUrl = 'system/error/404?type=routeError';
  696. public function __construct()
  697. {
  698. $this->_init();
  699. }
  700. /**
  701. * Init object
  702. * @return void
  703. */
  704. protected function _init()
  705. {
  706. // Gather all rules, add-on info, cache, module for main namespace etc
  707. $this->_loadConfig()
  708. ->setAliases();
  709. // we need config first as setter does some checks if module can be set as main
  710. $this->setMainModule(e107::getPref('url_main_module', ''));
  711. }
  712. /**
  713. * Set module for default namespace
  714. * @param string $module
  715. * @return eRouter
  716. */
  717. public function setMainModule($module)
  718. {
  719. if(!$module || !$this->isModule($module) || !$this->getConfigValue($module, 'allowMain')) return $this;
  720. $this->_mainNsModule = $module;
  721. return $this;
  722. }
  723. /**
  724. * Get main url namespace module
  725. * @return string
  726. */
  727. public function getMainModule()
  728. {
  729. return $this->_mainNsModule;
  730. }
  731. /**
  732. * Check if given module is the main module
  733. * @param string $module
  734. * @return boolean
  735. */
  736. public function isMainModule($module)
  737. {
  738. return ($this->_mainNsModule === $module);
  739. }
  740. /**
  741. * @return string get|path
  742. */
  743. public function getUrlFormat()
  744. {
  745. return $this->_urlFormat;
  746. }
  747. /**
  748. * Load config and url rules, if not available - build it on the fly
  749. * @return eRouter
  750. */
  751. protected function _loadConfig()
  752. {
  753. if(!is_readable(e_CACHE_URL.'config.php')) $config = $this->buildGlobalConfig();
  754. else $config = include(e_CACHE_URL.'config.php');
  755. if(!$config) $config = array();
  756. $rules = array();
  757. foreach ($config as $module => $c)
  758. {
  759. $rules[$module] = $c['rules'];
  760. unset($config[$module]['rules']);
  761. $config[$module] = $config[$module]['config'];
  762. }
  763. $this->_globalConfig = $config;
  764. $this->setRuleSets($rules);
  765. return $this;
  766. }
  767. public static function clearCache()
  768. {
  769. if(file_exists(e_CACHE_URL.'config.php'))
  770. {
  771. @unlink(e_CACHE_URL.'config.php');
  772. }
  773. }
  774. /**
  775. * Build unified config.php
  776. */
  777. public function buildGlobalConfig($save = true)
  778. {
  779. $active = e107::getPref('url_config', array());
  780. $config = array();
  781. foreach ($active as $module => $location)
  782. {
  783. $_config = array();
  784. $obj = eDispatcher::getConfigObject($module, $location);
  785. $path = eDispatcher::getConfigPath($module, $location, true);
  786. if(null !== $obj)
  787. {
  788. $_config = $obj->config();
  789. $_config['config']['configPath'] = $path;
  790. $_config['config']['configClass'] = eDispatcher::getConfigClassName($module, $location);
  791. }
  792. if(!isset($_config['config'])) $_config['config'] = array();
  793. $_config['config']['location'] = $location;
  794. if(!isset($_config['config']['format']) || !in_array($_config['config']['format'], array(self::FORMAT_GET, self::FORMAT_PATH)))
  795. {
  796. $_config['config']['format'] = $this->getUrlFormat();
  797. }
  798. if(!isset($_config['rules'])) $_config['rules'] = array();
  799. foreach ($_config['rules'] as $pattern => $rule)
  800. {
  801. if(!is_array($rule))
  802. {
  803. $_config['rules'][$pattern] = array($rule);
  804. }
  805. }
  806. $config[$module] = $_config;
  807. }
  808. if($save)
  809. {
  810. $fileContent = '<?php'."\n### Auto-generated - DO NOT EDIT ### \nreturn ";
  811. $fileContent .= trim(var_export($config, true)).';';
  812. file_put_contents(e_CACHE_URL.'config.php', $fileContent);
  813. }
  814. return $config;
  815. }
  816. /**
  817. * Retrieve config array from a given system path
  818. * @param string $path
  819. * @param string $location core|plugin|override
  820. */
  821. public static function adminReadConfigs($path, $location = null)
  822. {
  823. $file = e107::getFile(false);
  824. $ret = array();
  825. $file->mode = 'fname';
  826. $files = $file->setFileInfo('fname')
  827. ->get_files($path, '^([a-z_]{1,}_)?url\.php$');
  828. foreach ($files as $file)
  829. {
  830. if(null === $location)
  831. {
  832. $c = eRouter::file2config($file, $location);
  833. if($c) $ret[] = $c;
  834. continue;
  835. }
  836. $ret[] = eRouter::file2config($file, $location);
  837. }
  838. return $ret;
  839. }
  840. /**
  841. * Convert filename to configuration string
  842. * @param string $filename
  843. * @param string $location core|plugin|override
  844. */
  845. public static function file2config($filename, $location = '')
  846. {
  847. if($filename == 'url.php') return $location;
  848. if($location) $location .= '/';
  849. return $location.substr($filename, 0, strrpos($filename, '_'));
  850. }
  851. /**
  852. * Detect all available system url modules, used as a map on administration configuration path
  853. * and required (same structure) {@link from eDispatcher::adminBuildConfig())
  854. * This is a very liberal detection, as it doesn't require config file.
  855. * It goes through both config and dispatch locations and registers directory tree as modules
  856. * The only exception are plugins - if plugin requires install (plugin.xml) and it is not installed,
  857. * it won't be registered
  858. * Another important thing is - core has always higher priority, as plugins are not allowed to
  859. * directly override core modules. At this moment, core modules could be overloaded only via override configs (e107_core/override/url/)
  860. * and controllers (e107_core/override/controllers)
  861. * This array is stored as url_modules core preference
  862. *
  863. * @param string $type possible values are all|plugin|core|override
  864. * @return array available system url modules stored as url_modules core preference
  865. */
  866. public static function adminReadModules($type = 'all')
  867. {
  868. $f = e107::getFile();
  869. $ret = array('core' => array(), 'plugin' => array(), 'override' => array());
  870. if($type == 'all' || $type = 'core')
  871. {
  872. $location = eDispatcher::getDispatchLocationPath('core');
  873. // search for controllers first
  874. $ret['core'] = $f->get_dirs($location);
  875. // merge with configs
  876. $configArray = $f->get_dirs(eDispatcher::getConfigPath('*', 'core'));
  877. foreach ($configArray as $config)
  878. {
  879. if(!in_array($config, $ret['core']))
  880. {
  881. $ret['core'][] = $config;
  882. }
  883. }
  884. sort($ret['core']);
  885. }
  886. if($type == 'all' || $type = 'plugin')
  887. {
  888. $plugins = $f->get_dirs(e_PLUGIN);
  889. foreach ($plugins as $plugin)
  890. {
  891. // DON'T ALLOW PLUGINS TO OVERRIDE CORE!!!
  892. // This will be possible in the future under some other, more controllable form
  893. if(in_array($plugin, $ret['core'])) continue;
  894. $location = eDispatcher::getDispatchLocationPath('plugin', $plugin);
  895. $config = eDispatcher::getConfigPath($plugin, 'plugin');
  896. if(e107::isInstalled($plugin))
  897. {
  898. if(is_dir($location) || is_readable($config))
  899. {
  900. $ret['plugin'][] = $plugin;
  901. }
  902. continue;
  903. }
  904. // Register only those who don't need install and may be dispatchable
  905. if((!is_readable(e_PLUGIN.$plugin.'/plugin.php') && !is_readable(e_PLUGIN.$plugin.'/plugin.xml')))
  906. {
  907. if(is_dir($location) || is_readable($config))
  908. {
  909. $ret['plugin'][] = $plugin;
  910. }
  911. }
  912. }
  913. sort($ret['plugin']);
  914. }
  915. if($type == 'all' || $type = 'override')
  916. {
  917. // search for controllers first
  918. $location = eDispatcher::getDispatchLocationPath('override');
  919. $ret['override'] = $f->get_dirs($location);
  920. // merge with configs
  921. $configArray = $f->get_dirs(eDispatcher::getConfigPath('*', 'override'));
  922. foreach ($configArray as $config)
  923. {
  924. if(!in_array($config, $ret['override']))
  925. {
  926. $ret['override'][] = $config;
  927. }
  928. }
  929. sort($ret['override']);
  930. // remove not installed plugin locations, possible only for 'all' type
  931. if($type == 'all')
  932. {
  933. foreach ($ret['override'] as $i => $l)
  934. {
  935. // it's a plugin override, but not listed in current plugin array - remove
  936. if(in_array($l, $plugins) && !in_array($l, $ret['plugin']))
  937. {
  938. unset($ret['override'][$i]);
  939. }
  940. }
  941. }
  942. }
  943. return $ret;
  944. }
  945. /**
  946. * Rebuild configuration array, stored as url_config core preference
  947. * More strict detection compared to {@link eDispatcher::adminReadModules()}
  948. * Current flat array containing config locations per module are rebuilt so that new
  949. * modules are registered, missing modules - removed. Additionally fallback to the default location
  950. * is done if current user defined location is not readable
  951. * @see eDispatcher::adminReadModules()
  952. * @param array current configuration array (url_config core preference like)
  953. * @param array available URL modules as detected by {@link eDispatcher::adminReadModules()} and stored as url_modules core preference value
  954. * @return array new url_config array
  955. */
  956. public static function adminBuildConfig($current, $adminReadModules = null)
  957. {
  958. if(null === $adminReadModules) $adminReadModules = self::adminReadModules();
  959. $ret = array();
  960. $all = array_unique(array_merge($adminReadModules['core'], $adminReadModules['plugin'], $adminReadModules['override']));
  961. foreach ($all as $module)
  962. {
  963. if(isset($current[$module]))
  964. {
  965. // current contains custom (readable) config location e.g. news => core/rewrite
  966. if(strpos($current[$module], '/') !== false && is_readable(eDispatcher::getConfigPath($module, $current[$module])))
  967. {
  968. $ret[$module] = $current[$module];
  969. continue;
  970. }
  971. // in all other cases additional re-check will be made - see below
  972. }
  973. if(in_array($module, $adminReadModules['override']))
  974. {
  975. // core check
  976. if(in_array($module, $adminReadModules['core']))
  977. {
  978. $mustHave = is_readable(eDispatcher::getConfigPath($module, 'core'));
  979. $has = is_readable(eDispatcher::getConfigPath($module, 'override'));
  980. // No matter if it must have, it has e_url config
  981. if($has) $ret[$module] = 'override';
  982. // It must have but it doesn't have e_url config, fallback
  983. elseif($mustHave && !$has) $ret[$module] = 'core';
  984. // Rest is always core as controller override is done on run time
  985. else $ret[$module] = 'core';
  986. }
  987. // plugin check
  988. elseif(in_array($module, $adminReadModules['plugin']))
  989. {
  990. $mustHave = is_readable(eDispatcher::getConfigPath($module, 'plugin'));
  991. $has = is_readable(eDispatcher::getConfigPath($module, 'override'));
  992. // No matter if it must have, it has e_url config
  993. if($has) $ret[$module] = 'override';
  994. // It must have but it doesn't have e_url config, fallback
  995. elseif($mustHave && !$has) $ret[$module] = 'plugin';
  996. // Rest is always plugin as config is most important, controller override check is done on run time
  997. else $ret[$module] = 'plugin';
  998. }
  999. // standalone override module
  1000. else
  1001. {
  1002. $ret[$module] = 'override';
  1003. }
  1004. }
  1005. // default core location
  1006. elseif(in_array($module, $adminReadModules['core']))
  1007. {
  1008. $ret[$module] = 'core';
  1009. }
  1010. // default plugin location
  1011. elseif(in_array($module, $adminReadModules['plugin']))
  1012. {
  1013. $ret[$module] = 'plugin';
  1014. }
  1015. }
  1016. return $ret;
  1017. }
  1018. /**
  1019. * Detect available config locations (readable check), based on available url_modules {@link eDispatcher::adminReadModules()} core preference arrays
  1020. * Used to rebuild url_locations core preference value
  1021. * @see eDispatcher::adminBuildConfig()
  1022. * @see eDispatcher::adminReadModules()
  1023. * @param array $available {@link eDispatcher::adminReadModules()} stored as url_modules core preference
  1024. * @return array available config locations, stored as url_locations core preference
  1025. */
  1026. public static function adminBuildLocations($available = null)
  1027. {
  1028. $ret = array();
  1029. if(null === $available) $available = self::adminReadModules();
  1030. $fl = e107::getFile();
  1031. // Core
  1032. foreach ($available['core'] as $module)
  1033. {
  1034. // Default module
  1035. $ret[$module] = array('core');
  1036. // read sub-locations
  1037. $path = eDispatcher::getConfigLocationPath($module, 'core');
  1038. //$sub = $fl->get_dirs($path);
  1039. $sub = eRouter::adminReadConfigs($path);
  1040. if($sub)
  1041. {
  1042. foreach ($sub as $moduleSub)
  1043. {
  1044. // auto-override: override available (controller or url config), check for config
  1045. if(in_array($module, $available['override']) && is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
  1046. {
  1047. $ret[$module][] = 'override/'.$moduleSub;
  1048. }
  1049. // no override available, register the core location
  1050. elseif(is_readable(eDispatcher::getConfigPath($module, 'core/'.$moduleSub)))
  1051. {
  1052. $ret[$module][] = 'core/'.$moduleSub;
  1053. }
  1054. }
  1055. }
  1056. }
  1057. // Plugins
  1058. foreach ($available['plugin'] as $module)
  1059. {
  1060. // Default module
  1061. $ret[$module] = array('plugin');
  1062. // read sub-locations
  1063. $path = eDispatcher::getConfigLocationPath($module, 'plugin');
  1064. //$sub = $fl->get_dirs($path);
  1065. $sub = eRouter::adminReadConfigs($path);
  1066. if($sub)
  1067. {
  1068. foreach ($sub as $moduleSub)
  1069. {
  1070. // auto-override: override available (controller or url config), check for config
  1071. if(in_array($module, $available['override']) && is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
  1072. {
  1073. $ret[$module][] = 'override/'.$moduleSub;
  1074. }
  1075. // no override available, register the core location
  1076. elseif(is_readable(eDispatcher::getConfigPath($module, 'plugin/'.$moduleSub)))
  1077. {
  1078. $ret[$module][] = 'plugin/'.$moduleSub;
  1079. }
  1080. }
  1081. }
  1082. }
  1083. // Go through all overrides, register those who don't belong to core & plugins as standalone core modules
  1084. foreach ($available['override'] as $module)
  1085. {
  1086. // either it is a core/plugin module or e_url.php is not readable - continue
  1087. if(in_array($module, $available['core']) || in_array($module, $available['plugin']))
  1088. {
  1089. continue;
  1090. }
  1091. // Default module
  1092. $ret[$module] = array('override');
  1093. // read sub-locations
  1094. $path = eDispatcher::getConfigLocationPath($module, 'override');
  1095. //$sub = $fl->get_dirs($path);
  1096. $sub = eRouter::adminReadConfigs($path);
  1097. if($sub)
  1098. {
  1099. foreach ($sub as $moduleSub)
  1100. {
  1101. if(is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
  1102. {
  1103. $ret[$module][] = 'override/'.$moduleSub;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. return $ret;
  1109. }
  1110. /**
  1111. * Match current aliases against currently available module and languages
  1112. * @param array $currentAliases url_aliases core preference
  1113. * @param array $currentConfig url_config core preference
  1114. * @return array cleaned aliases
  1115. */
  1116. public static function adminSyncAliases($currentAliases, $currentConfig)
  1117. {
  1118. if(empty($currentAliases)) return array();
  1119. $modules = array_keys($currentConfig);
  1120. // remove non existing languages
  1121. $lng = e107::getLanguage();
  1122. $lanList = $lng->installed();
  1123. if(is_array($currentAliases))
  1124. {
  1125. foreach ($currentAliases as $lanCode => $aliases)
  1126. {
  1127. $lanName = $lng->convert($lanCode);
  1128. if(!$lanName || !in_array($lanName, $lanList))
  1129. {
  1130. unset($currentAliases[$lanCode]);
  1131. continue;
  1132. }
  1133. // remove non-existing modules
  1134. foreach ($aliases as $alias => $module)
  1135. {
  1136. if(!isset($currentConfig[$module])) unset($currentAliases[$lanCode][$alias]);
  1137. }
  1138. }
  1139. }
  1140. return $currentAliases;
  1141. }
  1142. /**
  1143. * Retrieve global configuration array for a single or all modules
  1144. * @param string $module system module
  1145. * @return array configuration
  1146. */
  1147. public function getConfig($module = null)
  1148. {
  1149. if(null === $module) return $this->_globalConfig;
  1150. return isset($this->_globalConfig[$module]) ? $this->_globalConfig[$module] : array();
  1151. }
  1152. /**
  1153. * Retrieve single value from a module global configuration array
  1154. * @param string $module system module
  1155. * @return array configuration
  1156. */
  1157. public function getConfigValue($module, $key, $default = null)
  1158. {
  1159. return isset($this->_globalConfig[$module]) && isset($this->_globalConfig[$module][$key]) ? $this->_globalConfig[$module][$key] : $default;
  1160. }
  1161. /**
  1162. * Get system name of a module by its alias
  1163. * Returns null if $alias is not an existing alias
  1164. * @param string $alias
  1165. * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
  1166. * @return string module
  1167. */
  1168. public function getModuleFromAlias($alias, $lan = null)
  1169. {
  1170. if($lan) return e107::findPref('url_aliases/'.$lan.'/'.$alias, null);
  1171. return (isset($this->_aliases[$alias]) ? $this->_aliases[$alias] : null);
  1172. }
  1173. /**
  1174. * Get alias name for a module
  1175. * Returns null if module doesn't have an alias
  1176. * @param string $module
  1177. * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
  1178. * @return string alias
  1179. */
  1180. public function getAliasFromModule($module, $lan = null)
  1181. {
  1182. if($lan)
  1183. {
  1184. $aliases = e107::findPref('url_aliases/'.$lan, array());
  1185. return (in_array($module, $aliases) ? array_search($module, $aliases) : null);
  1186. }
  1187. return (in_array($module, $this->_aliases) ? array_search($module, $this->_aliases) : null);
  1188. }
  1189. /**
  1190. * Check if alias exists
  1191. * @param string $alias
  1192. * @param string $lan optional language alias. Example $lan = 'bg' (search for Bulgarian aliases)
  1193. * @return boolean
  1194. */
  1195. public function isAlias($alias, $lan = null)
  1196. {
  1197. if($lan)
  1198. {
  1199. $aliases = e107::findPref('url_aliases/'.$lan, array());
  1200. return isset($aliases[$alias]);
  1201. }
  1202. return isset($this->_aliases[$alias]);
  1203. }
  1204. /**
  1205. * Check if there is an alias for provided module
  1206. * @param string $module
  1207. * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
  1208. * @return boolean
  1209. */
  1210. public function hasAlias($module, $lan = null)
  1211. {
  1212. if($lan)
  1213. {
  1214. $aliases = e107::findPref('url_aliases/'.$lan, array());
  1215. return in_array($module, $aliases);
  1216. }
  1217. return in_array($module, $this->_aliases);
  1218. }
  1219. /**
  1220. * Get all available module aliases
  1221. * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
  1222. * @return array
  1223. */
  1224. public function getAliases($lanCode = null)
  1225. {
  1226. if($lan)
  1227. {
  1228. return e107::findPref('url_aliases/'.$lan, array());
  1229. }
  1230. return $this->_aliases;
  1231. }
  1232. /**
  1233. * Set module aliases
  1234. * @param array $aliases
  1235. * @return eRouter
  1236. */
  1237. public function setAliases($aliases = null)
  1238. {
  1239. if(null === $aliases)
  1240. {
  1241. $lanCode = e107::getLanguage()->convert(e_LANGUAGE);
  1242. $aliases = e107::findPref('url_aliases/'.$lanCode, array());
  1243. }
  1244. $this->_aliases = $aliases;
  1245. return $this;
  1246. }
  1247. /**
  1248. * Check if provided module is present in the rules config
  1249. * @param string module
  1250. * @return boolean
  1251. */
  1252. public function isModule($module)
  1253. {
  1254. return isset($this->_globalConfig[$module]);
  1255. }
  1256. /**
  1257. * Check if the passed value is valid module or module alias, returns system module
  1258. * or null on failure
  1259. * @param string $module
  1260. * @param boolean $strict check for existence if true
  1261. * @return string module
  1262. */
  1263. public function retrieveModule($module, $strict = true)
  1264. {
  1265. if($this->isAlias($module))
  1266. $module = $this->getModuleFromAlias($module);
  1267. if($strict && (!$module || !$this->isModule($module)))
  1268. return null;
  1269. return $module;
  1270. }
  1271. /**
  1272. * Set rule config for this instance
  1273. * @param array $rules
  1274. * @return void
  1275. */
  1276. public function setRuleSets($rules)
  1277. {
  1278. $this->_rules = $rules;
  1279. }
  1280. /**
  1281. * Retrieve rule set for a module
  1282. * @param string $module
  1283. */
  1284. public function getRuleSet($module)
  1285. {
  1286. return (isset($this->_rules[$module]) ? $this->_rules[$module] : array());
  1287. }
  1288. /**
  1289. * Get all rule sets
  1290. */
  1291. public function getRuleSets()
  1292. {
  1293. return $this->_rules;
  1294. }
  1295. /**
  1296. * Retrive array of eUrlRule objects for given module
  1297. */
  1298. public function getRules($module)
  1299. {
  1300. return $this->_processRules($module);
  1301. }
  1302. /**
  1303. * Process rule set array, create rule objects
  1304. * TODO - rule cache
  1305. * @param string $module
  1306. * @return array processed rule set
  1307. */
  1308. protected function _processRules($module)
  1309. {
  1310. if(!$this->isModule($module)) return array();
  1311. if(!isset($this->_parsedRules[$module]))
  1312. {
  1313. $rules = $this->getRuleSet($module);
  1314. $config = $this->getConfig($module);
  1315. $this->_parsedRules[$module] = array();
  1316. $map = array('urlSuffix' => 'urlSuffix', 'legacy' => 'legacy', 'legacyQuery' => 'legacyQuery', 'mapVars' => 'mapVars', 'allowVars' => 'allowVars', 'matchValue' => 'matchValue');
  1317. foreach ($rules as $pattern => $set)
  1318. {
  1319. foreach ($map as $key => $value)
  1320. {
  1321. if(!isset($set[$value]) && isset($config[$key]))
  1322. {
  1323. $set[$value] = $config[$key];
  1324. }
  1325. }
  1326. $this->_parsedRules[$module][$pattern] = $this->createRule($set, $pattern);
  1327. }
  1328. }
  1329. return $this->_parsedRules[$module];
  1330. }
  1331. /**
  1332. * Create rule object
  1333. *
  1334. * @param string $route
  1335. * @param string|array $pattern
  1336. * @param boolean $cache
  1337. * @return eUrlRule
  1338. */
  1339. protected function createRule($route, $pattern = null, $cache = false)
  1340. {
  1341. return new eUrlRule($route, $pattern, $cache);
  1342. }
  1343. /**
  1344. * Route current request
  1345. * @param eRequest $request
  1346. * @return boolean
  1347. */
  1348. public function route(eRequest $request, $checkOnly = false)
  1349. {
  1350. $request->routed = false;
  1351. if(isset($_GET[$this->routeVar]))
  1352. {
  1353. $rawPathInfo = $_GET[$this->routeVar];
  1354. unset($_GET[$this->routeVar]);
  1355. $this->_urlFormat = self::FORMAT_GET;
  1356. }
  1357. else
  1358. {
  1359. $rawPathInfo = rawurldecode($request->getPathInfo());
  1360. //$this->_urlFormat = self::FORMAT_PATH;
  1361. }
  1362. // Route to front page - index/index/index route
  1363. if(!$rawPathInfo && (!$this->getMainModule() || empty($_GET)))
  1364. {
  1365. // front page settings will be detected and front page will be rendered
  1366. $request->setRoute('index/index/index');
  1367. $request->addRouteHistory($rawPathInfo);
  1368. $request->routed = true;
  1369. return true;
  1370. }
  1371. // max number of parts is actually 4 - module/controller/action/[additional/pathinfo/vars], here for reference only
  1372. $parts = $rawPathInfo ? explode('/', $rawPathInfo, 4) : array();
  1373. // find module - check aliases
  1374. $module = $this->retrieveModule($parts[0]);
  1375. $mainSwitch = false;
  1376. // no module found, switch to Main module (pref) if available
  1377. if(null === $module && $this->getMainModule() && $this->isModule($this->getMainModule()))
  1378. {
  1379. $module = $this->getMainModule();
  1380. $rawPathInfo = $module.'/'.$rawPathInfo;
  1381. array_unshift($parts, $module);
  1382. $mainSwitch = true;
  1383. }
  1384. $request->routePathInfo = $rawPathInfo;
  1385. // valid module
  1386. if(null !== $module)
  1387. {
  1388. // we have valid module
  1389. $config = $this->getConfig($module);
  1390. // set legacy state
  1391. eFront::isLegacy(varset($config['legacy']));
  1392. // Don't allow single entry if required by module config
  1393. if(vartrue($config['noSingleEntry']))
  1394. {
  1395. $request->routed = true;
  1396. if(!eFront::isLegacy())
  1397. {
  1398. $request->setRoute($this->notFoundRoute);
  1399. return false;
  1400. }
  1401. // legacy entry point - include it later in the bootstrap, legacy query string will be set to current
  1402. $request->addRouteHistory($rawPathInfo);
  1403. return true;
  1404. }
  1405. // URL format - the one set by current config overrides the auto-detection
  1406. $format = isset($config['format']) && $config['format'] ? $config['format'] : $this->getUrlFormat();
  1407. //remove leading module, unnecessary overhead while matching
  1408. array_shift($parts);
  1409. $rawPathInfo = $parts ? implode('/', $parts) : '';
  1410. $pathInfo = $this->removeUrlSuffix($rawPathInfo, $this->urlSuffix);
  1411. // retrieve rules if any and if needed
  1412. $rules = $format == self::FORMAT_PATH ? $this->getRules($module) : array();
  1413. // Further parsing may still be needed
  1414. if(empty($rawPathInfo))
  1415. {
  1416. $rawPathInfo = $pathInfo;
  1417. }
  1418. // parse callback
  1419. if(vartrue($config['selfParse']))
  1420. {
  1421. // controller/action[/additional/parms]
  1422. if(vartrue($config['urlSuffix'])) $rawPathInfo = $this->removeUrlSuffix($rawPathInfo, $config['urlSuffix']);
  1423. $route = $this->configCallback($module, 'parse', array($rawPathInfo, $_GET, $request, $this, $config), $config['location']);
  1424. }
  1425. // default module route
  1426. elseif($format == self::FORMAT_GET || !$rules)
  1427. {
  1428. $route = $pathInfo;
  1429. }
  1430. // rules available - try to match an Url Rule
  1431. elseif($rules)
  1432. {
  1433. foreach ($rules as $rule)
  1434. {
  1435. $route = $rule->parseUrl($this, $request, $pathInfo, $rawPathInfo);
  1436. if($route !== false)
  1437. {
  1438. eFront::isLegacy($rule->legacy); // legacy include override
  1439. if($rule->parseCallback)
  1440. {
  1441. $this->configCallback($module, $rule->parseCallback, array($request), $config['location']);
  1442. }
  1443. // parse legacy query string if any
  1444. if(null !== $rule->legacyQuery)
  1445. {
  1446. $obj = eDispatcher::getConfigObject($module, $config['location']);
  1447. // eUrlConfig::legacyQueryString set as legacy string by default in eUrlConfig::legacy() method
  1448. $vars = new e_vars($request->getRequestParams());
  1449. $vars->module = $module;
  1450. $vars->controller = $request->getController();
  1451. $vars->action = $request->getAction();
  1452. if($rule->allowVars)
  1453. {
  1454. foreach ($rule->allowVars as $key)
  1455. {
  1456. if(isset($_GET[$key]) && !$request->isRequestParam($key))
  1457. {
  1458. // sanitize
  1459. $vars->$key = preg_replace('/[^\d\w\-]/', '', $_GET[$key]);
  1460. }
  1461. }
  1462. }
  1463. $obj->legacyQueryString = e107::getParser()->simpleParse($rule->legacyQuery, $vars, '0');
  1464. unset($vars, $obj);
  1465. }
  1466. break;
  1467. }
  1468. }
  1469. }
  1470. // append module to be registered in the request object
  1471. if(false !== $route)
  1472. {
  1473. // don't modify if true - request directly modified by config callback
  1474. if(!$request->routed)
  1475. {
  1476. if(eFront::isLegacy()) $this->configCallback($module, 'legacy', array($route, $request), $config['location']);
  1477. $route = $module.'/'.$route;
  1478. }
  1479. }
  1480. // No route found, we didn't switched to main module auto-magically
  1481. elseif(!$mainSwitch && vartrue($config['errorRoute']))
  1482. {
  1483. $route = !$checkOnly ? $module.'/'.$config['errorRoute'] : false;
  1484. }
  1485. }
  1486. // final fallback
  1487. if(!vartrue($route))
  1488. {
  1489. if($request->routed)
  1490. {
  1491. $route = $request->getRoute();
  1492. }
  1493. if(!$route)
  1494. {
  1495. $route = $this->notFoundRoute;
  1496. eFront::isLegacy(''); // reset legacy - not found route isn't legacy call
  1497. $request->routed = true;
  1498. if($checkOnly) return false;
  1499. ## Global redirect on error option
  1500. if(e107::getPref('url_error_redirect', false) && $this->notFoundUrl)
  1501. {
  1502. $redirect = $this->assemble($this->notFoundUrl, '', 'encode=0&full=1');
  1503. //echo $redirect; exit;
  1504. e107::getRedirect()->redirect($redirect, true, 404);
  1505. }
  1506. }
  1507. }
  1508. $request->setRoute($route);
  1509. $request->addRouteHistory($route);
  1510. $request->routed = true;
  1511. return true;
  1512. }
  1513. /**
  1514. * And more BC
  1515. * Checks and does some addtional logic if registered module is of type legacy
  1516. * @param eRequest $request
  1517. * @return void
  1518. */
  1519. public function checkLegacy(eRequest $request)
  1520. {
  1521. $module = $request->getModule();
  1522. // forward from controller to a legacy module - bad stuff
  1523. if(!$request->isDispatched() && $this->getConfigValue($module, 'legacy'))
  1524. {
  1525. eFront::isLegacy($this->getConfigValue($module, 'legacy'));
  1526. $url = $this->assemble($request->getRoute(), $request->getRequestParams());
  1527. $request->setRequestInfo($url)->setPathInfo(null)->setRoute(null);
  1528. $_GET = $request->getRequestParams();
  1529. $_SERVER['QUERY_STRING'] = http_build_query($request->getRequestParams(), null, '&');
  1530. // Infinite loop impossible, as dispatcher will break because of the registered legacy path
  1531. $this->route($request);
  1532. }
  1533. }
  1534. /**
  1535. * Convenient way to call config methods
  1536. */
  1537. public function configCallback($module, $callBack, $params, $location)
  1538. {
  1539. if(null == $location) $location = eDispatcher::getModuleConfigLocation($module);
  1540. if(!$module || !($obj = eDispatcher::getConfigObject($module, $location))) return false;
  1541. return call_user_func_array(array($obj, $callBack), $params);
  1542. }
  1543. /**
  1544. * Convert assembled url to shortcode
  1545. *
  1546. * @param string $route
  1547. * @param array $params
  1548. * @param array $options {@see eRouter::$_defaultAssembleOptions}
  1549. */
  1550. public function assembleSc($route, $params = array(), $options = array())
  1551. {
  1552. //if(is_string($options)) parse_str($options, $options);
  1553. $url = $this->assemble($route, $params, $options);
  1554. return e107::getParser()->createConstants($url, 'mix');
  1555. }
  1556. /**
  1557. * Assemble system URL
  1558. * Examples:
  1559. * <?php
  1560. * $router->assemble('/'); // index page URL e.g. / or /site_folder/
  1561. * $router->assemble('news/view/item?id=1'); // depends on current news config, possible return value is /news/1
  1562. * $router->assemble('*', 'id=1'); // use current request info - /module/controller/action?id=1
  1563. * $router->assemble('* /* /newaction'); // (NO EMPTY SPACES) change only current action - /module/controller/newaction
  1564. * $newsItem = array('news_id' => 1, 'news_sef' => 'My Title', ...); // as retrieved from DB
  1565. * $router->assemble('news/view/item', $newsItem); // All unused key=>values will be removed and NOT appended as GET vars
  1566. *
  1567. * @param string $route
  1568. * @param array $params
  1569. * @param array $options {@see eRouter::$_defaultAssembleOptions}
  1570. */
  1571. public function assemble($route, $params = array(), $options = array())
  1572. {
  1573. // TODO - url options
  1574. $request = eFront::instance()->getRequest();
  1575. if(is_string($options)) parse_str($options, $options);
  1576. $o

Large files files are truncated, but you can click here to view the full file