/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
- <?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);
- $options = array_merge($this->_defaultAssembleOptions, $options);
- $base = ($options['full'] ? SITEURLBASE : '').$request->getBasePath();
-
- $anc = '';
-
-
- if(is_string($params)) parse_str($params, $params);
- if(isset($params['#']))
- {
- $anc = '#'.$params['#'];
- usnet($params['#']);
- }
-
- // Config independent - Deny parameter keys, useful for directly denying sensitive data e.g. password db fields
- if(isset($options['deny']))
- {
- $list = array_map('trim', explode(',', $options['deny']));
- foreach ($list as $value)
- {
- unset($params[$value]);
- }
- unset($list);
- }
-
- // Config independent - allow parameter keys, useful to directly allow data (and not to rely on config allowVars) e.g. when retrieved from db
- if(isset($options['allow']))
- {
- $list = array_map('trim', explode(',', $options['allow']));
- $_params = $params;
- $params = array();
- foreach ($list as $value)
- {
- if(isset($_params[$value])) $params[$value] = $_params[$value];
- }
- unset($list, $_params);
- }
-
- # Optional convenient masks for creating system URL's
- if($route === '/' || empty($route))
- {
- if($params)
- {
- $params = $this->createPathInfo($params, $options);
- return $base.'?'.$params;
- }
- return $base;
- }
- elseif(strpos($route, '?') !== false)
- {
- $tmp = explode('?', $route, 2);
- $route = $tmp[0];
- parse_str($tmp[1], $params);
- unset($tmp);
- }
-
- if($route === '*')
- {
- $route = $route = explode('/', $request->getRoute());
- }
- elseif(strpos($route, '*') !== false)
- {
- $route = explode('/', $route, 3);
- if($route[0] === '*') $route[0] = $request->getModule();
- if(isset($route[1]) && $route[1] === '*') $route[1] = $request->getController();
- }
- else
- {
- $route = explode('/', $route, 3);
- }
-
- // we don't know anything about this route, just build it blind
- if(!$this->isModule($route[0]))
- {
- if($params)
- {
- $params = $this->createPathInfo($params, $options);
- return $base.implode('/', $route).'?'.$params;
- }
- return $base.implode('/', $route);
- }
-
- # fill in index when needed - XXX not needed, may be removed soon
- switch (count($route))
- {
- case 1:
- $route[1] = 'index';
- $route[2] = 'index';
- break;
- case 2:
- $route[2] = 'index';
- break;
- }
-
- # aliases
- $module = $route[0];
- $config = $this->getConfig($module);
-
- $alias = $this->hasAlias($module, vartrue($options['lan'], null)) ? $this->getAliasFromModule($module, vartrue($options['lan'], null)) : $module;
- $route[0] = $alias;
- if($options['encode']) $alias = rawurlencode($alias);
-
- $format = isset($config['format']) && $config['format'] ? $config['format'] : self::FORMAT_GET;
-
- $urlSuffix = '';
-
- // Fix base url for legacy links
- if(vartrue($config['noSingleEntry'])) $base = $options['full'] ? SITEURL : e_HTTP;
- elseif(self::FORMAT_GET !== $config['format'])
- {
- $urlSuffix = $this->urlSuffix;
- if(isset($config['urlSuffix'])) $urlSuffix = $config['urlSuffix'];
- }
-
- // Create by config callback
- if(vartrue($config['selfCreate']))
- {
- $tmp = $this->configCallback($module, 'create', array(array($route[1], $route[2]), $params, $options), $config['location']);
-
- if(empty($tmp)) return '#not-found';
-
- if(is_array($tmp))
- {
- $route = $tmp[0];
- $params = $tmp[1];
-
- if($options['encode']) $route = array_map('rawurlencode', $route);
- $route = implode('/', $route);
-
- if(!$route)
- {
- $urlSuffix = '';
- if(!$this->isMainModule($module)) $route = $alias;
- }
- elseif (!$this->isMainModule($module))
- {
- $route = $alias.'/'.$route;
- }
-
- }
- else
- {
- // relative url returned
- return $base.$tmp.$anc;
- }
- unset($tmp);
-
- if($format === self::FORMAT_GET)
- {
- $params[$this->routeVar] = $route;
- $route = '';
- }
-
- if($params)
- {
- $params = $this->createPathInfo($params, $options);
- return $base.$route.$urlSuffix.'?'.$params.$anc;
- }
-
- return $base.$route.$urlSuffix.$anc;
- }
-
-
- // System URL create routine
- $rules = $this->getRules($module);
- if($format !== self::FORMAT_GET && !empty($rules))
- {
- foreach ($rules as $k => $rule)
- {
- if (($url = $rule->createUrl($this, array($route[1], $route[2]), $params, $options)) !== false)
- {
- return $base.rtrim(($this->isMainModule($module) ? '' : $alias.'/').$url, '/').$anc;
- }
- }
- }
-
- // default - module/controller/action
- if($this->isMainModule($module)) unset($route[0]);
- if($route[2] == 'index')
- {
- unset($route[2]);
- if($route[1] == 'index') unset($route[1]);
- }
-
- # Modify params if required
- if($params)
- {
- if(varset($config['mapVars']))
- {
- foreach ($config['mapVars'] as $srcKey => $dstKey)
- {
- if (isset($params[$srcKey]))
- {
- $params[$dstKey] = $params[$srcKey];
- unset($params[$srcKey]);
- }
- }
- }
-
- // false means - no vars are allowed, nothing to preserve here
- if(varset($config['allowVars']) === false) $params = array();
- // default empty array value - try to guess what's allowed - mapVars is the best possible candidate
- elseif(empty($config['allowVars']) && !empty($config['mapVars'])) $params = array_unique(array_values($config['mapVars']));
- // disallow everything but valid URL parameters
- if(!empty($config['allowVars']))
- {
- $copy = $params;
- $params = array();
- foreach ($config['allowVars'] as $key)
- {
- if(isset($copy[$key])) $params[$key] = $copy[$key];
- }
- unset($copy);
- }
-
- if($format === self::FORMAT_GET)
- {
- $urlSuffix = '';
- $copy = $params;
- $params = array();
- $params[$this->routeVar] = implode('/', $route);
- foreach ($copy as $key => $value)
- {
- $params[$key] = $value;
- }
- unset($copy);
- $route = array();
- }
- $params = $this->createPathInfo($params, $options);
- $route = implode('/', $route);
- if(!$route || $route == $alias) $urlSuffix = '';
- return $base.$route.$urlSuffix.'?'.$params.$anc;
- }
- $route = implode('/', $route);
- if(!$route || $route == $alias) $urlSuffix = '';
-
-
- return $format === self::FORMAT_GET ? $base.'?'.$this->routeVar.'='.$route.$anc : $base.$route.$urlSuffix.$anc;
- }
-
- /**
- * Alias of assemble()
- */
- public function url($route, $params = array())
- {
- return $this->assemble($route, $params);
- }
-
- /**
- * Creates a path info based on the given parameters.
- * XXX - maybe we can switch to http_build_query(), should be able to do everything we need in a much better way
- *
- * @param array $params list of GET parameters
- * @param array $options rawurlencode, equal, encode and amp settings
- * @param string $key this is used internally for recursive calls
- *
- * @return string the created path info
- */
- public function createPathInfo($params, $options, $key = null)
- {
- $pairs = array();
- $equal = $options['equal'];
- $encode = $options['encode'];
- $ampersand = !$encode && $options['amp'] == '&' ? '&' : $options['amp'];
- foreach ($params as $k => $v)
- {
- if (null !== $key) $k = $key.'['.rawurlencode($k).']';
-
- if (is_array($v)) $pairs[] = $this->createPathInfo($v, $options, $k);
- else
- {
- if(null === $v)
- {
- if($encode)
- {
- $k = null !== $key ? $k : rawurlencode($k);
- }
- $pairs[] = $k;
- continue;
- }
- if($encode)
- {
- $k = null !== $key ? $k : rawurlencode($k);
- $v = rawurlencode($v);
- }
- $pairs[] = $k.$equal.$v;
- }
- }
- return implode($ampersand, $pairs);
- }
-
- /**
- * Parses a path info into URL segments
- * Be sure to not use non-unique chars for equal and ampersand signs, or you'll break your URLs
- *
- * @param eRequest $request
- * @param string $pathInfo path info
- * @param string $equal
- * @param string $ampersand
- */
- public function parsePathInfo($pathInfo, $equal = '/', $ampersand = '/')
- {
- if ('' === $pathInfo) return;
-
- if ($equal != $ampersand) $pathInfo = str_replace($equal, $ampersand, $pathInfo);
- $segs = explode($ampersand, $pathInfo.$ampersand);
-
- $segs = explode('/', $pathInfo);
- $ret = array();
-
- for ($i = 0, $n = count($segs); $i < $n - 1; $i += 2)
- {
- $key = $segs[$i];
- if ('' === $key) continue;
- $value = $segs[$i + 1];
- // array support
- if (($pos = strpos($key, '[')) !== false && ($pos2 = strpos($key, ']', $pos + 1)) !== false)
- {
- $name = substr($key, 0, $pos);
- // numerical array
- if ($pos2 === $pos + 1)
- $ret[$name][] = $value;
- // associative array
- else
- {
- $key = substr($key, $pos + 1, $pos2 - $pos - 1);
- $ret[$name][$key] = $value;
- }
- }
- else
- {
- $ret[$key] = $value;
-
- }
- }
- return $ret;
- }
-
- /**
- * Removes the URL suffix from path info.
- * @param string $pathInfo path info part in the URL
- * @param string $urlSuffix the URL suffix to be removed
- *
- * @return string path info with URL suffix removed.
- */
- public function removeUrlSuffix($pathInfo, $urlSuffix)
- {
- if ('' !== $urlSuffix && substr($pathInfo, -strlen($urlSuffix)) === $urlSuffix) return substr($pathInfo, 0, -strlen($urlSuffix));
- else return $pathInfo;
- }
- }
-
- class eException extends Exception
- {
-
- }
-
- /**
- * Based on Yii Framework UrlRule handler <www.yiiframework.com>
- */
- class eUrlRule
- {
- /**
- *
- * For example, ".html" can be used so that the URL looks like pointing to a static HTML page.
- * Defaults to null, meaning using the value of {@link cl_shop_core_url::urlSuffix}.
- *
- * @var string the URL suffix used for this rule.
- */
- public $urlSuffix;
-
- /**
- * When this rule is used to parse the incoming request, the values declared in this property
- * will be injected into $_GET.
- *
- * @var array the default GET parameters (name=>value) that this rule provides.
- */
- public $defaultParams = array();
-
- /**
- * @var string module/controller/action
- */
- public $route;
-
- /**
- * @var array the mapping from route param name to token name (e.g. _r1=><1>)
- */
- public $references = array();
-
- /**
- * @var string the pattern used to match route
- */
- public $routePattern;
-
- /**
- * @var string regular expression used to parse a URL
- */
- public $pattern;
-
- /**
- * @var string template used to construct a URL
- */
- public $template;
-
- /**
- * @var array list of parameters (name=>regular expression)
- */
- public $params = array();
-
- /**
- * @var boolean whether the URL allows additional parameters at the end of the path info.
- */
- public $append;
-
- /**
- * @var array list of SourceKey=>DestinationKey associations
- */
- public $mapVars = array();
-
- /**
- * Numerical array of allowed parameter keys. If set, everything else will be wiped out from the passed parameter array
- * @var array
- */
- public $allowVars = array();
-
- /**
- * Should be values matched vs route patterns when assembling URLs
- * Warning SLOW when true!!!
- * @var mixed true or 1 for preg_match (extremely slower), or 'empty' for only empty check (better)
- */
- public $matchValue;
-
- /**
- * Method member of module config object, to be called after successful request parsing
- * @var string
- */
- public $parseCallback;
-
- /**
- * Shortcode path to the old entry point e.g. '{e_BASE}news.php'
- * @var string
- */
- public $legacy;
-
- /**
- * Template used for automated recognition of legacy QueryString (parsed via simpleParser with values of retrieved requestParameters)
- * @var string
- */
- public $legacyQuery;
-
- /**
- * Core regex templates
- * Example usage - route <var:{number}> will result in
- * @var array
- */
- public $regexTemplates = array(
- 'az' => '[A-Za-z]+', // NOTE - it won't match non-latin word characters!
- 'alphanum' => '[\w\pL]+',
- 'sefsecure' => '[\w\pL\s\-+.,]+',
- 'secure' => '[^\/\'"\\<%]+',
- 'number' => '[\d]+',
- 'username' => '[\w\pL.\-\s!,]+', // TODO - should equal to username pattern, sync it
- 'azOptional' => '[A-Za-z]{0,}',
- 'alphanumOptional' => '[\w\pL]{0,}',
- 'sefsecureOptional' => '[\w\pL\s\-+.,]{0,}',
- 'secureOptional' => '[^\/\'"\\<%]{0,}',
- 'numberOptional' => '[\d]{0,}',
- 'usernameOptional' => '[\w\pL.\-\s!,]{0,}', // TODO - should equal to username pattern, sync it
- );
-
- /**
- * User defined regex templates
- * @var array
- */
- public $varTemplates = array();
-
- /**
- * All regex templates
- * @var e_var
- */
- protected $_regexTemplates;
-
-
- /**
- * Constructor.
- * @param string $route the route of the URL (controller/action)
- * @param string $pattern the pattern for matching the URL
- */
- public function __construct($route, $pattern, $fromCache = false)
- {
- if (is_array($route))
- {
- if ($fromCache && !$pattern)
- {
- $this->setData($route);
- $this->_regexTemplates = new e_vars($this->regexTemplates);
- return;
- }
-
- $this->setData($route);
- if($this->defaultParams && is_string($this->defaultParams))
- {
- parse_str($this->defaultParams, $this->defaultParams);
- }
- $route = $this->route = $route[0];
- }
- else $this->route = $route;
-
- $tr2['/'] = $tr['/'] = '\\/';
-
- if (strpos($route, '<') !== false && preg_match_all('/<(\w+)>/', $route, $matches2))
- {
- foreach ($matches2[1] as $name) $this->references[$name] = "<$name>";
- }
-
- if($this->varTemplates)
- {
- // don't override core regex templates
- $this->regexTemplates = array_merge($this->varTemplates, $this->regexTemplates);
- $this->varTemplates = array();
- }
- $this->_regexTemplates = new e_vars($this->regexTemplates);
-
- if (preg_match_all('/<(\w+):?(.*?)?>/', $pattern, $matches))
- {
- $tokens = array_combine($matches[1], $matches[2]);
- $tp = e107::getParser();
- foreach ($tokens as $name => $value)
- {
- if ($value === '') $value = '[^\/]+';
- elseif($value[0] == '{')
- {
- $value = $tp->simpleParse($value, $this->_regexTemplates, '[^\/]+');
- }
- $tr["<$name>"] = "(?P<$name>$value)";
- if (isset($this->references[$name])) $tr2["<$name>"] = $tr["<$name>"];
- else $this->params[$name] = $value;
- }
- }
-
- $p = rtrim($pattern, '*');
- $this->append = $p !== $pattern;
- $p = trim($p, '/');
- $this->template = preg_replace('/<(\w+):?.*?>/', '<$1>', $p);
- $this->pattern = '/^'.strtr($this->template, $tr).'\/?';
- if ($this->append) $this->pattern .= '/u';
- else $this->pattern .= '$/u';
-
- if ($this->references !== array()) $this->routePattern = '/^'.strtr($this->route, $tr2).'$/u';
- }
-
- public function getData()
- {
- $vars = array_keys(get_class_vars(__CLASS__));
- $data = array();
- foreach ($vars as $prop)
- {
- $data[$prop] = $this->$prop;
- }
- return $data;
- }
-
- protected function setData($data)
- {
- if (!is_array($data)) return;
- $vars = array_keys(get_class_vars(__CLASS__));
-
- foreach ($vars as $prop)
- {
- if (!isset($data[$prop])) continue;
- $this->$prop = $data[$prop];
- }
- }
-
- /**
- * Creates a URL based on this rule.
- * TODO - more clear logic and flexibility by building the query string
- *
- * @param eRouter $manager the router/manager
- * @param string $route the route
- * @param array $params list of parameters
- * @param array $options
- * @return mixed the constructed URL or false on error
- */
- public function createUrl($manager, $route, $params, $options)
- {
- $case = 'i';
- $ampersand = $options['amp'];
- $encode = vartrue($options['encode']);
-
- if(is_array($route)) $route = implode('/', $route);
-
-
-
- $tr = array();
- if ($route !== $this->route)
- {
- if ($this->routePattern !== null && preg_match($this->routePattern.$case, $route, $matches))
- {
- foreach ($this->references as $key => $name) $tr[$name] = $matches[$key];
- }
- else return false;
- }
-
- // map vars first
- foreach ($this->mapVars as $srcKey => $dstKey)
- {
- if (isset($params[$srcKey])/* && !isset($params[$dstKey])*/)
- {
- $params[$dstKey] = $params[$srcKey];
- unset($params[$srcKey]);
- }
- }
-
- // false means - no vars are allowed, preserve only route vars
- if($this->allowVars === false) $this->allowVars = array_keys($this->params);
- // empty array (default) - everything is allowed
-
- // disallow everything but valid URL parameters
- if(!empty($this->allowVars))
- {
- $copy = $params;
- $params = array();
- $this->allowVars = array_unique(array_merge($this->allowVars, array_keys($this->params)));
- foreach ($this->allowVars as $key)
- {
- if(isset($copy[$key])) $params[$key] = $copy[$key];
- }
- unset($copy);
- }
-
- foreach ($this->defaultParams as $key => $value)
- {
- if (isset($params[$key]))
- {
- if ($params[$key] == $value) unset($params[$key]);
- else return false;
- }
- }
-
- foreach ($this->params as $key => $value) if (!isset($params[$key])) return false;
-
- if($this->matchValue)
- {
-
- if('empty' !== $this->matchValue)
- {
- foreach($this->params as $key=>$value)
- {
- if(!preg_match('/'.$value.'/'.$case,$params[$key]))
- return false;
- }
- }
- else
- {
- foreach($this->params as $key=>$value)
- {
- if(empty($params[$key]) )
- return false;
- }
- }
- }
-
- foreach ($this->params as $key => $value)
- {
- // FIX - non-latin URLs proper encoded
- $tr["<$key>"] = rawurlencode($params[$key]);
- unset($params[$key]);
- }
-
- $suffix = $this->urlSuffix === null ? $manager->urlSuffix : $this->urlSuffix;
-
- // XXX TODO Find better place for this check which will affect all types of SEF URL configurations. (@see news/sef_noid_url.php for duplicate)
- $urlFormat = e107::getConfig()->get('url_sef_translate');
-
- if($urlFormat == 'dashl' || $urlFormat == 'underscorel' || $urlFormat == 'plusl') // convert template to lowercase when using lowercase SEF URL format.
- {
- $this->template = strtolower($this->template);
- }
-
- $url = strtr($this->template, $tr);
-
- if(empty($params))
- {
- return $url !== '' ? $url.$suffix : $url;
- }
-
- // apppend not supported, maybe in the future...?
- if ($this->append) $url .= '/'.$manager->createPathInfo($params, '/', '/').$suffix;
- else
- {
- if ($url !== '') $url = $url.$suffix;
-
- $options['equal'] = '=';
- $url .= '?'.$manager->createPathInfo($params, $options);
- }
-
-
- return rtrim($url, '/');
- }
-
- /**
- * Parases a URL based on this rule.
- * @param eRouter $manager the router/URL manager
- * @param eRequest $request the request object
- * @param string $pathInfo path info part of the URL
- * @param string $rawPathInfo path info that contains the potential URL suffix
- * @return mixed the route that consists of the controller ID and action ID or false on error
- */
- public function parseUrl($manager, $request, $pathInfo, $rawPathInfo)
- {
- $case = 'i'; # 'i' = insensitive
-
- if ($this->urlSuffix !== null) $pathInfo = $manager->removeUrlSuffix($rawPathInfo, $this->urlSuffix);
-
- $pathInfo = rtrim($pathInfo, '/').'/';
- // pathInfo is decoded, pattern could be encoded - required for proper url assemble (e.g. cyrillic chars)
- if (preg_match(rawurldecode($this->pattern).$case, $pathInfo, $matches))
- {
- foreach ($this->defaultParams as $name => $value)
- {
- //if (!isset($_GET[$name])) $_REQUEST[$name] = $_GET[$name] = $value;
- if (!$request->isRequestParam($name)) $request->setRequestParam($name, $value);
- }
- $tr = array();
- foreach ($matches as $key => $value)
- {
- if (isset($this->references[$key])) $tr[$this->references[$key]] = $value;
- elseif (isset($this->params[$key]))
- {
- //$_REQUEST[$key] = $_GET[$key] = $value;
- $request->setRequestParam($key, $value);
- }
- }
-
- if ($pathInfo !== $matches[0]) # Additional GET params exist
- {
- $manager->parsePathInfo($request, ltrim(substr($pathInfo, strlen($matches[0])), '/'));
- }
- return (null !== $this->routePattern ? strtr($this->route, $tr) : $this->route);
- }
- else return false;
- }
-
- }
-
- abstract class eUrlConfig
- {
- /**
- * Registered by parse method legacy query string
- */
- public $legacyQueryString = null;
-
- /**
- * User defined initialization
- */
- public function init() {}
-
- /**
- * Retrieve module config options (including url rules if any)
- * Return array is called once and cached, so runtime changes are not an option
- * @return array
- */
- abstract public function config();
-
- /**
- * Create URL callback, called only when config option selfParse is set to true
- * Expected return array format:
- * <code>
- * array(
- * array(part1, part2, part3),
- * array(parm1 => val1, parm2 => val2),
- * );
- * </code>
- * @param array $route parts
- * @param array $params
- * @return array|string numerical of type (routeParts, GET Params)| string route or false on error
- */
- public function create($route, $params = array(), $options = array()) {}
-
- /**
- * Parse URL callback, called only when config option selfCreate is set to true
- * TODO - register variable eURLConfig::currentConfig while initializing the object, remove from method arguments
- * @param string $pathInfo
- * @param array $params request parameters
- * @param eRequest $request
- * @param eRouter $router
- * @param array $config
- * @return string route or false on error
- */
- public function parse($pathInfo, $params = array(), eRequest $request = null, eRouter $router = null, $config = array()) { return false; }
-
- /**
- * Legacy callback, used called when config option legacy is not empty
- * By default it sets legacy query string to $legacyQueryString value (normaly assigned inside of the parse method)
- * @param string $resolvedRoute
- * @param eRequest $request
- * @param string $callType 'route' - called once, when parsing the request, 'dispatch' - called inside the dispatch loop (in case of controller _forward)
- * @param void
- */
- public function legacy($resolvedRoute, eRequest $request, $callType = 'route')
- {
- if($this->legacyQueryString !== null)
- {
- $request->setLegacyQstring($this->legacyQueryString);
- $request->setLegacyPage();
- }
- }
-
- /**
- * Developed mainly for legacy modules.
- * It should be manually triggered inside of old entry point. The idea is
- * to avoid multiple URL addresses having same content (bad SEO practice)
- * FIXME - under construction
- */
- public function forward() {}
-
- /**
- * Admin interface callback, returns array with all required from administration data
- * Return array structure:
- * <code>
- * <?php
- * return array(
- * 'labels' => array(
- * 'name' => 'Module name',
- * 'label' => 'Profile Label',
- * 'description' => 'Additional profile info, exmples etc.',
- * ),
- * 'form' => array(), // awaiting future development
- * 'callbacks' => array(), // awaiting future development
- * );
- * </code>
- */
- public function admin() { return array(); }
-
- /**
- * Admin submit hook
- * FIXME - under construction
- */
- public function submit() {}
-
- /**
- * Admin interface help messages, labels and titles
- * FIXME - under construction
- */
- public function help() {}
-
-
- }
-
- /**
- * Controller base class, actions are extending it
- *
- */
- class eController
- {
- protected $_request;
- protected $_response;
-
- public function __construct(eRequest $request, eResponse $response = null)
- {
- $this->setRequest($request)
- ->setResponse($response)
- ->init();
- }
-
- /**
- * Custom init, always called in the constructor, no matter what is the request dispatch status
- */
- public function init() {}
-
- /**
- * Custom shutdown, always called after the controller dispatch, no matter what is the request dispatch status
- */
- public function shutdown() {}
-
- /**
- * Pre-action callback, fired only if dispatch status is still true and action method is found
- */
- public function preAction() {}
-
- /**
- * Post-action callback, fired only if dispatch status is still true and action method is found
- */
- public function postAction() {}
-
- /**
- * @param eRequest $request
- * @return eController
- */
- public function setRequest($request)
- {
- $this->_request = $request;
- return $this;
- }
-
- /**
- * @return eRequest
- */
- public function getRequest()
- {
- return $this->_request;
- }
-
- /**
- * @param eResponse $response
- * @return eController
- */
- public function setResponse($response)
- {
- $this->_response = $response;
- return $this;
- }
-
- /**
- * @return eResponse
- */
- public function getResponse()
- {
- return $this->_response;
- }
-
- public function addBody($content)
- {
- $this->getResponse()->appendBody($content);
- return $this;
- }
-
- public function addMetaDescription($description)
- {
- $this->getResponse()->addMetaDescription($description);
- return $this;
- }
-
- /**
- * Add document title
- * @param string $title
- * @param boolean $meta auto-add it as meta-title
- * @return eResponse
- */
- public function addTitle($title, $meta = true)
- {
- $this->getResponse()->appendTitle($title);
- if($meta) $this->addMetaTitle(strip_tags($title));
- return $this;
- }
-
-
- public function addMetaTitle($title)
- {
- $this->getResponse()->addMetaTitle($title);
- return $this;
- }
-
- public function dispatch($actionMethodName)
- {
- $request = $this->getRequest();
- $content = '';
-
- // init() could modify the dispatch status
- if($request->isDispatched())
- {
- if(method_exists($this, $actionMethodName))
- {
- $this->preAction();
- // TODO request userParams() to store private data - check for noPopulate param here
- if($request->isDispatched())
- {
- $request->populateRequestParams();
-
- // allow return output
- $content = $this->$actionMethodName();
- if(!empty($content)) $this->addBody($content);
-
- if($request->isDispatched())
- {
- $this->postAction();
- }
- }
- }
- else
- {
- //TODO not found method by controller or default one
- $action = substr($actionMethodName, 6);
- throw new eException('Action "'.$action.'" does not exist');
- }
- }
- $this->shutdown();
- }
-
- public function run(eRequest $request = null, eResponse $response = null)
- {
- if(null === $request) $request = $this->getRequest();
- else $this->setRequest($request);
-
- if(null === $response) $response = $this->getResponse();
- else $this->setResponse($response);
-
- $action = $request->getActionMethodName();
-
- $request->setDispatched(true);
- $this->dispatch($action);
-
- return $this->getResponse();
- }
-
- protected function _redirect($url, $createURL = false, $code = null)
- {
- $redirect = e107::getRedirect();
- if($createURL)
- {
- $url = eFront::instance()->getRouter()->assemble($url, '', 'encode=0');
- }
- if(strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0)
- {
- $url = $url[0] == '/' ? SITEURLBASE.$url : SITEURL.$url;
- }
- $redirect->redirect($url, true, $code);
- }
-
- /**
- * System forward
- * @param string $route
- * @param array $params
- */
- protected function _forward($route, $params = array())
- {
- $request = $this->getRequest();
-
- if(is_string($params))
- {
- parse_str($params, $params);
- }
-
- $oldRoute = $request->getRoute();
- $route = explode('/', trim($route, '/'));
-
- switch (count($route)) {
- case 3:
- if($route[0] !== '*') $request->setModule($route[0]);
- if($route[1] !== '*') $request->setController($route[1]);
- $request->setAction($route[2]);
- break;
-
- case 2:
- if($route[1] !== '*') $request->setController($route[0]);
- $request->setAction($route[1]);
- break;
-
- case 1:
- $request->setAction($route[0]);
- break;
-
- default:
- return;
- break;
- }
-
- $request->addRouteHistory($oldRoute);
-
- if(false !== $params) $request->setRequestParams($params);
- $request->setDispatched(false);
- }
-
- /**
- * @param string $methodName
- * @param array $args
- * @return void
- * @throws eException
- */
- public function __call($methodName, $args)
- {
- if ('action' == substr($methodName, 0, 6))
- {
- $action = substr($methodName, 6);
- throw new eException('Action "'.$action.'" does not exist', 2404);
- }
-
- throw new eException('Method "'.$methodName.'" does not exist', 3404);
- }
- }
-
- /**
- * @package e107
- * @subpackage e107_handlers
- * @version $Id$
- *
- * Base front-end controller
- */
-
- class eControllerFront extends eController
- {
- /**
- * Plugin name - used to check if plugin is installed
- * Set this only if plugin requires installation
- * @var string
- */
- protected $plugin = null;
-
- /**
- * Default controller access
- * @var integer
- */
- protected $userclass = e_UC_PUBLIC;
-
- /**
- * Generic 404 page URL (redirect), SITEURL will be added
- * @var string
- */
- protected $e404 = '404.html';
-
- /**
- * Generic 403 page URL (redirect), SITEURL will be added
- * @var string
- */
- protected $e403 = '403.html';
-
- /**
- * Generic 404 route URL (forward)
- * @var string
- */
- protected $e404route = 'index/not-found';
-
- /**
- * Generic 403 route URL (forward)
- * @var string
- */
- protected $e403route = 'index/access-denied';
-
- /**
- * View renderer objects
- * @var array
- */
- protected $_validator;
-
- /**
- * Per action access
- * Format 'action' => userclass
- * @var array
- */
- protected $access = array();
-
- /**
- * User input filter (_GET)
- * Format 'action' => array(var => validationArray)
- * @var array
- */
- protected $filter = array();
-
- /**
- * Base constructor - set 404/403 locations
- */
- public function __construct(eRequest $request, eResponse $response = null)
- {
- parent::__construct($request, $response);
- $this->_init();
- }
-
- /**
- * Base init, called after the public init() - handle access restrictions
- * The base init() method is able to change controller variables on the fly (e.g. access, filters, etc)
- */
- final protected function _init()
- {
- // plugin check
- if(null !== $this->plugin)
- {
- if(!e107::isInstalled($this->plugin))
- {
- $this->forward403();
- return;
- }
- }
-
- // global controller restriction
- if(!e107::getUser()->checkClass($this->userclass, false))
- {
- $this->forward403();
- return;
- }
-
- // by action access
- if(!$this->checkActionPermissions()) exit;
-
- // _GET input validation
- $this->validateInput();
-
- // Set Render mode to module-controller-action, override possible within the action
- $this->getResponse()->setRenderMod(str_replace('/', '-', $this->getRequest()->getRoute()));
- }
-
- /**
- * Check persmission for current action
- * @return boolean
- */
- protected function checkActionPermissions()
- {
- // per action restrictions
- $action = $this->getRequest()->getAction();
- if(isset($this->access[$action]) && !e107::getUser()->checkClass($this->access[$action], false))
- {
- $this->forward403();
- return false;
- }
- return true;
- }
-
- public function redirect404()
- {
- e107::getRedirect()->redirect(SITEURL.$this->e404);
- }
-
- public function redirect403()
- {
- e107::getRedirect()->redirect(SITEURL.$this->e403);
- }
-
- public function forward404()
- {
- $this->_forward($this->e404route);
- }
-
- public function forward403()
- {
- $this->_forward($this->e403route);
- }
-
- /**
- * Controller validator object
- * @return e_validator
- */
- public function getValidator()
- {
- if(null === $this->_validator)
- {
- $this->_validator = new e_validator('controller');
- }
-
- return $this->_validator;
- }
-
- /**
- * Register request parameters based on current $filter data (_GET only)
- * Additional security layer
- */
- public function validateInput()
- {
- $validator = $this->getValidator();
- $request = $this->getRequest();
- if(empty($this->filter) || !isset($this->filter[$request->getAction()])) return;
- $validator->setRules($this->filter[$request->getAction()])
- ->validate($_GET);
-
- $validData = $validator->getValidData();
-
- foreach ($validData as $key => $value)
- {
- if(!$request->isRequestParam($key)) $request->setRequestParam($key, $value);
- }
- $validator->clearValidateMessages();
- }
-
- /**
- * System error message proxy
- * @param string $message
- * @param boolean $session
- */
- public function messageError($message, $session = false)
- {
- return e107::getMessage()->addError($message, 'default', $session);
- }
-
- /**
- * System success message proxy
- * @param string $message
- * @param boolean $session
- */
- public function messageSuccess($message, $session = false)
- {
- return e107::getMessage()->addSuccess($message, 'default', $session);
- }
-
- /**
- * System warning message proxy
- * @param string $message
- * @param boolean $session
- */
- public function messageWarning($message, $session = false)
- {
- return e107::getMessage()->addWarning($message, 'default', $session);
- }
-
- /**
- * System debug message proxy
- * @param string $message
- * @param boolean $session
- */
- public function messageDebug($message, $session = false)
- {
- return e107::getMessage()->addDebug($message, 'default', $session);
- }
- }
-
-
- /**
- * Request handler
- *
- */
- class eRequest
- {
- /**
- * @var string
- */
- protected $_module;
-
- /**
- * @var string
- */
- protected $_controller;
-
- /**
- * @var string
- */
- protected $_action;
-
- /**
- * Request status
- * @var boolean
- */
- protected $_dispatched = false;
-
- /**
- * @var array
- */
- protected $_requestParams = array();
-
- /**
- * @var string
- */
- protected $_basePath;
-
- /**
- * @var string
- */
- protected $_pathInfo;
-
-
- /**
- * @var string
- */
- protected $_requestInfo;
-
- /**
- * Pathinfo string used for initial system routing
- */
- public $routePathInfo;
-
- /**
- * @var array
- */
- protected $_routeHistory = array();
-
- /**
- * @var boolean if request is already routed - generally set by callbacks to notify router about route changes
- */
- public $routed = false;
-
- /**
- * Name of the bootstrap file
- * @var string
- */
- public $singleEntry = 'index.php';
-
- /**
- * Request constructor
- */
- public function __construct($route = null)
- {
- if(null !== $route)
- {
- $this->setRoute($route);
- $this->routed = true;
- }
- }
-
- /**
- * Get system base path
- * @return string
- */
- public function getBasePath()
- {
- if(null == $this->_basePath)
- {
- $this->_basePath = e_HTTP;
- if(!e107::getPref('url_disable_pathinfo')) $this->_basePath .= $this->singleEntry.'/';
- }
-
- return $this->_basePath;
- }
-
- /**
- * Set system base path
- * @param string $basePath
- * @return eRequest
- */
- public function setBasePath($basePath)
- {
- $this->_basePath = $basePath;
- return $this;
- }
-
- /**
- * Get path info
- * If not set, it'll be auto-retrieved
- * @return string path info
- */
- public function getPathInfo()
- {
- if(null == $this->_pathInfo)
- {
- if($this->getBasePath() == $this->getRequestInfo())
- $this->_pathInfo = ''; // map to indexRoute
-
- else
- $this->_pathInfo = substr($this->getRequestInfo(), strlen($this->getBasePath()));
-
- if($this->_pathInfo && trim($this->_pathInfo, '/') == trim($this->singleEntry, '/')) $this->_pathInfo = '';
- }
-
- return $this->_pathInfo;
- }
-
- /**
- * Override path info
- * @param string $pathInfo
- * @return eRequest
- */
- public function setPathInfo($pathInfo)
- {
- $this->_pathInfo = $pathInfo;
- return $this;
- }
-
- /**
- * @return string request info
- */
- public function getRequestInfo()
- {
- if(null === $this->_requestInfo)
- {
- $this->_requestInfo = e_REQUEST_HTTP;
- }
- return $this->_requestInfo;
- }
-
-
- /**
- * Override request info
- * @param string $pathInfo
- * @return eRequest
- */
- public function setRequestInfo($requestInfo)
- {
- $this->_requestInfo = $requestInfo;
- return $this;
- }
-
- /**
- * Quick front page check
- */
- public static function isFrontPage($entryScript = 'index.php', $currentPathInfo = e_REQUEST_HTTP)
- {
- $basePath = e_HTTP;
- if(!e107::getPref('url_disable_pathinfo')) $basePath .= $entryScript.'/';
-
- return ($basePath == $currentPathInfo);
- }
-
- /**
- * Get current controller string
- * @return string
- */
- public function getController()
- {
- return $this->_controller;
- }
-
- /**
- * Get current controller name
- * Example: requested controller-name or 'controller name' -> converted to controller_name
- * @return string
- */
- public function getControllerName()
- {
- return eHelper::underscore($this->_controller);
- }
-
- /**
- * Set current controller name
- * Example: controller_name OR 'controller name' -> converted to controller-name
- * Always sanitized
- * @param string $controller
- * @return eRequest
- */
- public function setController($controller)
- {
- $this->_controller = strtolower(eHelper::dasherize($this->sanitize($controller)));
- return $this;
- }
-
- /**
- * Get current module string
- * @return string
- */
- public function getModule()
- {
- return $this->_module;
- }
-
- /**
- * Get current module name
- * Example: module-name OR 'module name' -> converted to module_name
- * @return string
- */
- public function getModuleName()
- {
- return eHelper::underscore($this->_module);
- }
-
- /**
- * Set current module name
- * Example: module_name OR 'module name' -> converted to module-name
- * Always sanitized
- * @param string $module
- * @return eRequest
- */
- public function setModule($module)
- {
- $this->_module = strtolower(eHelper::dasherize($this->sanitize($module)));
- return $this;
- }
-
- /**
- * Get current action string
- * @return string
- */
- public function getAction()
- {
- return $this->_action;
- }
-
- /**
- * Get current action name
- * Example: action-name OR 'action name' OR action_name -> converted to ActionName
- * @return string
- */
- public function getActionName()
- {
- return eHelper::camelize($this->_action, true);
- }
-
- /**
- * Get current action method name
- * Example: action-name OR 'action name' OR action_name -> converted to actionActionName
- * @return string
- */
- public function getActionMethodName()
- {
- return 'action'.eHelper::camelize($this->_action, true);
- }
-
- /**
- * Set current action name
- * Example: action_name OR 'action name' OR Action_Name OR 'Action Name' -> converted to ation-name
- * Always sanitized
- * @param string $action
- * @return eRequest
- */
- public function setAction($action)
- {
- $this->_action = strtolower(eHelper::dasherize($this->sanitize($action)));
- return $this;
- }
-
- /**
- * Get current route string/array -> module/controller/action
- * @param boolean $array
- * @return string|array route
- */
- public function getRoute($array = false)
- {
- if(!$this->getModule())
- {
- $route = array('index', 'index', 'index');
- }
- else
- {
- $route = array(
- $this->getModule(),
- $this->getController() ? $this->getController() : 'index',
- $this->getAction() ? $this->getAction() : 'index',
- );
- }
- return ($array ? $route : implode('/', $route));
- }
-
- /**
- * Set current route
- * @param string $route module/controller/action
- * @return eRequest
- */
- public function setRoute($route)
- {
- if(null === $route)
- {
- $this->_module = null;
- $this->_controller = null;
- $this->_action = null;
- }
- return $this->initFromRoute($route);
- }
-
- /**
- * System routing track, used in controllers forwarder
- * @param string $route
- * @return eRequest
- */
- public function addRouteHistory($route)
- {
- $this->_routeHistory[] = $route;
- return $this;
- }
-
- /**
- * Retrieve route from history track
- * Based on $source we can retrieve
- * - array of all history records
- * - 'first' route record
- * - 'last' route record
- * - history record by its index number
- * @param mixed $source
- * @return string|array
- */
- public function getRouteHistory($source = null)
- {
- if(null === $source) return $this->_routeHistory;
-
- if(!$this->_routeHistory) return null;
- elseif('last' === $source)
- {
- return $this->_routeHistory[count($this->_routeHistory) -1];
- }
- elseif('first' === $source)
- {
- return $this->_routeHistory[0];
- }
- elseif(is_int($source))
- {
- return isset($this->_routeHistory[$source]) ? $this->_routeHistory[$source] : null;
- }
- return null;
- }
-
- /**
- * Search route history for the given $route
- *
- * @param string $route
- * @return integer route index or false if not found
- */
- public function findRouteHistory($route)
- {
- return array_search($route, $this->_routeHistory);
- }
-
- /**
- * Populate module, controller and action from route string
- * @param string $route
- * @return array route data
- */
- public function initFromRoute($route)
- {
- $route = trim($route, '/');
- if(!$route)
- {
- $route = 'index/index/index';
- }
- $parts = explode('/', $route);
- $this->setModule($parts[0])
- ->setController(vartrue($parts[1], 'index'))
- ->setAction(vartrue($parts[2], 'index'));
-
- return $this;//->getRoute(true);
- }
-
- /**
- * Get request parameter
- * @param string $key
- * @param string $default value if key not set
- * @return mixed value
- */
- public function getRequestParam($key, $default = null)
- {
- return (isset($this->_requestParams[$key]) ? $this->_requestParams[$key] : $default);
- }
-
- /**
- * Check if request parameter exists
- * @param string $key
- * @return boolean
- */
- public function isRequestParam($key)
- {
- return isset($this->_requestParams[$key]);
- }
-
- /**
- * Get request parameters array
- * @return array value
- */
- public function getRequestParams()
- {
- return $this->_requestParams;
- }
-
- /**
- * Set request parameter
- * @param string $key
- * @param mixed $value
- * @return eRequest
- */
- public function setRequestParam($key, $value)
- {
- $this->_requestParams[$key] = $value;
- return $this;
- }
-
- /**
- * Set request parameters
- * @param array $params
- * @return eRequest
- */
- public function setRequestParams($params)
- {
- $this->_requestParams = $params;
- return $this;
- }
-
- /**
- * Populate current request parameters (_GET scope)
- * @return eRequest
- */
- public function populateRequestParams()
- {
- $rp = $this->getRequestParams();
-
- foreach ($rp as $key => $value)
- {
- $_GET[$key] = $value;
- }
- return $this;
- }
-
- /**
- * More BC
- * @param string $qstring
- * @return eRequest
- */
- public function setLegacyQstring($qstring = null)
- {
- if(defined('e_QUERY')) return $this;
-
- if(null === $qstring)
- {
- $qstring = self::getQueryString();
- }
-
- define("e_SELF", e_REQUEST_SELF);
- define("e_QUERY", $qstring);
- $_SERVER['QUERY_STRING'] = e_QUERY;
-
- if(strpos(e_QUERY,"=")!==false ) // Fix for legacyQuery using $_GET ie. ?x=y&z=1 etc.
- {
- parse_str(str_replace(array('&'), array('&'), e_QUERY),$tmp);
- foreach($tmp as $key=>$value)
- {
- $_GET[$key] = $value;
- }
- }
-
- return $this;
- }
-
- /**
- * And More BC :/
- * @param string $page
- * @return eRequest
- */
- public function setLegacyPage($page = null)
- {
- if(defined('e_PAGE')) return $this;
- if(null === $page)
- {
- $page = eFront::isLegacy();
- }
- if(!$page)
- {
- define('e_PAGE', $this->singleEntry);
- }
- else define('e_PAGE', basename(str_replace(array('{', '}'), '/', $page)));
- return $this;
- }
-
- /**
- * And More from the same - BC :/
- * @return string
- */
- public static function getQueryString()
- {
- $qstring = '';
- if($_SERVER['QUERY_STRING'])
- {
- $qstring = str_replace(array('{', '}', '%7B', '%7b', '%7D', '%7d'), '', rawurldecode($_SERVER['QUERY_STRING']));
- }
- $qstring = str_replace('&', '&', e107::getParser()->post_toForm($qstring));
- return $qstring;
- }
-
- /**
- * Basic sanitize method for module, controller and action input values
- * @param string $str string to be sanitized
- * @param string $pattern optional replace pattern
- * @param string $replace optional replace string, defaults to dash
- */
- public function sanitize($str, $pattern='', $replace='-')
- {
- if (!$pattern) $pattern = '/[^\w\pL-]/u';
-
- return preg_replace($pattern, $replace, $str);
- }
-
- /**
- * Set dispatched status of the request
- * @param boolean $mod
- * @return eRequest
- */
- public function setDispatched($mod)
- {
- $this->_dispatched = $mod ? true : false;
- return $this;
- }
-
- /**
- * Get dispatched status of the request
- * @return boolean
- */
- public function isDispatched()
- {
- return $this->_dispatched;
- }
- }
-
- class eResponse
- {
- protected $_body = array('default' => '');
- protected $_title = array('default' => array());
- protected $_e_PAGETITLE = array();
- protected $_META_DESCRIPTION = array();
- protected $_META_KEYWORDS = array();
- protected $_render_mod = array('default' => 'default');
- protected $_meta_title_separator = ' - ';
- protected $_meta = array();
- protected $_title_separator = ' » ';
- protected $_content_type = 'html';
- protected $_content_type_arr = array(
- 'html' => 'text/html',
- 'css' => 'text/css',
- 'xml' => 'text/xml',
- 'json' => 'application/json',
- 'js' => 'application/javascript',
- 'rss' => 'application/rss+xml',
- 'soap' => 'application/soap+xml',
- );
-
- protected $_params = array(
- 'render' => true,
- 'meta' => false,
- 'jsonNoTitle' => false,
- 'jsonRender' => false,
- );
-
- public function setParam($key, $value)
- {
- $this->_params[$key] = $value;
- return $this;
- }
-
- public function setParams($params)
- {
- $this->_params = $params;
- return $this;
- }
-
- public function getParam($key, $default = null)
- {
- return (isset($this->_params[$key]) ? $this->_params[$key] : $default);
- }
-
- public function isParam($key)
- {
- return isset($this->_params[$key]);
- }
-
- public function addContentType($typeName, $mediaType)
- {
- $this->_content_type_arr[$typeName] = $mediaType;
- return $this;
- }
-
- public function getContentType()
- {
- return $this->_content_type;
- }
-
- public function getContentMediaType($typeName)
- {
- if(isset($this->_content_type_arr[$typeName]))
- return $this->_content_type_arr[$typeName];
- }
-
- public function setContentType($typeName)
- {
- $this->_content_type = $typeName;
- }
-
- /**
- * @return eResponse
- */
- public function sendContentType()
- {
- $ctypeStr = $this->getContentMediaType($this->getContentType());
- if($ctypeStr)
- {
- header('Content-type: '.$this->getContentMediaType($this->getContentType()).'; charset=utf-8', TRUE);
- }
- return $this;
- }
-
- /**
- * @return eResponse
- */
- public function addHeader($header, $override = false, $responseCode = null)
- {
- header($header, $override, $responseCode);
- return $this;
- }
-
- /**
- * Append content
- * @param str $body
- * @param str $ns namespace
- * @return eResponse
- */
- public function appendBody($body, $ns = 'default')
- {
- if(!isset($this->_body[$ns]))
- {
- $this->_body[$ns] = '';
- }
- $this->_body[$ns] .= $body;
-
- return $this;
- }
-
- /**
- * Set content
- * @param str $body
- * @param str $ns namespace
- * @return eResponse
- */
- public function setBody($body, $ns = 'default')
- {
- $this->_body[$ns] = $body;
- return $this;
- }
-
- /**
- * Prepend content
- * @param str $body
- * @param str $ns namespace
- * @return eResponse
- */
- function prependBody($body, $ns = 'default')
- {
- if(!isset($this->_body[$ns]))
- {
- $this->_body[$ns] = '';
- }
- $this->_body[$ns] = $content.$this->_body[$ns];
-
- return $this;
- }
-
- /**
- * Get content
- * @param str $ns
- * @param boolean $reset
- * @return string
- */
- public function getBody($ns = 'default', $reset = false)
- {
- if(!isset($this->_body[$ns]))
- {
- $this->_body[$ns] = '';
- }
- $ret = $this->_body[$ns];
- if($reset) unset($this->_body[$ns]);
-
- return $ret;
- }
-
- /**
- * @param str $title
- * @param str $ns
- * @return eResponse
- */
- function setTitle($title, $ns = 'default')
- {
-
- if(!is_string($ns) || empty($ns))
- {
- $this->_title['default'] = array((string) $title);
- }
- else
- {
- $this->_title[$ns] = array((string) $title);
- }
- return $this;
- }
-
- /**
- * @param str $title
- * @param str $ns
- * @return eResponse
- */
- function appendTitle($title, $ns = 'default')
- {
- if(empty($title))
- {
- return $this;
- }
- if(!is_string($ns) || empty($ns))
- {
- $ns = 'default';
- }
- elseif(!isset($this->_title[$ns]))
- {
- $this->_title[$ns] = array();
- }
- $this->_title[$ns][] = (string) $title;
- return $this;
- }
-
- /**
- * @param str $title
- * @param str $ns
- * @return eResponse
- */
- function prependTitle($title, $ns = 'default')
- {
- if(empty($title))
- {
- return $this;
- }
- if(!is_string($ns) || empty($ns))
- {
- $ns = 'default';
- }
- elseif(!isset($this->_title[$ns]))
- {
- $this->_title[$ns] = array();
- }
- array_unshift($this->_title[$ns], $title);
- return $this;
- }
-
- /**
- * Assemble title
- * @param str $ns
- * @param bool $reset
- */
- function getTitle($ns = 'default', $reset = false)
- {
- if(!is_string($ns) || empty($ns))
- {
- $ret = implode($this->_title_separator, $this->_title['default']);
- if($reset)
- $this->_title['default'] = '';
- }
- elseif(isset($this->_title[$ns]))
- {
- $ret = implode($this->_title_separator, $this->_title[$ns]);
- if($reset)
- unset($this->_title[$ns]);
- }
- else
- {
- $ret = '';
- }
- return $ret;
- }
-
- /**
- *
- * @param string $render_mod
- * @param string $ns
- * @return eResponse
- */
- function setRenderMod($render_mod, $ns = 'default')
- {
- if(!is_string($ns) || empty($ns))
- {
- return $this;
- }
- $this->_render_mod[$ns] = (string) $render_mod;
- return $this;
- }
-
- /**
- * Retrieve render mod
- * @param string $ns
- */
- function getRenderMod($ns = 'default')
- {
- if(!is_string($ns) || empty($ns))
- {
- $ns = 'default';
- }
- return vartrue($this->_render_mod[$ns], null);
- }
-
- /**
- * Generic meta information
- * Example usage:
- * addMeta('og:title', 'My Title');
- * addMeta(null, 30, array('http-equiv' => 'refresh'));
- * addMeta(null, null, array('http-equiv' => 'refresh', 'content' => 30)); // same as above
- * @param string $name 'name' attribute value, or null to avoid it
- * @param string $content 'content' attribute value, or null to avoid it
- * @param array $extended format 'attribute_name' => 'value'
- * @return eResponse
- */
- public function addMeta($name = null, $content = null, $extended = array())
- {
- if(empty($content)){ return $this; } // content is required, otherwise ignore.
-
- //TODO need an option that allows subsequent entries to overwrite existing ones.
- //ie. 'description' and 'keywords' should never be duplicated, but overwritten by plugins and other non-pref-based meta data.
-
- $attr = array();
-
- if(null !== $name)
- {
- $key = (substr($name,0,3) == 'og:') ? 'property' : 'name';
- $attr[$key] = $name;
- }
- if(null !== $content) $attr['content'] = $content;
- if(!empty($extended))
- {
- if(!empty($attr)) $attr = array_merge($attr, $extended);
- else $attr = $extended;
- }
-
- if(!empty($attr)) $this->_meta[] = $attr;
- return $this;
- }
-
- /**
- * Render meta tags, registered via addMeta() method
- * @return string
- */
- public function renderMeta()
- {
- $attrData = '';
-
- foreach ($this->_meta as $attr)
- {
- $attrData .= '<meta';
- foreach ($attr as $p => $v)
- {
- $attrData .= ' '.preg_replace('/[^\w\-]/', '', $p).'="'.str_replace(array('"', '<'), '', $v).'"';
- }
- $attrData .= ' />'."\n";
- }
- return $attrData;
- }
-
- /**
- * Add meta title, description and keywords
- *
- * @param string $meta property name
- * @param string $content meta content
- * @return eResponse
- */
- function addMetaData($meta, $content)
- {
- $meta = '_' . $meta;
- if(isset($this->$meta) && !empty($content))
- {
- $content = str_replace(array('&', '"', "'"), array('&', '', ''), $content);
- $this->{$meta}[] = htmlspecialchars((string) $content, ENT_QUOTES, 'UTF-8');
- }
- return $this;
- }
-
- /**
- * Get meta title, description and keywords
- *
- * @param string $meta property name
- * @return string
- */
- function getMetaData($meta, $separator = '')
- {
- $meta = '_' . $meta;
- if(isset($this->$meta) && !empty($this->$meta))
- {
- return implode($separator, $this->$meta);
- }
- return '';
- }
-
- /**
- * @param string $title
- * @return eResponse
- */
- function addMetaTitle($title)
- {
- return $this->addMetaData('e_PAGETITLE', $title);
- }
-
- function getMetaTitle()
- {
- return $this->getMetaData('e_PAGETITLE', $this->_meta_title_separator);
- }
-
- /**
- * @param string $description
- * @return eResponse
- */
- function addMetaDescription($description)
- {
- return $this->addMetaData('META_DESCRIPTION', $description);
- }
-
- function getMetaDescription()
- {
- return $this->getMetaData('META_DESCRIPTION');
- }
-
- /**
- * @param string $keywords
- * @return eResponse
- */
- function addMetaKeywords($keywords)
- {
- return $this->addMetaData('META_KEYWORDS', $keywords);
- }
-
- function getMetaKeywords()
- {
- return $this->getMetaData('META_KEYWORDS', ',');
- }
-
- /**
- * Send e107 meta-data
- * @return eResponse
- */
- function sendMeta()
- {
- //HEADERF already included or meta content already sent
- if(e_AJAX_REQUEST || defined('USER_AREA') || defined('e_PAGETITLE'))
- return $this;
-
- if(!defined('e_PAGETITLE') && !empty($this->_e_PAGETITLE))
- {
- define('e_PAGETITLE', $this->getMetaTitle());
- }
- if(!defined('META_DESCRIPTION') && !empty($this->_META_DESCRIPTION))
- {
- define('META_DESCRIPTION', $this->getMetaDescription());
- }
- if(!defined('META_KEYWORDS') && !empty($this->_META_KEYWORDS))
- {
- define('META_KEYWORDS', $this->getMetaKeywords());
- }
- return $this;
- }
-
- /**
- * Send Response Output - default method
- * TODO - ajax send, using js_manager
- * @param string $ns namespace/segment
- * @param bool $return
- * @param bool $render_message append system messages
- */
- function send($ns = null, $return = true, $render_message = true)
- {
- $content = $this->getBody($ns, true);
- $render = $this->getParam('render');
- $meta = $this->getParam('meta');
-
- $this->sendContentType();
-
- if($render_message)
- {
- $content = eMessage::getInstance()->render().$content;
- }
-
- if($meta)
- {
- $this->sendMeta();
- }
-
- //render disabled by the controller
- if(!$this->getRenderMod($ns))
- {
- $render = false;
- }
-
- if($render)
- {
- $render = e107::getRender();
- if($return)
- {
- return $render->tablerender($this->getTitle($ns, true), $content, $this->getRenderMod($ns), true);
- }
- else
- {
- $render->tablerender($this->getTitle($ns, true), $content, $this->getRenderMod($ns));
- return '';
- }
- }
- elseif($return)
- {
- return $content;
- }
- else
- {
- print $content;
- return '';
- }
- }
-
- /**
- * Send AJAX Json Response Output - default method
- * It's fully compatible with the core dialog.js
- * @param array $override override output associative array (header, body and footer keys)
- * @param string $ns namespace/segment
- * @param bool $render_message append system messages
- */
- function sendJson($override = array(), $ns = null, $render_message = true)
- {
- if(!$ns) $ns = 'default';
-
- $content = $this->getBody($ns, true);
- // separate render parameter for json response, false by default
- $render = $this->getParam('jsonRender');
- if($render_message)
- {
- $content = eMessage::getInstance()->render().$content;
- }
-
- //render disabled by the controller
- if(!$this->getRenderMod($ns))
- {
- $render = false;
- }
-
-
- $title = '';
- if(!$this->getParam('jsonNoTitle'))
- {
- $titleArray = $this->_title;
- $title = isset($titleArray[$ns]) ? array_pop($titleArray[$ns]) : '';
- }
-
- if($render)
- {
- $render = e107::getRender();
- $content = $render->tablerender($this->getTitle($ns, true), $content, $this->getRenderMod($ns), true);
- }
-
- $jshelper = e107::getJshelper();
- $override = array_merge(array(
- 'header' => $title,
- 'body' => $content,
- 'footer' => $statusText,
- ), $override);
- echo $jshelper->buildJsonResponse($override);
- $jshelper->sendJsonResponse(null);
- }
-
- /**
- * JS manager
- * @return e_jsmanager
- */
- function getJs()
- {
- return e107::getJs();
- }
- }
-
- /**
- * We move all generic helper functionallity here - a lot of candidates in e107 class
- *
- */
- class eHelper
- {
- protected static $_classRegEx = '#[^\w\s\-]#';
- protected static $_idRegEx = '#[^\w\-]#';
- protected static $_styleRegEx = '#[^\w\s\-\.;:!]#';
-
- public static function secureClassAttr($string)
- {
- return preg_replace(self::$_classRegEx, '', $string);
- }
-
- public static function secureIdAttr($string)
- {
- return preg_replace(self::$_idRegEx, '', $string);
- }
-
- public static function secureStyleAttr($string)
- {
- return preg_replace(self::$_styleRegEx, '', $string);
- }
-
- public static function buildAttr($safeArray)
- {
- return http_build_query($safeArray, null, '&');
- }
-
- public static function formatMetaTitle($title)
- {
- $title = trim(str_replace(array('"', "'"), '', strip_tags(e107::getParser()->toHTML($title, TRUE))));
- return trim(preg_replace('/[\s,]+/', ' ', str_replace('_', ' ', $title)));
- }
-
- public static function secureSef($sef)
- {
- return trim(preg_replace('/[^\w\pL\s\-+.,]+/u', '', strip_tags(e107::getParser()->toHTML($sef, TRUE))));
- }
-
- public static function formatMetaKeys($keywordString)
- {
- $keywordString = preg_replace('/[^\w\pL\s\-.,+]/u', '', strip_tags(e107::getParser()->toHTML($keywordString, TRUE)));
- return trim(preg_replace('/[\s]?,[\s]?/', ',', str_replace('_', ' ', $keywordString)));
- }
-
- public static function formatMetaDescription($descrString)
- {
- $descrString = preg_replace('/[\r]*\n[\r]*/', ' ', trim(str_replace(array('"', "'"), '', strip_tags(e107::getParser()->toHTML($descrString, TRUE)))));
- return trim(preg_replace('/[\s]+/', ' ', str_replace('_', ' ', $descrString)));
- }
-
- /**
- * Convert title to valid SEF URL string
- * Type ending with 'l' stands for 'to lowercase', ending with 'c' - 'to camel case'
- * @param string $title
- * @param string $type dashl|dashc|dash|underscorel|underscorec|underscore|plusl|plusc|plus|none
- */
- public static function title2sef($title, $type = null)
- {
- $title = preg_replace('/[^\w\pL\s.,]/u', '', strip_tags(e107::getParser()->toHTML($title, TRUE)));
- $title = trim(preg_replace('/[\s]+/', ' ', str_replace('_', ' ', $title)));
-
- if(null === $type)
- {
- $type = e107::getPref('url_sef_translate');
- }
- $tp = e107::getParser();
- switch ($type)
- {
- case 'dashl': //dasherize, to lower case
- return self::dasherize($tp->ustrtolower($title));
- break;
-
- case 'dashc': //dasherize, camel case
- return self::dasherize(self::camelize($title, true, ' '));
- break;
-
- case 'dash': //dasherize
- return self::dasherize($title);
- break;
-
- case 'underscorel': ///underscore, to lower case
- return self::underscore($tp->ustrtolower($title));
- break;
-
- case 'underscorec': ///underscore, camel case
- return self::underscore(self::camelize($title, true, ' '));
- break;
-
- case 'underscore': ///underscore
- return self::underscore($title);
- break;
-
- case 'plusl': ///plus separator, to lower case
- return str_replace(' ', '+', $tp->ustrtolower($title));
- break;
-
- case 'plusc': ///plus separator, to lower case
- return str_replace(' ', '+', self::camelize($title, true, ' '));
- break;
-
- case 'plus': ///plus separator
- return str_replace(' ', '+', $title);
- break;
-
- case 'none':
- default:
- return $title;
- break;
- }
- }
-
- /**
- * Return a memory value formatted helpfully
- * $dp overrides the number of decimal places displayed - realistically, only 0..3 are sensible
- * FIXME e107->parseMemorySize() START
- * - move here all e107 class ban/ip related methods
- * - out of (integer) range case?
- * 32 bit systems range: -2147483648 to 2147483647
- * 64 bit systems range: -9223372036854775808 9223372036854775807
- * {@link http://www.php.net/intval}
- * FIXME e107->parseMemorySize() END
- *
- * @param integer $size
- * @param integer $dp
- * @return string formatted size
- */
- public static function parseMemorySize($size, $dp = 2)
- {
- if (!$size) { $size = 0; }
- if ($size < 4096)
- { // Fairly arbitrary limit below which we always return number of bytes
- return number_format($size, 0).CORE_LAN_B;
- }
-
- $size = $size / 1024;
- $memunit = CORE_LAN_KB;
-
- if ($size > 1024)
- { /* 1.002 mb, etc */
- $size = $size / 1024;
- $memunit = CORE_LAN_MB;
- }
- if ($size > 1024)
- { /* show in GB if >1GB */
- $size = $size / 1024;
- $memunit = CORE_LAN_GB;
- }
- if ($size > 1024)
- { /* show in TB if >1TB */
- $size = $size / 1024;
- $memunit = CORE_LAN_TB;
- }
- return (number_format($size, $dp).$memunit);
- }
-
- /**
- * Get the current memory usage of the code
- * If $separator argument is null, raw data (array) will be returned
- *
- * @param null|string $separator
- * @return string|array memory usage
- */
- public static function getMemoryUsage($separator = '/')
- {
- $ret = array();
- if(function_exists("memory_get_usage"))
- {
- $ret[] = eHelper::parseMemorySize(memory_get_usage());
- // With PHP>=5.2.0, can show peak usage as well
- if (function_exists("memory_get_peak_usage")) $ret[] = eHelper::parseMemorySize(memory_get_peak_usage(TRUE));
- }
- else
- {
- $ret[] = 'Unknown';
- }
-
- return (null !== $separator ? implode($separator, $ret) : $ret);
- }
-
- public static function camelize($str, $all = false, $space = '')
- {
- // clever recursion o.O
- if($all) return self::camelize('-'.$str, false, $space);
-
- $tmp = explode('-', str_replace(array('_', ' '), '-', e107::getParser()->ustrtolower($str)));
- return trim(implode($space, array_map('ucfirst', $tmp)), $space);
- }
-
- public static function labelize($str, $space = ' ')
- {
- return self::camelize($str, true, ' ');
- }
-
- public static function dasherize($str)
- {
- return str_replace(array('_', ' '), '-', $str);
- }
-
- public static function underscore($str)
- {
- return str_replace(array('-', ' '), '_', $str);
- }
-
- /**
- * Parse generic shortcode parameter string
- * Format expected: {SC=key=val&key1=val1...}
- * Escape strings: \& => &
- *
- * @param string $parmstr
- * @return array associative param array
- */
- public static function scParams($parm)
- {
- if (!$parm) return array();
- if (!is_array($parm))
- {
- $parm = str_replace('\&', '%%__amp__%%', $parm);
- $parm = str_replace('&', '&', $parm); // clean when it comes from the DB
- parse_str($parm, $parm);
- foreach ($parm as $k => $v)
- {
- $parm[str_replace('%%__amp__%%', '&', $k)] = str_replace('%%__amp__%%', '\&', $v);
- }
- }
-
- return $parm;
- }
-
- /**
- * Parse shortcode parameter string of type 'dual parameters' - advanced, more complex and slower(!) case
- * Format expected: {SC=name|key=val&key1=val1...}
- * Escape strings: \| => | , \& => & and \& => &
- * Return array is formatted like this:
- * 1 => string|array (depends on $name2array value) containing first set of parameters;
- * 2 => array containing second set of parameters;
- * 3 => string containing second set of parameters;
- *
- * @param string $parmstr
- * @param boolean $first2array If true, first key (1) of the returned array will be parsed to array as well
- * @return array
- */
- public static function scDualParams($parmstr, $first2array = false)
- {
- if (!$parmstr) return array(1 => '', 2 => array(), 3 => '');
- if (is_array($parmstr)) return $parmstr;
-
- $parmstr = str_replace('&', '&', $parmstr); // clean when it comes from the DB
- $parm = explode('|', str_replace(array('\|', '\&', '\&'), array('%%__pipe__%%', '%%__ampamp__%%', '%%__amp__%%'), $parmstr), 2);
-
- $multi = str_replace('%%__pipe__%%', '|', $parm[0]);
- if ($first2array)
- {
- parse_str($multi, $multi);
- foreach ($multi as $k => $v)
- {
- $multi[str_replace(array('%%__ampamp__%%', '%%__amp__%%'), array('&', '&'), $k)] = str_replace(array('%%__ampamp__%%', '%%__amp__%%'), array('&', '&'), $v);
- }
- }
-
- if (varset($parm[1]))
- {
- // second paramater as a string - allow to be further passed to shortcodes
- $parmstr = str_replace(array('%%__pipe__%%', '%%__ampamp__%%', '%%__amp__%%'), array('\|', '\&', '\&'), $parm[1]);
- parse_str(str_replace('%%__pipe__%%', '|', $parm[1]), $params);
- foreach ($params as $k => $v)
- {
- $params[str_replace(array('%%__ampamp__%%', '%%__amp__%%'), array('&', '&'), $k)] = str_replace(array('%%__ampamp__%%', '%%__amp__%%'), array('&', '&'), $v);
- }
- }
- else
- {
- $parmstr = '';
- $params = array();
- }
-
- return array(1 => $multi, 2 => $params, 3 => $parmstr);
- }
- }