/e107_handlers/application.php
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
- <?php
- /*
- * e107 website system
- *
- * Copyright (C) 2008-2012 e107 Inc (e107.org)
- * Released under the terms and conditions of the
- * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)
- *
- * $URL$
- * $Id$
- */
-
-
- /**
- * @package e107
- * @subpackage e107_handlers
- * @version $Id$
- * @author SecretR
- *
- * e107 Single Entry Point handling
- *
- * Currently this file contains all classes required for single entry point functionallity
- * They will be separated in different files in a proper way (soon)
- */
-
-
- /**
- * e107 Front controller
- */
- class eFront
- {
- /**
- * Singleton instance
- * @var eFront
- */
- private static $_instance;
-
- /**
- * @var eDispatcher
- */
- protected $_dispatcher;
-
- /**
- * @var eRequest
- */
- protected $_request;
-
- /**
- * @var eRouter
- */
- protected $_router;
-
- /**
- * @var string path to file to include - the old deprecated way of delivering content
- */
- protected static $_legacy = '';
-
- /**
- * Constructor
- */
- private function __construct()
- {
- }
-
- /**
- * Cloning not allowed
- *
- */
- private function __clone()
- {
- }
-
- /**
- * Singleton implementation
- * @return eFront
- */
- public static function instance()
- {
- if(null == self::$_instance)
- {
- self::$_instance = new self();
- }
- return self::$_instance;
- }
-
- /**
- * Dispatch
- */
- public function dispatch(eRequest $request = null, eResponse $response = null, eDispatcher $dispatcher = null)
- {
- if(null === $request)
- {
- if(null === $this->getRequest())
- {
- $request = new eRequest();
- $this->setRequest($request);
- }
- else $request = $this->getRequest();
- }
- elseif(null === $this->getRequest()) $this->setRequest($request);
-
- if(null === $response)
- {
- if(null === $this->getResponse())
- {
- $response = new eResponse();
- $this->setResponse($response);
- }
- else $response = $this->getResponse();
- }
- elseif(null === $this->getRequest()) $this->setRequest($request);
-
-
- if(null === $dispatcher)
- {
- if(null === $this->getDispatcher())
- {
- $dispatcher = new eDispatcher();
- $this->setDispatcher($dispatcher);
- }
- else $dispatcher = $this->getDispatcher();
- }
- elseif(null === $this->getDispatcher()) $this->setDispatcher($dispatcher);
-
-
- // set dispatched status true, required for checkLegacy()
- $request->setDispatched(true);
-
- $router = $this->getRouter();
-
- // If current request not already routed outside the dispatch method, route it
- if(!$request->routed) $router->route($request);
-
- $c = 0;
- // dispatch loop
- do
- {
- $c++;
- if($c > 100)
- {
- throw new eException("Too much dispatch loops", 1);
- }
-
- // dispatched status true on first loop
- $router->checkLegacy($request);
-
- // dispatched by default - don't allow legacy to alter dispatch status
- $request->setDispatched(true);
-
- // legacy mod - return control to the bootstrap
- if(self::isLegacy())
- {
- return;
- }
-
- // for the good players - dispatch loop - no more BC!
- try
- {
- $dispatcher->dispatch($request, $response);
- }
- catch(eException $e)
- {
- echo $request->getRoute().' - '.$e->getMessage();
- exit;
- }
-
-
- } while (!$request->isDispatched());
- }
-
- /**
- * Init all objects required for request dispatching
- * @return eFront
- */
- public function init()
- {
- $request = new eRequest();
- $this->setRequest($request);
-
- $dispatcher = new eDispatcher();
- $this->setDispatcher($dispatcher);
-
- $router = new eRouter();
- $this->setRouter($router);
-
- $response = new eResponse();
- $this->setResponse($response);
-
- return $this;
- }
-
- /**
- * Dispatch
- * @param string|eRequest $route
- */
- public function run($route = null)
- {
- if($route)
- {
- if(is_object($route) && ($route instanceof eRequest)) $this->setRequest($route);
- elseif(null !== $route && null !== $this->getRequest()) $this->getRequest()->setRoute($route);
- }
- try
- {
- $this->dispatch();
- }
- catch(eException $e)
- {
- echo $e->getMessage();
- exit;
- }
-
- }
-
- /**
- * Application instance (e107 class)
- * @return e107
- */
- public static function app()
- {
- return e107::getInstance();
- }
-
- /**
- * Get dispatcher instance
- * @return eDispatcher
- */
- public function getDispatcher()
- {
- return $this->_dispatcher;
- }
-
- /**
- * Set dispatcher
- * @param eDispatcher $dispatcher
- * @return eFront
- */
- public function setDispatcher(eDispatcher $dispatcher)
- {
- $this->_dispatcher = $dispatcher;
- return $this;
- }
-
- /**
- * Get request instance
- * @return eRequest
- */
- public function getRequest()
- {
- return $this->_request;
- }
-
- /**
- * Set request
- * @param eRequest $request
- * @return eFront
- */
- public function setRequest(eRequest $request)
- {
- $this->_request = $request;
- return $this;
- }
-
- /**
- * Get response instance
- * @return eResponse
- */
- public function getResponse()
- {
- return $this->_response;
- }
-
- /**
- * Set response
- * @param eResponse $response
- * @return eFront
- */
- public function setResponse(eResponse $response)
- {
- $this->_response = $response;
- return $this;
- }
-
- /**
- * Get router instance
- * @return eRouter
- */
- public function getRouter()
- {
- return $this->_router;
- }
-
- /**
- * Set router instance
- * @return eFront
- */
- public function setRouter(eRouter $router)
- {
- $this->_router = $router;
- return $this;
- }
-
- /**
- * Set/get legacy status of the current request
- * @param boolean $status
- * @return boolean
- */
- public static function isLegacy($status = null)
- {
- if(null !== $status)
- {
- if($status[0] === '{')
- {
- $status = e107::getParser()->replaceConstants($status);
- }
- self::$_legacy = $status;
- }
- return self::$_legacy;
- }
- }
-
- /**
- * e107 Dispatcher
- * It decides how to dispatch the request.
- */
- class eDispatcher
- {
- protected static $_configObjects = array();
-
- public function dispatch(eRequest $request = null, eResponse $response = null)
- {
- $controllerName = $request->getControllerName();
- $moduleName = $request->getModuleName();
- $className = $this->isDispatchable($request, false);
-
- // dispatch based on rule settings
- if(!$className)
- {
- if($controllerName == 'index') // v2.x upgrade has not been run yet.
- {
- e107::getRedirect()->redirect(e_ADMIN."admin.php");
- }
-
- throw new eException("Invalid controller '".$controllerName."'");
- }
-
- $controller = new $className($request, $response);
- if(!($controller instanceof eController))
- {
- throw new eException("Controller $controller is not an instance of eController");
- }
-
- $actionName = $request->getActionMethodName();
-
- ob_start();
-
- $controller->dispatch($actionName);
-
- $content = ob_get_contents();
- ob_end_clean();
-
- $response->appendBody($content);
- unset($controller);
- }
-
- /**
- * Get path to the e_url handler
- * @param string $module
- * @param string $location plugin|core|override
- * @param boolean $sc
- * @return string path
- */
- public static function getConfigPath($module, $location, $sc = false)
- {
- $tmp = explode('/', $location);
- $custom = '';
- $location = $tmp[0];
- if(isset($tmp[1]) && !empty($tmp[1]))
- {
- $custom = $tmp[1].'_';
- }
- unset($tmp);
- if($module !== '*') $module .= '/';
-
- switch ($location)
- {
- case 'plugin':
- //if($custom) $custom = 'url/'.$custom;
- return $sc ? '{e_PLUGIN}'.$module.'url/'.$custom.'url.php' : e_PLUGIN.$module.'url/'.$custom.'url.php';
- break;
-
- case 'core':
- if($module === '*') return $sc ? '{e_CORE}url/' : e_CORE.'url/';
- return $sc ? '{e_CORE}url/'.$module.$custom.'url.php' : e_CORE.'url/'.$module.$custom.'url.php';
- break;
-
- case 'override':
- if($module === '*') return $sc ? '{e_CORE}override/url/' : e_CORE.'override/url/' ;
- return $sc ? '{e_CORE}override/url/'.$module.$custom.'url.php' : e_CORE.'override/url/'.$module.$custom.'url.php' ;
- break;
-
- default:
- return null;
- break;
- }
- }
-
- /**
- * Get path to url configuration subfolders
- * @param string $module
- * @param string $location plugin|core|override
- * @param boolean $sc
- * @return string path
- */
- public static function getConfigLocationPath($module, $location, $sc = false)
- {
- switch ($location)
- {
- case 'plugin':
- return $sc ? '{e_PLUGIN}'.$module.'/url/' : e_PLUGIN.$module.'/url/';
- break;
-
- case 'core':
- return $sc ? '{e_CORE}url/'.$module.'/' : e_CORE.'url/'.$module.'/';
- break;
-
- case 'override':
- return $sc ? '{e_CORE}override/url/'.$module.'/' : e_CORE.'override/url/'.$module.'/';
- break;
-
- default:
- return null;
- break;
- }
- }
-
- /**
- * Get dispatch system path
- * @param string $location plugin|core|override
- * @param string $plugin required only when $location is plugin
- * @param boolean $sc
- * @return string path
- */
- public static function getDispatchLocationPath($location, $plugin = null, $sc = false)
- {
- switch ($location)
- {
- case 'plugin':
- if(!$plugin) return null;
- return $sc ? '{e_PLUGIN}'.$plugin.'/controllers/' : e_PLUGIN.$plugin.'/controllers/';
- break;
-
- case 'core':
- return $sc ? '{e_CORE}controllers/' : e_CORE.'controllers/';
- break;
-
- case 'override':
- return $sc ? '{e_CORE}override/controllers/' : e_CORE.'override/controllers/';
- break;
-
- default:
- return null;
- break;
- }
- }
-
- /**
- * Get full dispatch system path
- * @param string $module
- * @param string $location plugin|core|override
- * @param boolean $sc
- * @return string path
- */
- public static function getDispatchPath($module, $location, $sc = false)
- {
- switch ($location)
- {
- case 'plugin':
- return $sc ? '{e_PLUGIN}'.$module.'/controllers/' : e_PLUGIN.$module.'/controllers/';
- break;
-
- case 'core':
- return $sc ? '{e_CORE}controllers/'.$module.'/' : e_CORE.'controllers/'.$module.'/';
- break;
-
- case 'override':
- return $sc ? '{e_CORE}override/controllers/'.$module.'/' : e_CORE.'override/controllers/'.$module.'/';
- break;
-
- default:
- return null;
- break;
- }
- }
-
- /**
- * Get include path to a given module/controller
- *
- * @param string $module valid module name
- * @param string $controller controller name
- * @param string $location core|plugin|override
- * @param boolean $sc return relative (false) OR shortcode (true) path
- * @return string controller path
- */
- public static function getControllerPath($module, $controller, $location = null, $sc = false)
- {
- if(null === $location) $location = self::getDispatchLocation($module);
-
- return ($location ? self::getDispatchPath($module, $location, $sc).$controller.'.php': null);
- }
-
- /**
- * Get class name of a given module/controller
- *
- * @param string $module valid module name
- * @param string $controllerName controller name
- * @param string $location core|plugin|override
- * @return string controller path
- */
- public static function getControllerClass($module, $controllerName, $location = null)
- {
- if(null === $location) $location = self::getDispatchLocation($module);
-
- return ($location ? $location.'_'.$module.'_'.$controllerName.'_controller' : null);
- }
-
-
- /**
- * Get controller object
- *
- * @param eRequest $request
- * @param boolean $checkOverride whether to check the override location
- * @return eController null if not dispatchable
- */
- public function getController(eRequest $request, $checkOverride = true)
- {
- $class_name = $this->isDispatchable($request, true, $checkOverride);
- if(!$class_name) return null;
-
- return new $class_name();
- }
-
- /**
- * Check if given module/controller is dispatchable
- * @param string $module valid module name
- * @param string $controllerName controller name
- * @param string $location core|plugin|override
- * @param boolean $checkOverride whether to check the override location
- * @return string class name OR false if not dispatchable
- */
- public function isDispatchableModule($module, $controllerName, $location, $checkOverride = false)
- {
- if($checkOverride || $location == 'override')
- {
- $path = self::getControllerPath($module, $controllerName, 'override', false);
-
- $class_name = self::getControllerClass($module, $controllerName, 'override');
- if($class_name && !class_exists($class_name, false) && is_readable($path)) include_once($path);
-
- if($class_name && class_exists($class_name, false)) return $class_name;
- }
-
- // fallback to original dispatch location if any
- if($location === 'override')
- {
- // check for real location
- if(($location = eDispatcher::getModuleRealLocation($module)) === null) return false;
- }
-
- if($location !== 'override')
- {
- $path = self::getControllerPath($module, $controllerName, $location, false);
-
- $class_name = self::getControllerClass($module, $controllerName, $location);
- if(!$class_name) return false;
-
- if(!class_exists($class_name, false) && is_readable($path)) include_once($path);
-
- if(class_exists($class_name, false)) return $class_name;
- }
- return false;
- }
-
- /**
- * Automated version of self::isDispatchableModule()
- * @param eRequest $request
- * @param boolean $checkReflection deep check - proper subclassing, action
- * @param boolean $checkOverride try override controller folder first
- * @return mixed class name OR false if not dispatchable
- */
- public function isDispatchable(eRequest $request, $checkReflection = false, $checkOverride = true)
- {
- $location = self::getDispatchLocation($request->getModuleName());
-
- $controllerName = $request->getControllerName();
- $moduleName = $request->getModuleName();
- $className = false;
-
- // dispatch based on url_config preference value, if config location is override and there is no
- // override controller, additional check against real controller location will be made
- if($location)
- {
- $className = $this->isDispatchableModule($moduleName, $controllerName, $location, $checkOverride);
- }
- //else
- //{
- # Disable plugin check for routes with no config info - prevent calling of non-installed plugins
- # We may allow this for plugins which don't have plugin.xml in the future
- // $className = $this->isDispatchableModule($moduleName, $controllerName, 'plugin', $checkOverride);
- // if(!$className)
- //$className = $this->isDispatchableModule($moduleName, $controllerName, 'core', $checkOverride);
- //}
-
- if(empty($className)) return false;
- elseif(!$checkReflection) return $className;
-
- $rfl = new ReflectionClass($className);
- $method = $request->getActionMethodName();
- if($rfl->isSubclassOf('eController') && $rfl->hasMethod($method) && $rfl->getMethod($method)->isPublic() && !$rfl->getMethod($method)->isStatic())
- return $className;
-
- return false;
- }
-
- /**
- * Get class name of a given module config
- *
- * @param string $module valid module name
- * @param string $location core|plugin|override[/custom]
- * @return string controller path
- */
- public static function getConfigClassName($module, $location)
- {
- $tmp = explode('/', $location);
- $custom = '';
- $location = $tmp[0];
- if(isset($tmp[1]) && !empty($tmp[1]))
- {
- $custom = $tmp[1].'_';
- }
- unset($tmp);
- $module .= '_';
-
- // we need to prepend location to avoid namespace colisions
- return $location.'_'.$module.$custom.'url';
- }
-
- /**
- * Get config object for a module
- *
- * @param string $module valid module name
- * @param string $location core|plugin|override[/custom]
- * @return eUrlConfig
- */
- public static function getConfigObject($module, $location = null)
- {
- if(null === $location)
- {
- $location = self::getModuleConfigLocation($module);
- if(!$location) return null;
- }
- $reg = $module.'/'.$location;
- if(isset(self::$_configObjects[$reg])) return self::$_configObjects[$reg];
- $className = self::getConfigClassName($module, $location);
- if(!class_exists($className, false))
- {
- $path = self::getConfigPath($module, $location, false);
- if(!is_readable($path)) return null;
- include_once($path);
-
- if(!class_exists($className, false)) return null;
- }
- $obj = new $className();
- $obj->init();
- self::$_configObjects[$reg] = $obj;
- $obj = null;
-
- return self::$_configObjects[$reg];
- }
-
- /**
- * Auto discover module location from stored in core prefs data
- * @param string $module
- */
- public static function getModuleConfigLocation($module)
- {
- //retrieve from config prefs
- return e107::findPref('url_config/'.$module, '');
- }
-
- /**
- * Auto discover module location from stored in core prefs data
- * @param string $module
- */
- public static function getDispatchLocation($module)
- {
- //retrieve from prefs
- $location = self::getModuleConfigLocation($module);
- if(!$location) return null;
-
- if(($pos = strpos($location, '/'))) //can't be 0
- {
- return substr($location, 0, $pos);
- }
- return $location;
- }
-
-
- /**
- * Auto discover module real location (and not currently set from url adminsitration) from stored in core prefs data
- * @param string $module
- */
- public static function getModuleRealLocation($module)
- {
- //retrieve from prefs
- $searchArray = e107::findPref('url_modules');
- if(!$searchArray) return null;
-
- $search = array('core', 'plugin', 'override');
-
- foreach ($search as $location)
- {
- $_searchArray = vartrue($searchArray[$location], array());
- if(in_array($module, $_searchArray)) return $location;
- }
- return null;
- }
- }
-
- /**
- * URL manager - parse and create URLs based on rules set
- * Inspired by Yii Framework UrlManager <www.yiiframework.com>
- */
- class eRouter
- {
- /**
- * Configuration array containing all available syste routes and route object configuration values
- * @var array
- */
- protected $_rules = array();
-
- /**
- * List of all system wide available aliases
- * This includes multi-lingual configurations as well
- * @var array
- */
- protected $_aliases = array();
-
- /**
- * Cache for rule objects
- * @var array
- */
- protected $_parsedRules = array(); // array of rule objects
-
- /**
- * Global config values per rule set
- * @var array
- */
- protected $_globalConfig = array();
-
- /**
- * Module name which is used for site main namespace
- * Example mysite.com/news/News Item => converted to mysite.com/News Item
- * NOTE: Could be moved to rules config
- *
- * @var string
- */
- protected $_mainNsModule = '';
-
- /**
- * Default URL suffix - to be added to end of all urls (e.g. '.html')
- * This value can be overridden per rule item
- * NOTE could be moved to rules config only
- * @var string
- */
- public $urlSuffix = '';
-
- /**
- * @var string GET variable name for route. Defaults to 'route'.
- */
- public $routeVar = 'route';
-
- /**
- * @var string
- */
- const FORMAT_GET = 'get';
-
- /**
- * @var string
- */
- const FORMAT_PATH = 'path';
-
- /**
- * @var string
- */
- private $_urlFormat = self::FORMAT_PATH;
-
- /**
- * Not found route
- * @var string
- */
- public $notFoundRoute = 'system/error/notfound';
-
- protected $_defaultAssembleOptions = array('full' => false, 'amp' => '&', 'equal' => '=', 'encode' => true);
-
- /**
- * Not found URL - used when system route not found and 'url_error_redirect' core pref is true
- * TODO - user friendly URL ('/system/404') when system config is ready ('/system/404')
- * @var string
- */
- public $notFoundUrl = 'system/error/404?type=routeError';
-
- public function __construct()
- {
- $this->_init();
- }
-
- /**
- * Init object
- * @return void
- */
- protected function _init()
- {
- // Gather all rules, add-on info, cache, module for main namespace etc
- $this->_loadConfig()
- ->setAliases();
- // we need config first as setter does some checks if module can be set as main
- $this->setMainModule(e107::getPref('url_main_module', ''));
- }
-
- /**
- * Set module for default namespace
- * @param string $module
- * @return eRouter
- */
- public function setMainModule($module)
- {
- if(!$module || !$this->isModule($module) || !$this->getConfigValue($module, 'allowMain')) return $this;
- $this->_mainNsModule = $module;
- return $this;
- }
-
- /**
- * Get main url namespace module
- * @return string
- */
- public function getMainModule()
- {
- return $this->_mainNsModule;
- }
-
- /**
- * Check if given module is the main module
- * @param string $module
- * @return boolean
- */
- public function isMainModule($module)
- {
- return ($this->_mainNsModule === $module);
- }
-
-
- /**
- * @return string get|path
- */
- public function getUrlFormat()
- {
- return $this->_urlFormat;
- }
-
- /**
- * Load config and url rules, if not available - build it on the fly
- * @return eRouter
- */
- protected function _loadConfig()
- {
- if(!is_readable(e_CACHE_URL.'config.php')) $config = $this->buildGlobalConfig();
- else $config = include(e_CACHE_URL.'config.php');
-
- if(!$config) $config = array();
-
- $rules = array();
-
- foreach ($config as $module => $c)
- {
- $rules[$module] = $c['rules'];
- unset($config[$module]['rules']);
- $config[$module] = $config[$module]['config'];
- }
- $this->_globalConfig = $config;
- $this->setRuleSets($rules);
-
- return $this;
- }
-
- public static function clearCache()
- {
- if(file_exists(e_CACHE_URL.'config.php'))
- {
- @unlink(e_CACHE_URL.'config.php');
- }
- }
-
- /**
- * Build unified config.php
- */
- public function buildGlobalConfig($save = true)
- {
- $active = e107::getPref('url_config', array());
-
- $config = array();
- foreach ($active as $module => $location)
- {
- $_config = array();
- $obj = eDispatcher::getConfigObject($module, $location);
- $path = eDispatcher::getConfigPath($module, $location, true);
-
- if(null !== $obj)
- {
- $_config = $obj->config();
- $_config['config']['configPath'] = $path;
- $_config['config']['configClass'] = eDispatcher::getConfigClassName($module, $location);
- }
- if(!isset($_config['config'])) $_config['config'] = array();
-
- $_config['config']['location'] = $location;
- if(!isset($_config['config']['format']) || !in_array($_config['config']['format'], array(self::FORMAT_GET, self::FORMAT_PATH)))
- {
- $_config['config']['format'] = $this->getUrlFormat();
- }
-
- if(!isset($_config['rules'])) $_config['rules'] = array();
-
- foreach ($_config['rules'] as $pattern => $rule)
- {
- if(!is_array($rule))
- {
- $_config['rules'][$pattern] = array($rule);
- }
- }
-
- $config[$module] = $_config;
- }
-
- if($save)
- {
- $fileContent = '<?php'."\n### Auto-generated - DO NOT EDIT ### \nreturn ";
- $fileContent .= trim(var_export($config, true)).';';
-
- file_put_contents(e_CACHE_URL.'config.php', $fileContent);
- }
- return $config;
- }
-
- /**
- * Retrieve config array from a given system path
- * @param string $path
- * @param string $location core|plugin|override
- */
- public static function adminReadConfigs($path, $location = null)
- {
- $file = e107::getFile(false);
- $ret = array();
-
- $file->mode = 'fname';
- $files = $file->setFileInfo('fname')
- ->get_files($path, '^([a-z_]{1,}_)?url\.php$');
-
-
- foreach ($files as $file)
- {
- if(null === $location)
- {
- $c = eRouter::file2config($file, $location);
- if($c) $ret[] = $c;
- continue;
- }
- $ret[] = eRouter::file2config($file, $location);
- }
- return $ret;
- }
-
- /**
- * Convert filename to configuration string
- * @param string $filename
- * @param string $location core|plugin|override
- */
- public static function file2config($filename, $location = '')
- {
- if($filename == 'url.php') return $location;
- if($location) $location .= '/';
- return $location.substr($filename, 0, strrpos($filename, '_'));
- }
-
- /**
- * Detect all available system url modules, used as a map on administration configuration path
- * and required (same structure) {@link from eDispatcher::adminBuildConfig())
- * This is a very liberal detection, as it doesn't require config file.
- * It goes through both config and dispatch locations and registers directory tree as modules
- * The only exception are plugins - if plugin requires install (plugin.xml) and it is not installed,
- * it won't be registered
- * Another important thing is - core has always higher priority, as plugins are not allowed to
- * directly override core modules. At this moment, core modules could be overloaded only via override configs (e107_core/override/url/)
- * and controllers (e107_core/override/controllers)
- * This array is stored as url_modules core preference
- *
- * @param string $type possible values are all|plugin|core|override
- * @return array available system url modules stored as url_modules core preference
- */
- public static function adminReadModules($type = 'all')
- {
- $f = e107::getFile();
- $ret = array('core' => array(), 'plugin' => array(), 'override' => array());
-
- if($type == 'all' || $type = 'core')
- {
- $location = eDispatcher::getDispatchLocationPath('core');
- // search for controllers first
- $ret['core'] = $f->get_dirs($location);
-
- // merge with configs
- $configArray = $f->get_dirs(eDispatcher::getConfigPath('*', 'core'));
- foreach ($configArray as $config)
- {
- if(!in_array($config, $ret['core']))
- {
- $ret['core'][] = $config;
- }
- }
- sort($ret['core']);
- }
-
- if($type == 'all' || $type = 'plugin')
- {
- $plugins = $f->get_dirs(e_PLUGIN);
- foreach ($plugins as $plugin)
- {
- // DON'T ALLOW PLUGINS TO OVERRIDE CORE!!!
- // This will be possible in the future under some other, more controllable form
- if(in_array($plugin, $ret['core'])) continue;
-
- $location = eDispatcher::getDispatchLocationPath('plugin', $plugin);
- $config = eDispatcher::getConfigPath($plugin, 'plugin');
-
- if(e107::isInstalled($plugin))
- {
- if(is_dir($location) || is_readable($config))
- {
- $ret['plugin'][] = $plugin;
- }
- continue;
- }
-
- // Register only those who don't need install and may be dispatchable
- if((!is_readable(e_PLUGIN.$plugin.'/plugin.php') && !is_readable(e_PLUGIN.$plugin.'/plugin.xml')))
- {
- if(is_dir($location) || is_readable($config))
- {
- $ret['plugin'][] = $plugin;
- }
- }
- }
- sort($ret['plugin']);
- }
-
- if($type == 'all' || $type = 'override')
- {
- // search for controllers first
- $location = eDispatcher::getDispatchLocationPath('override');
- $ret['override'] = $f->get_dirs($location);
-
- // merge with configs
- $configArray = $f->get_dirs(eDispatcher::getConfigPath('*', 'override'));
- foreach ($configArray as $config)
- {
- if(!in_array($config, $ret['override']))
- {
- $ret['override'][] = $config;
- }
- }
- sort($ret['override']);
-
- // remove not installed plugin locations, possible only for 'all' type
- if($type == 'all')
- {
- foreach ($ret['override'] as $i => $l)
- {
- // it's a plugin override, but not listed in current plugin array - remove
- if(in_array($l, $plugins) && !in_array($l, $ret['plugin']))
- {
- unset($ret['override'][$i]);
- }
- }
- }
- }
-
- return $ret;
- }
-
- /**
- * Rebuild configuration array, stored as url_config core preference
- * More strict detection compared to {@link eDispatcher::adminReadModules()}
- * Current flat array containing config locations per module are rebuilt so that new
- * modules are registered, missing modules - removed. Additionally fallback to the default location
- * is done if current user defined location is not readable
- * @see eDispatcher::adminReadModules()
- * @param array current configuration array (url_config core preference like)
- * @param array available URL modules as detected by {@link eDispatcher::adminReadModules()} and stored as url_modules core preference value
- * @return array new url_config array
- */
- public static function adminBuildConfig($current, $adminReadModules = null)
- {
- if(null === $adminReadModules) $adminReadModules = self::adminReadModules();
-
- $ret = array();
- $all = array_unique(array_merge($adminReadModules['core'], $adminReadModules['plugin'], $adminReadModules['override']));
- foreach ($all as $module)
- {
- if(isset($current[$module]))
- {
- // current contains custom (readable) config location e.g. news => core/rewrite
- if(strpos($current[$module], '/') !== false && is_readable(eDispatcher::getConfigPath($module, $current[$module])))
- {
- $ret[$module] = $current[$module];
- continue;
- }
-
- // in all other cases additional re-check will be made - see below
- }
-
- if(in_array($module, $adminReadModules['override']))
- {
- // core check
- if(in_array($module, $adminReadModules['core']))
- {
- $mustHave = is_readable(eDispatcher::getConfigPath($module, 'core'));
- $has = is_readable(eDispatcher::getConfigPath($module, 'override'));
-
- // No matter if it must have, it has e_url config
- if($has) $ret[$module] = 'override';
- // It must have but it doesn't have e_url config, fallback
- elseif($mustHave && !$has) $ret[$module] = 'core';
- // Rest is always core as controller override is done on run time
- else $ret[$module] = 'core';
- }
- // plugin check
- elseif(in_array($module, $adminReadModules['plugin']))
- {
- $mustHave = is_readable(eDispatcher::getConfigPath($module, 'plugin'));
- $has = is_readable(eDispatcher::getConfigPath($module, 'override'));
-
- // No matter if it must have, it has e_url config
- if($has) $ret[$module] = 'override';
- // It must have but it doesn't have e_url config, fallback
- elseif($mustHave && !$has) $ret[$module] = 'plugin';
- // Rest is always plugin as config is most important, controller override check is done on run time
- else $ret[$module] = 'plugin';
- }
- // standalone override module
- else
- {
- $ret[$module] = 'override';
- }
-
- }
- // default core location
- elseif(in_array($module, $adminReadModules['core']))
- {
- $ret[$module] = 'core';
- }
- // default plugin location
- elseif(in_array($module, $adminReadModules['plugin']))
- {
- $ret[$module] = 'plugin';
- }
- }
- return $ret;
- }
-
- /**
- * Detect available config locations (readable check), based on available url_modules {@link eDispatcher::adminReadModules()} core preference arrays
- * Used to rebuild url_locations core preference value
- * @see eDispatcher::adminBuildConfig()
- * @see eDispatcher::adminReadModules()
- * @param array $available {@link eDispatcher::adminReadModules()} stored as url_modules core preference
- * @return array available config locations, stored as url_locations core preference
- */
- public static function adminBuildLocations($available = null)
- {
- $ret = array();
- if(null === $available) $available = self::adminReadModules();
-
- $fl = e107::getFile();
-
- // Core
- foreach ($available['core'] as $module)
- {
- // Default module
- $ret[$module] = array('core');
-
- // read sub-locations
- $path = eDispatcher::getConfigLocationPath($module, 'core');
- //$sub = $fl->get_dirs($path);
- $sub = eRouter::adminReadConfigs($path);
-
- if($sub)
- {
- foreach ($sub as $moduleSub)
- {
- // auto-override: override available (controller or url config), check for config
- if(in_array($module, $available['override']) && is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
- {
- $ret[$module][] = 'override/'.$moduleSub;
- }
- // no override available, register the core location
- elseif(is_readable(eDispatcher::getConfigPath($module, 'core/'.$moduleSub)))
- {
- $ret[$module][] = 'core/'.$moduleSub;
- }
- }
- }
- }
-
-
- // Plugins
- foreach ($available['plugin'] as $module)
- {
- // Default module
- $ret[$module] = array('plugin');
-
- // read sub-locations
- $path = eDispatcher::getConfigLocationPath($module, 'plugin');
- //$sub = $fl->get_dirs($path);
- $sub = eRouter::adminReadConfigs($path);
-
- if($sub)
- {
- foreach ($sub as $moduleSub)
- {
- // auto-override: override available (controller or url config), check for config
- if(in_array($module, $available['override']) && is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
- {
- $ret[$module][] = 'override/'.$moduleSub;
- }
- // no override available, register the core location
- elseif(is_readable(eDispatcher::getConfigPath($module, 'plugin/'.$moduleSub)))
- {
- $ret[$module][] = 'plugin/'.$moduleSub;
- }
- }
- }
- }
-
- // Go through all overrides, register those who don't belong to core & plugins as standalone core modules
- foreach ($available['override'] as $module)
- {
- // either it is a core/plugin module or e_url.php is not readable - continue
- if(in_array($module, $available['core']) || in_array($module, $available['plugin']))
- {
- continue;
- }
-
- // Default module
- $ret[$module] = array('override');
-
- // read sub-locations
- $path = eDispatcher::getConfigLocationPath($module, 'override');
- //$sub = $fl->get_dirs($path);
- $sub = eRouter::adminReadConfigs($path);
-
- if($sub)
- {
- foreach ($sub as $moduleSub)
- {
- if(is_readable(eDispatcher::getConfigPath($module, 'override/'.$moduleSub)))
- {
- $ret[$module][] = 'override/'.$moduleSub;
- }
- }
- }
- }
-
- return $ret;
- }
-
- /**
- * Match current aliases against currently available module and languages
- * @param array $currentAliases url_aliases core preference
- * @param array $currentConfig url_config core preference
- * @return array cleaned aliases
- */
- public static function adminSyncAliases($currentAliases, $currentConfig)
- {
- if(empty($currentAliases)) return array();
-
- $modules = array_keys($currentConfig);
-
- // remove non existing languages
- $lng = e107::getLanguage();
- $lanList = $lng->installed();
-
- if(is_array($currentAliases))
- {
- foreach ($currentAliases as $lanCode => $aliases)
- {
- $lanName = $lng->convert($lanCode);
- if(!$lanName || !in_array($lanName, $lanList))
- {
- unset($currentAliases[$lanCode]);
- continue;
- }
-
- // remove non-existing modules
- foreach ($aliases as $alias => $module)
- {
- if(!isset($currentConfig[$module])) unset($currentAliases[$lanCode][$alias]);
- }
- }
- }
- return $currentAliases;
- }
-
- /**
- * Retrieve global configuration array for a single or all modules
- * @param string $module system module
- * @return array configuration
- */
- public function getConfig($module = null)
- {
- if(null === $module) return $this->_globalConfig;
-
- return isset($this->_globalConfig[$module]) ? $this->_globalConfig[$module] : array();
- }
-
- /**
- * Retrieve single value from a module global configuration array
- * @param string $module system module
- * @return array configuration
- */
- public function getConfigValue($module, $key, $default = null)
- {
- return isset($this->_globalConfig[$module]) && isset($this->_globalConfig[$module][$key]) ? $this->_globalConfig[$module][$key] : $default;
- }
-
- /**
- * Get system name of a module by its alias
- * Returns null if $alias is not an existing alias
- * @param string $alias
- * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
- * @return string module
- */
- public function getModuleFromAlias($alias, $lan = null)
- {
- if($lan) return e107::findPref('url_aliases/'.$lan.'/'.$alias, null);
- return (isset($this->_aliases[$alias]) ? $this->_aliases[$alias] : null);
- }
-
- /**
- * Get alias name for a module
- * Returns null if module doesn't have an alias
- * @param string $module
- * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
- * @return string alias
- */
- public function getAliasFromModule($module, $lan = null)
- {
- if($lan)
- {
- $aliases = e107::findPref('url_aliases/'.$lan, array());
- return (in_array($module, $aliases) ? array_search($module, $aliases) : null);
- }
- return (in_array($module, $this->_aliases) ? array_search($module, $this->_aliases) : null);
- }
-
- /**
- * Check if alias exists
- * @param string $alias
- * @param string $lan optional language alias. Example $lan = 'bg' (search for Bulgarian aliases)
- * @return boolean
- */
- public function isAlias($alias, $lan = null)
- {
- if($lan)
- {
- $aliases = e107::findPref('url_aliases/'.$lan, array());
- return isset($aliases[$alias]);
- }
- return isset($this->_aliases[$alias]);
- }
-
- /**
- * Check if there is an alias for provided module
- * @param string $module
- * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
- * @return boolean
- */
- public function hasAlias($module, $lan = null)
- {
- if($lan)
- {
- $aliases = e107::findPref('url_aliases/'.$lan, array());
- return in_array($module, $aliases);
- }
- return in_array($module, $this->_aliases);
- }
-
- /**
- * Get all available module aliases
- * @param string $lan optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
- * @return array
- */
- public function getAliases($lanCode = null)
- {
- if($lan)
- {
- return e107::findPref('url_aliases/'.$lan, array());
- }
- return $this->_aliases;
- }
-
- /**
- * Set module aliases
- * @param array $aliases
- * @return eRouter
- */
- public function setAliases($aliases = null)
- {
- if(null === $aliases)
- {
- $lanCode = e107::getLanguage()->convert(e_LANGUAGE);
-
- $aliases = e107::findPref('url_aliases/'.$lanCode, array());
- }
- $this->_aliases = $aliases;
-
- return $this;
- }
-
- /**
- * Check if provided module is present in the rules config
- * @param string module
- * @return boolean
- */
- public function isModule($module)
- {
- return isset($this->_globalConfig[$module]);
- }
-
- /**
- * Check if the passed value is valid module or module alias, returns system module
- * or null on failure
- * @param string $module
- * @param boolean $strict check for existence if true
- * @return string module
- */
- public function retrieveModule($module, $strict = true)
- {
- if($this->isAlias($module))
- $module = $this->getModuleFromAlias($module);
-
- if($strict && (!$module || !$this->isModule($module)))
- return null;
-
- return $module;
- }
-
- /**
- * Set rule config for this instance
- * @param array $rules
- * @return void
- */
- public function setRuleSets($rules)
- {
- $this->_rules = $rules;
- }
-
- /**
- * Retrieve rule set for a module
- * @param string $module
- */
- public function getRuleSet($module)
- {
- return (isset($this->_rules[$module]) ? $this->_rules[$module] : array());
- }
-
- /**
- * Get all rule sets
- */
- public function getRuleSets()
- {
- return $this->_rules;
- }
-
- /**
- * Retrive array of eUrlRule objects for given module
- */
- public function getRules($module)
- {
- return $this->_processRules($module);
- }
-
- /**
- * Process rule set array, create rule objects
- * TODO - rule cache
- * @param string $module
- * @return array processed rule set
- */
- protected function _processRules($module)
- {
- if(!$this->isModule($module)) return array();
-
- if(!isset($this->_parsedRules[$module]))
- {
- $rules = $this->getRuleSet($module);
- $config = $this->getConfig($module);
- $this->_parsedRules[$module] = array();
- $map = array('urlSuffix' => 'urlSuffix', 'legacy' => 'legacy', 'legacyQuery' => 'legacyQuery', 'mapVars' => 'mapVars', 'allowVars' => 'allowVars', 'matchValue' => 'matchValue');
- foreach ($rules as $pattern => $set)
- {
- foreach ($map as $key => $value)
- {
- if(!isset($set[$value]) && isset($config[$key]))
- {
- $set[$value] = $config[$key];
- }
- }
- $this->_parsedRules[$module][$pattern] = $this->createRule($set, $pattern);
- }
- }
- return $this->_parsedRules[$module];
- }
-
- /**
- * Create rule object
- *
- * @param string $route
- * @param string|array $pattern
- * @param boolean $cache
- * @return eUrlRule
- */
- protected function createRule($route, $pattern = null, $cache = false)
- {
- return new eUrlRule($route, $pattern, $cache);
- }
-
- /**
- * Route current request
- * @param eRequest $request
- * @return boolean
- */
- public function route(eRequest $request, $checkOnly = false)
- {
- $request->routed = false;
-
- if(isset($_GET[$this->routeVar]))
- {
- $rawPathInfo = $_GET[$this->routeVar];
- unset($_GET[$this->routeVar]);
- $this->_urlFormat = self::FORMAT_GET;
- }
- else
- {
- $rawPathInfo = rawurldecode($request->getPathInfo());
- //$this->_urlFormat = self::FORMAT_PATH;
- }
-
- // Route to front page - index/index/index route
- if(!$rawPathInfo && (!$this->getMainModule() || empty($_GET)))
- {
- // front page settings will be detected and front page will be rendered
- $request->setRoute('index/index/index');
- $request->addRouteHistory($rawPathInfo);
- $request->routed = true;
- return true;
- }
-
- // max number of parts is actually 4 - module/controller/action/[additional/pathinfo/vars], here for reference only
- $parts = $rawPathInfo ? explode('/', $rawPathInfo, 4) : array();
-
- // find module - check aliases
- $module = $this->retrieveModule($parts[0]);
- $mainSwitch = false;
-
- // no module found, switch to Main module (pref) if available
- if(null === $module && $this->getMainModule() && $this->isModule($this->getMainModule()))
- {
- $module = $this->getMainModule();
- $rawPathInfo = $module.'/'.$rawPathInfo;
- array_unshift($parts, $module);
- $mainSwitch = true;
- }
-
- $request->routePathInfo = $rawPathInfo;
-
- // valid module
- if(null !== $module)
- {
- // we have valid module
- $config = $this->getConfig($module);
-
- // set legacy state
- eFront::isLegacy(varset($config['legacy']));
-
- // Don't allow single entry if required by module config
- if(vartrue($config['noSingleEntry']))
- {
- $request->routed = true;
- if(!eFront::isLegacy())
- {
- $request->setRoute($this->notFoundRoute);
- return false;
- }
- // legacy entry point - include it later in the bootstrap, legacy query string will be set to current
- $request->addRouteHistory($rawPathInfo);
- return true;
- }
-
- // URL format - the one set by current config overrides the auto-detection
- $format = isset($config['format']) && $config['format'] ? $config['format'] : $this->getUrlFormat();
-
- //remove leading module, unnecessary overhead while matching
- array_shift($parts);
- $rawPathInfo = $parts ? implode('/', $parts) : '';
- $pathInfo = $this->removeUrlSuffix($rawPathInfo, $this->urlSuffix);
-
- // retrieve rules if any and if needed
- $rules = $format == self::FORMAT_PATH ? $this->getRules($module) : array();
-
- // Further parsing may still be needed
- if(empty($rawPathInfo))
- {
- $rawPathInfo = $pathInfo;
- }
-
- // parse callback
- if(vartrue($config['selfParse']))
- {
- // controller/action[/additional/parms]
- if(vartrue($config['urlSuffix'])) $rawPathInfo = $this->removeUrlSuffix($rawPathInfo, $config['urlSuffix']);
- $route = $this->configCallback($module, 'parse', array($rawPathInfo, $_GET, $request, $this, $config), $config['location']);
- }
- // default module route
- elseif($format == self::FORMAT_GET || !$rules)
- {
- $route = $pathInfo;
- }
- // rules available - try to match an Url Rule
- elseif($rules)
- {
- foreach ($rules as $rule)
- {
- $route = $rule->parseUrl($this, $request, $pathInfo, $rawPathInfo);
- if($route !== false)
- {
- eFront::isLegacy($rule->legacy); // legacy include override
-
- if($rule->parseCallback)
- {
- $this->configCallback($module, $rule->parseCallback, array($request), $config['location']);
- }
- // parse legacy query string if any
- if(null !== $rule->legacyQuery)
- {
- $obj = eDispatcher::getConfigObject($module, $config['location']);
- // eUrlConfig::legacyQueryString set as legacy string by default in eUrlConfig::legacy() method
- $vars = new e_vars($request->getRequestParams());
- $vars->module = $module;
- $vars->controller = $request->getController();
- $vars->action = $request->getAction();
- if($rule->allowVars)
- {
- foreach ($rule->allowVars as $key)
- {
- if(isset($_GET[$key]) && !$request->isRequestParam($key))
- {
- // sanitize
- $vars->$key = preg_replace('/[^\d\w\-]/', '', $_GET[$key]);
- }
- }
- }
- $obj->legacyQueryString = e107::getParser()->simpleParse($rule->legacyQuery, $vars, '0');
- unset($vars, $obj);
- }
- break;
- }
- }
- }
-
- // append module to be registered in the request object
- if(false !== $route)
- {
- // don't modify if true - request directly modified by config callback
- if(!$request->routed)
- {
- if(eFront::isLegacy()) $this->configCallback($module, 'legacy', array($route, $request), $config['location']);
- $route = $module.'/'.$route;
- }
- }
- // No route found, we didn't switched to main module auto-magically
- elseif(!$mainSwitch && vartrue($config['errorRoute']))
- {
- $route = !$checkOnly ? $module.'/'.$config['errorRoute'] : false;
- }
-
- }
-
- // final fallback
- if(!vartrue($route))
- {
- if($request->routed)
- {
- $route = $request->getRoute();
- }
-
- if(!$route)
- {
- $route = $this->notFoundRoute;
- eFront::isLegacy(''); // reset legacy - not found route isn't legacy call
- $request->routed = true;
- if($checkOnly) return false;
- ## Global redirect on error option
- if(e107::getPref('url_error_redirect', false) && $this->notFoundUrl)
- {
- $redirect = $this->assemble($this->notFoundUrl, '', 'encode=0&full=1');
- //echo $redirect; exit;
- e107::getRedirect()->redirect($redirect, true, 404);
- }
- }
- }
-
- $request->setRoute($route);
- $request->addRouteHistory($route);
- $request->routed = true;
- return true;
- }
-
- /**
- * And more BC
- * Checks and does some addtional logic if registered module is of type legacy
- * @param eRequest $request
- * @return void
- */
- public function checkLegacy(eRequest $request)
- {
- $module = $request->getModule();
-
- // forward from controller to a legacy module - bad stuff
- if(!$request->isDispatched() && $this->getConfigValue($module, 'legacy'))
- {
- eFront::isLegacy($this->getConfigValue($module, 'legacy'));
-
- $url = $this->assemble($request->getRoute(), $request->getRequestParams());
- $request->setRequestInfo($url)->setPathInfo(null)->setRoute(null);
-
- $_GET = $request->getRequestParams();
- $_SERVER['QUERY_STRING'] = http_build_query($request->getRequestParams(), null, '&');
-
- // Infinite loop impossible, as dispatcher will break because of the registered legacy path
- $this->route($request);
- }
- }
-
- /**
- * Convenient way to call config methods
- */
- public function configCallback($module, $callBack, $params, $location)
- {
- if(null == $location) $location = eDispatcher::getModuleConfigLocation($module);
- if(!$module || !($obj = eDispatcher::getConfigObject($module, $location))) return false;
-
- return call_user_func_array(array($obj, $callBack), $params);
- }
-
- /**
- * Convert assembled url to shortcode
- *
- * @param string $route
- * @param array $params
- * @param array $options {@see eRouter::$_defaultAssembleOptions}
- */
- public function assembleSc($route, $params = array(), $options = array())
- {
- //if(is_string($options)) parse_str($options, $options);
- $url = $this->assemble($route, $params, $options);
- return e107::getParser()->createConstants($url, 'mix');
- }
-
- /**
- * Assemble system URL
- * Examples:
- * <?php
- * $router->assemble('/'); // index page URL e.g. / or /site_folder/
- * $router->assemble('news/view/item?id=1'); // depends on current news config, possible return value is /news/1
- * $router->assemble('*', 'id=1'); // use current request info - /module/controller/action?id=1
- * $router->assemble('* /* /newaction'); // (NO EMPTY SPACES) change only current action - /module/controller/newaction
- * $newsItem = array('news_id' => 1, 'news_sef' => 'My Title', ...); // as retrieved from DB
- * $router->assemble('news/view/item', $newsItem); // All unused key=>values will be removed and NOT appended as GET vars
- *
- * @param string $route
- * @param array $params
- * @param array $options {@see eRouter::$_defaultAssembleOptions}
- */
- public function assemble($route, $params = array(), $options = array())
- {
- // TODO - url options
- $request = eFront::instance()->getRequest();
- if(is_string($options)) parse_str($options, $options);
- $o…
Large files files are truncated, but you can click here to view the full file