mirror of
https://github.com/e107inc/e107.git
synced 2025-01-16 20:28:28 +01:00
5387 lines
128 KiB
PHP
5387 lines
128 KiB
PHP
<?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)
|
||
*
|
||
*/
|
||
|
||
|
||
/**
|
||
* Class e_url
|
||
* New v2.1.6
|
||
*/
|
||
class e_url
|
||
{
|
||
|
||
private static $_instance;
|
||
|
||
private $_request = null;
|
||
|
||
private $_config = array();
|
||
|
||
private $_include = null;
|
||
|
||
private $_rootnamespace = null;
|
||
|
||
private $_alias = array();
|
||
|
||
private $_legacy = array();
|
||
|
||
private $_legacyAliases = array();
|
||
|
||
|
||
/**
|
||
* e_url constructor.
|
||
*/
|
||
function __construct()
|
||
{
|
||
$this->_request = (e_HTTP === '/') ? ltrim(e_REQUEST_URI,'/') : str_replace(e_HTTP,'', e_REQUEST_URI) ;
|
||
|
||
$this->_config = e107::getUrlConfig();
|
||
$this->_alias = e107::getPref('e_url_alias');
|
||
|
||
$this->_rootnamespace = e107::getPref('url_main_module');
|
||
$this->_legacy = e107::getPref('url_config');
|
||
$this->_legacyAliases = e107::getPref('url_aliases');
|
||
|
||
|
||
$this->setRootNamespace();
|
||
|
||
}
|
||
|
||
/**
|
||
* Detect older e_url system.
|
||
* @return bool
|
||
*/
|
||
private function isLegacy()
|
||
{
|
||
|
||
$arr = (!empty($this->_legacyAliases[e_LAN])) ? array_merge($this->_legacy,$this->_legacyAliases[e_LAN]) : $this->_legacy;
|
||
|
||
$list = array_keys($arr);
|
||
|
||
foreach($list as $leg)
|
||
{
|
||
if(strpos($this->_request,$leg.'/') === 0 || $this->_request === $leg)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
|
||
/**
|
||
* @return string|null
|
||
*/
|
||
public function getInclude()
|
||
{
|
||
return $this->_include;
|
||
}
|
||
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
private function setRootNamespace()
|
||
{
|
||
|
||
$plugs = array_keys($this->_config);
|
||
|
||
if(!empty($this->_rootnamespace) && in_array($this->_rootnamespace,$plugs)) // Move rootnamespace check to the end of the list.
|
||
{
|
||
$v = $this->_config[$this->_rootnamespace];
|
||
unset($this->_config[$this->_rootnamespace]);
|
||
$this->_config[$this->_rootnamespace] = $v;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* @return bool|void
|
||
*/
|
||
public function run()
|
||
{
|
||
$pref = e107::getPref();
|
||
$tp = e107::getParser();
|
||
|
||
if(empty($this->_request)) // likely 'index.php' ie. the home page.
|
||
{
|
||
$this->_request = e107::getFrontPage();
|
||
e107::canonical('_SITEURL_');
|
||
}
|
||
|
||
if(empty($this->_config) /*|| empty($this->_request) || $this->_request === 'index.php'*/ || $this->isLegacy() === true)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
$replaceAlias = array('{alias}\/?','{alias}/?','{alias}\/','{alias}/',);
|
||
|
||
foreach($this->_config as $plug=>$cfg)
|
||
{
|
||
if(empty($pref['e_url_list'][$plug])) // disabled.
|
||
{
|
||
e107::getDebug()->log('e_URL for <b>'.$plug.'</b> is disabled.');
|
||
continue;
|
||
}
|
||
|
||
foreach($cfg as $k=>$v)
|
||
{
|
||
|
||
if(empty($v['regex']))
|
||
{
|
||
// e107::getMessage()->addDebug("Skipping empty regex: <b>".$k."</b>");
|
||
continue;
|
||
}
|
||
|
||
if((empty($v['domain']) && (empty($this->_request) || $this->_request === 'index.php')) || (isset($v['domain']) && ($v['domain'] !== $_SERVER['HTTP_HOST'])))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
if(!empty($v['alias']))
|
||
{
|
||
$alias = (!empty($this->_alias[e_LAN][$plug][$k])) ? $this->_alias[e_LAN][$plug][$k] : $v['alias'];
|
||
// e107::getMessage()->addDebug("e_url alias found: <b>".$alias."</b>");
|
||
if(!empty($this->_rootnamespace) && $this->_rootnamespace === $plug)
|
||
{
|
||
$v['regex'] = str_replace($replaceAlias, '', $v['regex']);
|
||
}
|
||
else
|
||
{
|
||
|
||
$v['regex'] = str_replace('{alias}', $alias, $v['regex']);
|
||
}
|
||
}
|
||
|
||
|
||
$regex = '#'.$v['regex'].'#';
|
||
|
||
if(empty($v['redirect']))
|
||
{
|
||
continue;
|
||
}
|
||
|
||
|
||
$newLocation = preg_replace($regex, $v['redirect'], $this->_request);
|
||
|
||
if($newLocation != $this->_request)
|
||
{
|
||
$redirect = e107::getParser()->replaceConstants($newLocation);
|
||
list($file,$query) = explode("?", $redirect,2);
|
||
|
||
$get = array();
|
||
if(!empty($query))
|
||
{
|
||
// issue #3171 fix double ampersand in case of wrong query definition
|
||
$query = str_replace('&&', '&', $query);
|
||
parse_str($query,$get);
|
||
}
|
||
|
||
|
||
foreach($get as $gk=>$gv)
|
||
{
|
||
$_GET[$gk] = $gv;
|
||
}
|
||
|
||
e107::getDebug()->log('e_URL in <b>'.$plug.'</b> with key: <b>'.$k.'</b> matched <b>'.$v['regex'].'</b> and included: <b>'.$file.'</b> with $_GET: '.print_a($_GET,true),1);
|
||
|
||
if(file_exists($file))
|
||
{
|
||
define('e_CURRENT_PLUGIN', $plug);
|
||
define('e_QUERY', str_replace('&&', '&', $query)); // do not add to e107_class.php
|
||
define('e_URL_LEGACY', $redirect);
|
||
if(!defined('e_PAGE'))
|
||
{
|
||
define('e_PAGE', basename($file));
|
||
}
|
||
|
||
if(!e107::route()) // subject to removal at any time.
|
||
{
|
||
e107::route($plug.'/'.$k);
|
||
}
|
||
|
||
$fpUrl = str_replace(SITEURL, '', rtrim(e_REQUEST_URL, '?/'));
|
||
$fpPref = e107::getFrontpage();
|
||
|
||
if($fpUrl === $fpPref)
|
||
{
|
||
|
||
}
|
||
unset($fpUrl, $fpPref);
|
||
|
||
|
||
$this->_include= $file;
|
||
return true;
|
||
// exit;
|
||
}
|
||
elseif(getperms('0'))
|
||
{
|
||
|
||
echo "<div class='alert alert-warning'>";
|
||
echo "<h3>SEF Debug Info</h3>";
|
||
echo "File missing: ".$file;
|
||
echo "<br />Matched key: <b>".$k."</b>";
|
||
print_a($v);
|
||
echo "</div>";
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Singleton implementation
|
||
* @return e_url
|
||
*/
|
||
public static function instance()
|
||
{
|
||
if(null == self::$_instance)
|
||
{
|
||
self::$_instance = new self();
|
||
}
|
||
return self::$_instance;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
/**
|
||
* e107 Front controller
|
||
*/
|
||
class eFront
|
||
{
|
||
/**
|
||
* Singleton instance
|
||
* @var eFront
|
||
*/
|
||
private static $_instance;
|
||
|
||
/**
|
||
* @var eDispatcher
|
||
*/
|
||
protected $_dispatcher;
|
||
|
||
/**
|
||
* @var eRequest
|
||
*/
|
||
protected $_request;
|
||
|
||
/**
|
||
* @var eRouter
|
||
*/
|
||
protected $_router;
|
||
|
||
|
||
protected $_response;
|
||
|
||
/**
|
||
* @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);
|
||
|
||
/** @var eResponse $response */
|
||
$response = e107::getSingleton('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(!empty($status[0]) && ($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();
|
||
|
||
/**
|
||
* @param eRequest|null $request
|
||
* @param eResponse|null $response
|
||
* @return void
|
||
* @throws eException
|
||
*/
|
||
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_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;
|
||
if(!defined('e_CURRENT_PLUGIN'))
|
||
{
|
||
define('e_CURRENT_PLUGIN', rtrim($module,'/')); // TODO Move to a better location.
|
||
}
|
||
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 false|string 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;
|
||
}
|
||
|
||
/** @var eUrlConfig $obj */
|
||
$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
|
||
* @return mixed
|
||
*/
|
||
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
|
||
* @return mixed|null|string
|
||
*/
|
||
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
|
||
*/
|
||
public function loadConfig($forceRebuild = false)
|
||
{
|
||
|
||
if(!is_readable(e_CACHE_URL . 'config.php') || $forceRebuild == true)
|
||
{
|
||
$config = $this->buildGlobalConfig();
|
||
}
|
||
else
|
||
{
|
||
$config = include(e_CACHE_URL . 'config.php');
|
||
}
|
||
|
||
if(empty($config))
|
||
{
|
||
trigger_error('URL Config is empty', E_USER_NOTICE);
|
||
$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;
|
||
}
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
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());
|
||
|
||
if(empty($active))
|
||
{
|
||
trigger_error('url_config pref is empty', E_USER_NOTICE);
|
||
}
|
||
|
||
$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());
|
||
$plugins = 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|null $lanCode optional language alias check. Example $lan = 'bg' (search for Bulgarian aliases)
|
||
* @return array
|
||
*/
|
||
public function getAliases($lanCode = null)
|
||
{
|
||
if($lanCode)
|
||
{
|
||
return e107::findPref('url_aliases/'.$lanCode, 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);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $label
|
||
* @param $val
|
||
* @param $line
|
||
* @return false|void
|
||
*/
|
||
private function _debug($label, $val=null, $line=null)
|
||
{
|
||
if(!deftrue('e_DEBUG_SEF'))
|
||
{
|
||
return false;
|
||
}
|
||
|
||
e107::getDebug()->log("<h3>SEF: ".$label . " <small>".basename(__FILE__)." (".$line.")</small></h3>".print_a($val,true));
|
||
}
|
||
|
||
/**
|
||
* Route current request
|
||
* @param eRequest $request
|
||
* @param bool $checkOnly
|
||
* @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;
|
||
}
|
||
|
||
|
||
|
||
// Ignore social trackers when determining route.
|
||
$get = eHelper::removeTrackers($_GET);
|
||
|
||
// 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();
|
||
|
||
$this->_debug('parts',$parts, __LINE__);
|
||
|
||
// 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;
|
||
|
||
$this->_debug('module',$module, __LINE__);
|
||
$this->_debug('rawPathInfo',$rawPathInfo, __LINE__);
|
||
|
||
|
||
// valid module
|
||
if(null !== $module)
|
||
{
|
||
// we have valid module
|
||
$config = $this->getConfig($module);
|
||
|
||
$this->_debug('config',$module, __LINE__);
|
||
|
||
// set legacy state
|
||
eFront::isLegacy(varset($config['legacy']));
|
||
|
||
// Don't allow single entry if required by module config
|
||
if(!empty($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(!empty($config['selfParse']))
|
||
{
|
||
// controller/action[/additional/parms]
|
||
if(!empty($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)
|
||
{
|
||
// $this->_debug('rules',$rules, __LINE__);
|
||
|
||
foreach ($rules as $rule)
|
||
{
|
||
$route = $rule->parseUrl($this, $request, $pathInfo, $rawPathInfo);
|
||
|
||
|
||
|
||
if($route !== false)
|
||
{
|
||
eFront::isLegacy($rule->legacy); // legacy include override
|
||
|
||
$this->_debug('rule->legacy',$rule->legacy, __LINE__);
|
||
$this->_debug('rule->parseCallback',$rule->parseCallback, __LINE__);
|
||
|
||
if($rule->parseCallback)
|
||
{
|
||
$this->configCallback($module, $rule->parseCallback, array($request), $config['location']);
|
||
}
|
||
|
||
// parse legacy query string if any
|
||
$this->_debug('rule->legacyQuery',$rule->legacyQuery, __LINE__);
|
||
|
||
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('/[^\w\-]/', '', $_GET[$key]);
|
||
}
|
||
}
|
||
}
|
||
$obj->legacyQueryString = e107::getParser()->simpleParse($rule->legacyQuery, $vars, '0');
|
||
|
||
$this->_debug('obj->legacyQueryString',$obj->legacyQueryString, __LINE__);
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
|
||
$this->_debug('route',$route, __LINE__);
|
||
|
||
$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['#'];
|
||
unset($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(!empty($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(!empty($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(!empty($config['mapVars']))
|
||
{
|
||
foreach ($config['mapVars'] as $srcKey => $dstKey)
|
||
{
|
||
if (isset($params[$srcKey]))
|
||
{
|
||
$params[$dstKey] = $params[$srcKey];
|
||
unset($params[$srcKey]);
|
||
}
|
||
else
|
||
{
|
||
trigger_error("Missing ".$srcKey." during URL creation in ".$module, E_USER_NOTICE);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 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];
|
||
}
|
||
else
|
||
{
|
||
trigger_error("Missing ".$key." during URL creation in ".$module, E_USER_NOTICE);
|
||
}
|
||
}
|
||
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 string $pathInfo path info
|
||
* @param string $equal
|
||
* @param string $ampersand
|
||
* @return array|void
|
||
*/
|
||
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';
|
||
}
|
||
|
||
/**
|
||
* @return array
|
||
*/
|
||
public function getData()
|
||
{
|
||
$vars = array_keys(get_class_vars(__CLASS__));
|
||
$data = array();
|
||
foreach ($vars as $prop)
|
||
{
|
||
$data[$prop] = $this->$prop;
|
||
}
|
||
return $data;
|
||
}
|
||
|
||
/**
|
||
* @param $data
|
||
* @return void
|
||
*/
|
||
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 false|string|null 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;
|
||
}
|
||
}
|
||
}
|
||
|
||
$tp = e107::getParser();
|
||
$urlFormat = e107::getConfig()->get('url_sef_translate');
|
||
|
||
foreach ($this->params as $key => $value)
|
||
{
|
||
// FIX - non-latin URLs proper encoded
|
||
$tr["<$key>"] = rawurlencode($params[$key]); //todo transliterate non-latin
|
||
// $tr["<$key>"] = eHelper::title2sef($tp->toASCII($params[$key]), $urlFormat); // enabled to test.
|
||
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)
|
||
|
||
|
||
|
||
|
||
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);
|
||
|
||
// Work-around fix for lowercase username
|
||
if($urlFormat == 'dashl' && $this->route == 'profile/view')
|
||
{
|
||
$url = str_replace('%20','-', strtolower($url));
|
||
}
|
||
|
||
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|null $request
|
||
* @param eRouter|null $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;
|
||
|
||
/**
|
||
* @param eRequest $request
|
||
* @param eResponse|null $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;
|
||
}
|
||
|
||
/**
|
||
* @param $content
|
||
* @return $this
|
||
*/
|
||
public function addBody($content)
|
||
{
|
||
$this->getResponse()->appendBody($content);
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* @param $description
|
||
* @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 eController
|
||
*/
|
||
public function addTitle($title, $meta = true)
|
||
{
|
||
$this->getResponse()->appendTitle($title);
|
||
if($meta) $this->addMetaTitle(strip_tags($title));
|
||
return $this;
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $title
|
||
* @return $this
|
||
*/
|
||
public function addMetaTitle($title)
|
||
{
|
||
$this->getResponse()->addMetaTitle($title);
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* @param $actionMethodName
|
||
* @return void
|
||
* @throws eException
|
||
*/
|
||
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();
|
||
}
|
||
|
||
/**
|
||
* @param eRequest|null $request
|
||
* @param eResponse|null $response
|
||
* @return eResponse
|
||
* @throws eException
|
||
*/
|
||
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();
|
||
}
|
||
|
||
/**
|
||
* @param $url
|
||
* @param $createURL
|
||
* @param $code
|
||
* @return void
|
||
*/
|
||
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 (strpos($methodName, 'action') === 0)
|
||
{
|
||
$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;
|
||
}
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
public function redirect404()
|
||
{
|
||
e107::getRedirect()->redirect(SITEURL.$this->e404);
|
||
}
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
public function redirect403()
|
||
{
|
||
e107::getRedirect()->redirect(SITEURL.$this->e403);
|
||
}
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
public function forward404()
|
||
{
|
||
$this->_forward($this->e404route);
|
||
}
|
||
|
||
/**
|
||
* @return void
|
||
*/
|
||
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 $requestInfo
|
||
* @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 eRequest
|
||
*/
|
||
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();
|
||
}
|
||
|
||
if(!defined('e_SELF'))
|
||
{
|
||
define("e_SELF", e_REQUEST_SELF);
|
||
}
|
||
|
||
if(!defined('e_QUERY'))
|
||
{
|
||
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(); // partial <title> tag.
|
||
protected $_e_PAGETITLE_OVERRIDE = array(); // Full <title> tag
|
||
protected $_META_DESCRIPTION = array();
|
||
protected $_META_KEYWORDS = array();
|
||
protected $_render_mod = array('default' => 'default');
|
||
protected $_meta_title_separator = ' - ';
|
||
protected $_meta_name_only = array(
|
||
'keywords', 'viewport', 'robots', 'twitter:url', 'twitter:title', 'twitter:card',
|
||
'twitter:image', 'twitter:description',
|
||
); // Keep FB happy.
|
||
protected $_meta_property_only = array( // Keep FB happy.
|
||
'article:section', 'article:tag', 'article:published_time', 'article:modified_time',
|
||
'og:description', 'og:image', 'og:title', 'og:updated_time','og:url', 'og:type'
|
||
);
|
||
protected $_meta_multiple = array();
|
||
protected $_meta = array();
|
||
protected $_meta_robot_types = array('noindex'=>'NoIndex', 'nofollow'=>'NoFollow','noarchive'=>'NoArchive','noimageindex'=>'NoImageIndex' );
|
||
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,
|
||
);
|
||
|
||
/**
|
||
* @return string[]
|
||
*/
|
||
public function getRobotTypes()
|
||
{
|
||
return $this->_meta_robot_types;
|
||
}
|
||
|
||
/**
|
||
* @return array
|
||
*/
|
||
public function getRobotDescriptions()
|
||
{
|
||
$_meta_robot_descriptions = array(
|
||
'noindex' => LAN_ROBOTS_NOINDEX,
|
||
'nofollow' => LAN_ROBOTS_NOFOLLOW,
|
||
'noarchive' => LAN_ROBOTS_NOARCHIVE,
|
||
'noimageindex' => LAN_ROBOTS_NOIMAGE );
|
||
|
||
return $_meta_robot_descriptions;
|
||
}
|
||
|
||
/**
|
||
* @param $key
|
||
* @param $value
|
||
* @return $this
|
||
*/
|
||
public function setParam($key, $value)
|
||
{
|
||
$this->_params[$key] = $value;
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* @param $params
|
||
* @return $this
|
||
*/
|
||
public function setParams($params)
|
||
{
|
||
$this->_params = $params;
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* @param $key
|
||
* @param $default
|
||
* @return mixed|null
|
||
*/
|
||
public function getParam($key, $default = null)
|
||
{
|
||
return (isset($this->_params[$key]) ? $this->_params[$key] : $default);
|
||
}
|
||
|
||
/**
|
||
* @param $key
|
||
* @return bool
|
||
*/
|
||
public function isParam($key)
|
||
{
|
||
return isset($this->_params[$key]);
|
||
}
|
||
|
||
/**
|
||
* @param $typeName
|
||
* @param $mediaType
|
||
* @return $this
|
||
*/
|
||
public function addContentType($typeName, $mediaType)
|
||
{
|
||
$this->_content_type_arr[$typeName] = $mediaType;
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
public function getContentType()
|
||
{
|
||
return $this->_content_type;
|
||
}
|
||
|
||
/**
|
||
* @param $typeName
|
||
* @return mixed|string|void
|
||
*/
|
||
public function getContentMediaType($typeName)
|
||
{
|
||
if(isset($this->_content_type_arr[$typeName]))
|
||
return $this->_content_type_arr[$typeName];
|
||
}
|
||
|
||
/**
|
||
* @param $typeName
|
||
* @return void
|
||
*/
|
||
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 string $body
|
||
* @param string $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 string $body
|
||
* @param string $ns namespace
|
||
* @return eResponse
|
||
*/
|
||
public function setBody($body, $ns = 'default')
|
||
{
|
||
$this->_body[$ns] = $body;
|
||
return $this;
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $name
|
||
* @param $content
|
||
* @return $this
|
||
*/
|
||
public function setMeta($name, $content)
|
||
{
|
||
foreach($this->_meta as $k=>$v)
|
||
{
|
||
if(!empty($v['name']) && ($v['name'] === $name))
|
||
{
|
||
$this->_meta[$k]['content'] = $content;
|
||
}
|
||
}
|
||
|
||
return $this;
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Removes a Meta tag by name/property.
|
||
*
|
||
* @param string $name
|
||
* 'name' or 'property' for the meta tag we want to remove.
|
||
*
|
||
* @return eResponse $this
|
||
*/
|
||
public function removeMeta($name)
|
||
{
|
||
foreach($this->_meta as $k=>$v)
|
||
{
|
||
// Meta tags like: <meta content="..." name="description" />
|
||
if(isset($v['name']) && $v['name'] === $name)
|
||
{
|
||
unset($this->_meta[$k]);
|
||
continue;
|
||
}
|
||
|
||
// Meta tags like: <meta content="..." property="og:title" />
|
||
if(isset($v['property']) && $v['property'] === $name)
|
||
{
|
||
unset($this->_meta[$k]);
|
||
}
|
||
}
|
||
|
||
return $this;
|
||
}
|
||
|
||
|
||
/**
|
||
* Prepend content
|
||
* @param string $body
|
||
* @param string $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 string $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 string $title
|
||
* @param string $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 string $title
|
||
* @param string $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 string $title
|
||
* @param string $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 string $ns
|
||
* @param bool $reset
|
||
* @return string
|
||
*/
|
||
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 mixed $ns
|
||
* @return eResponse
|
||
*/
|
||
function setRenderMod($render_mod, $ns = 'default')
|
||
{
|
||
$this->_render_mod[$ns] = $render_mod;
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* Retrieve render mod
|
||
* @param mixed $ns
|
||
* @return mixed
|
||
*/
|
||
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(!in_array($name, $this->_meta_name_only))
|
||
{
|
||
$attr['property'] = $name; // giving both should be valid and avoid issues with FB and others.
|
||
}
|
||
|
||
if(!in_array($name, $this->_meta_property_only))
|
||
{
|
||
$attr['name'] = $name;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
if(null !== $content) $attr['content'] = $content;
|
||
if(!empty($extended))
|
||
{
|
||
if(!empty($attr)) $attr = array_merge($attr, $extended);
|
||
else $attr = $extended;
|
||
}
|
||
|
||
|
||
if(!empty($attr))
|
||
{
|
||
if(!in_array($name, $this->_meta_multiple)) // prevent multiple keyword tags.
|
||
{
|
||
$this->_meta[$name] = $attr;
|
||
}
|
||
else // multiple allowed.
|
||
{
|
||
$this->_meta[] = $attr;
|
||
}
|
||
}
|
||
|
||
return $this;
|
||
}
|
||
|
||
/**
|
||
* Render meta tags, registered via addMeta() method
|
||
* @return string
|
||
*/
|
||
public function renderMeta()
|
||
{
|
||
$attrData = '';
|
||
|
||
e107::getEvent()->trigger('system_meta_pre', $this->_meta);
|
||
|
||
$pref = e107::getPref();
|
||
|
||
if(!empty($pref['meta_keywords'][e_LANGUAGE])) // Always append (global) meta keywords to the end.
|
||
{
|
||
$tmp1 = (array) explode(",", $this->getMetaKeywords());
|
||
$tmp2 = (array) explode(",", $pref['meta_keywords'][e_LANGUAGE]);
|
||
|
||
$tmp3 = array_unique(array_merge($tmp1,$tmp2));
|
||
|
||
$this->setMeta('keywords', implode(',',$tmp3));
|
||
}
|
||
|
||
|
||
|
||
e107::getDebug()->log($this->_meta);
|
||
|
||
|
||
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('&', '&', $content);
|
||
|
||
if($meta !== '_e_PAGETITLE' && $meta !== '_e_PAGETITLE_OVERRIDE')
|
||
{
|
||
$content = htmlspecialchars((string) $content, ENT_QUOTES, 'UTF-8');
|
||
}
|
||
|
||
$this->{$meta}[] = $content;
|
||
}
|
||
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 '';
|
||
}
|
||
|
||
|
||
|
||
/**
|
||
* Return an array of all meta data
|
||
* @return array
|
||
*/
|
||
function getMeta()
|
||
{
|
||
return $this->_meta;
|
||
}
|
||
|
||
|
||
/**
|
||
* @param string $title
|
||
* @return eResponse
|
||
*/
|
||
public function addMetaTitle($title, $reset=false, $override=false)
|
||
{
|
||
if($reset)
|
||
{
|
||
if($override)
|
||
{
|
||
$this->_e_PAGETITLE_OVERRIDE = array();
|
||
}
|
||
else
|
||
{
|
||
$this->_e_PAGETITLE = array();
|
||
}
|
||
}
|
||
|
||
$title = str_replace([''','''], "'", $title);
|
||
|
||
if($override)
|
||
{
|
||
return $this->addMetaData('e_PAGETITLE_OVERRIDE', $title);
|
||
}
|
||
|
||
return $this->addMetaData('e_PAGETITLE', $title);
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
public function getMetaTitle($override = false)
|
||
{
|
||
if($override)
|
||
{
|
||
return $this->getMetaData('e_PAGETITLE_OVERRIDE', $this->_meta_title_separator);
|
||
}
|
||
|
||
return $this->getMetaData('e_PAGETITLE', $this->_meta_title_separator);
|
||
}
|
||
|
||
/**
|
||
* @param string $description
|
||
* @return eResponse
|
||
*/
|
||
function addMetaDescription($description)
|
||
{
|
||
return $this->addMetaData('META_DESCRIPTION', $description);
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
function getMetaDescription()
|
||
{
|
||
return $this->getMetaData('META_DESCRIPTION');
|
||
}
|
||
|
||
/**
|
||
* @param string $keywords
|
||
* @return eResponse
|
||
*/
|
||
function addMetaKeywords($keywords)
|
||
{
|
||
return $this->addMetaData('META_KEYWORDS', $keywords);
|
||
}
|
||
|
||
/**
|
||
* @return string
|
||
*/
|
||
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
|
||
* @return null|string
|
||
*/
|
||
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, // FIXME $statusText has no value.
|
||
), $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\-\.;:!]#';
|
||
|
||
protected static $_systemNotify = 'systemNotifications';
|
||
|
||
/**
|
||
* @param $string
|
||
* @return array|string|string[]|null
|
||
*/
|
||
public static function secureClassAttr($string)
|
||
{
|
||
return preg_replace(self::$_classRegEx, '', $string);
|
||
}
|
||
|
||
|
||
/**
|
||
* @return array
|
||
*/
|
||
public static function getSystemNotification()
|
||
{
|
||
return (array) e107::getSession()->get(self::$_systemNotify);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param string $id
|
||
* @param string $message
|
||
* @return array|e_core_session|null
|
||
*/
|
||
public static function addSystemNotification($id, $message)
|
||
{
|
||
return e107::getSession()->set(self::$_systemNotify.'/'.$id.'/message', $message);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $id
|
||
* @return array|e_core_session|null
|
||
*/
|
||
public static function clearSystemNotification($id = '')
|
||
{
|
||
$key = !empty($id) ? self::$_systemNotify.'/'.$id : self::$_systemNotify;
|
||
|
||
return e107::getSession()->clear($key);
|
||
}
|
||
|
||
|
||
/**
|
||
* @param $string
|
||
* @return array|string|string[]|null
|
||
*/
|
||
public static function secureIdAttr($string)
|
||
{
|
||
$string = str_replace(array('/','_'),'-',$string);
|
||
return preg_replace(self::$_idRegEx, '', $string);
|
||
}
|
||
|
||
/**
|
||
* @param $string
|
||
* @return array|string|string[]|null
|
||
*/
|
||
public static function secureStyleAttr($string)
|
||
{
|
||
return preg_replace(self::$_styleRegEx, '', $string);
|
||
}
|
||
|
||
/**
|
||
* @param $safeArray
|
||
* @return string
|
||
*/
|
||
public static function buildAttr($safeArray)
|
||
{
|
||
return http_build_query($safeArray, null, '&');
|
||
}
|
||
|
||
/**
|
||
* @param $title
|
||
* @return string
|
||
*/
|
||
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)));
|
||
}
|
||
|
||
/**
|
||
* @param $sef
|
||
* @return string
|
||
*/
|
||
public static function secureSef($sef)
|
||
{
|
||
return trim(preg_replace('/[^\w\pL\s\-+.,]+/u', '', strip_tags(e107::getParser()->toHTML($sef, TRUE))));
|
||
}
|
||
|
||
/**
|
||
* @param $keywordString
|
||
* @return string
|
||
*/
|
||
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)));
|
||
}
|
||
|
||
/**
|
||
* @param $descrString
|
||
* @return string
|
||
*/
|
||
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
|
||
* @return array|string|string[]
|
||
*/
|
||
public static function title2sef($title, $type = null)
|
||
{
|
||
/*$char_map = array(
|
||
// Latin
|
||
'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C',
|
||
'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I',
|
||
'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O',
|
||
'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH',
|
||
'ß' => 'ss',
|
||
'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c',
|
||
'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i',
|
||
'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o',
|
||
'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th',
|
||
'ÿ' => 'y',
|
||
// Latin symbols
|
||
'©' => '(c)',
|
||
// Greek
|
||
'Α' => 'A', 'Β' => 'B', 'Γ' => 'G', 'Δ' => 'D', 'Ε' => 'E', 'Ζ' => 'Z', 'Η' => 'H', 'Θ' => '8',
|
||
'Ι' => 'I', 'Κ' => 'K', 'Λ' => 'L', 'Μ' => 'M', 'Ν' => 'N', 'Ξ' => '3', 'Ο' => 'O', 'Π' => 'P',
|
||
'Ρ' => 'R', 'Σ' => 'S', 'Τ' => 'T', 'Υ' => 'Y', 'Φ' => 'F', 'Χ' => 'X', 'Ψ' => 'PS', 'Ω' => 'W',
|
||
'Ά' => 'A', 'Έ' => 'E', 'Ί' => 'I', 'Ό' => 'O', 'Ύ' => 'Y', 'Ή' => 'H', 'Ώ' => 'W', 'Ϊ' => 'I',
|
||
'Ϋ' => 'Y',
|
||
'α' => 'a', 'β' => 'b', 'γ' => 'g', 'δ' => 'd', 'ε' => 'e', 'ζ' => 'z', 'η' => 'h', 'θ' => '8',
|
||
'ι' => 'i', 'κ' => 'k', 'λ' => 'l', 'μ' => 'm', 'ν' => 'n', 'ξ' => '3', 'ο' => 'o', 'π' => 'p',
|
||
'ρ' => 'r', 'σ' => 's', 'τ' => 't', 'υ' => 'y', 'φ' => 'f', 'χ' => 'x', 'ψ' => 'ps', 'ω' => 'w',
|
||
'ά' => 'a', 'έ' => 'e', 'ί' => 'i', 'ό' => 'o', 'ύ' => 'y', 'ή' => 'h', 'ώ' => 'w', 'ς' => 's',
|
||
'ϊ' => 'i', 'ΰ' => 'y', 'ϋ' => 'y', 'ΐ' => 'i',
|
||
// Turkish
|
||
'Ş' => 'S', 'İ' => 'I', 'Ç' => 'C', 'Ü' => 'U', 'Ö' => 'O', 'Ğ' => 'G',
|
||
'ş' => 's', 'ı' => 'i', 'ç' => 'c', 'ü' => 'u', 'ö' => 'o', 'ğ' => 'g',
|
||
// Russian
|
||
'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D', 'Е' => 'E', 'Ё' => 'Yo', 'Ж' => 'Zh',
|
||
'З' => 'Z', 'И' => 'I', 'Й' => 'J', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N', 'О' => 'O',
|
||
'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T', 'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
|
||
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sh', 'Ъ' => '', 'Ы' => 'Y', 'Ь' => '', 'Э' => 'E', 'Ю' => 'Yu',
|
||
'Я' => 'Ya',
|
||
'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd', 'е' => 'e', 'ё' => 'yo', 'ж' => 'zh',
|
||
'з' => 'z', 'и' => 'i', 'й' => 'j', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n', 'о' => 'o',
|
||
'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't', 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'c',
|
||
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sh', 'ъ' => '', 'ы' => 'y', 'ь' => '', 'э' => 'e', 'ю' => 'yu',
|
||
'я' => 'ya',
|
||
// Ukrainian
|
||
'Є' => 'Ye', 'І' => 'I', 'Ї' => 'Yi', 'Ґ' => 'G',
|
||
'є' => 'ye', 'і' => 'i', 'ї' => 'yi', 'ґ' => 'g',
|
||
// Czech
|
||
'Č' => 'C', 'Ď' => 'D', 'Ě' => 'E', 'Ň' => 'N', 'Ř' => 'R', 'Š' => 'S', 'Ť' => 'T', 'Ů' => 'U',
|
||
'Ž' => 'Z',
|
||
'č' => 'c', 'ď' => 'd', 'ě' => 'e', 'ň' => 'n', 'ř' => 'r', 'š' => 's', 'ť' => 't', 'ů' => 'u',
|
||
'ž' => 'z',
|
||
// Polish
|
||
'Ą' => 'A', 'Ć' => 'C', 'Ę' => 'e', 'Ł' => 'L', 'Ń' => 'N', 'Ó' => 'o', 'Ś' => 'S', 'Ź' => 'Z',
|
||
'Ż' => 'Z',
|
||
'ą' => 'a', 'ć' => 'c', 'ę' => 'e', 'ł' => 'l', 'ń' => 'n', 'ó' => 'o', 'ś' => 's', 'ź' => 'z',
|
||
'ż' => 'z',
|
||
// Latvian
|
||
'Ā' => 'A', 'Č' => 'C', 'Ē' => 'E', 'Ģ' => 'G', 'Ī' => 'i', 'Ķ' => 'k', 'Ļ' => 'L', 'Ņ' => 'N',
|
||
'Š' => 'S', 'Ū' => 'u', 'Ž' => 'Z',
|
||
'ā' => 'a', 'č' => 'c', 'ē' => 'e', 'ģ' => 'g', 'ī' => 'i', 'ķ' => 'k', 'ļ' => 'l', 'ņ' => 'n',
|
||
'š' => 's', 'ū' => 'u', 'ž' => 'z'
|
||
);*/
|
||
|
||
$tp = e107::getParser();
|
||
|
||
// issue #3245: strip all html and bbcode before processing
|
||
$title = $tp->toText($title);
|
||
|
||
$title = $tp->toASCII($title);
|
||
|
||
$title = str_replace(array('/',' ',","),' ',$title);
|
||
$title = str_replace(array("&","(",")"),'',$title);
|
||
$title = preg_replace('/[^\w\pL\s.-]/u', '', strip_tags(e107::getParser()->toHTML($title, TRUE)));
|
||
$title = trim(preg_replace('/[\s]+/', ' ', str_replace('_', ' ', $title)));
|
||
$title = str_replace(array(' - ',' -','- ','--'),'-',$title); // cleanup to avoid ---
|
||
|
||
$words = str_word_count($title,1, '1234567890');
|
||
|
||
$limited = array_slice($words, 0, 14); // Limit number of words to 14. - any more and it ain't friendly.
|
||
|
||
$title = implode(" ",$limited);
|
||
|
||
if(null === $type)
|
||
{
|
||
$type = e107::getPref('url_sef_translate');
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
/**
|
||
* @param $str
|
||
* @param $all
|
||
* @param $space
|
||
* @return string
|
||
*/
|
||
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);
|
||
}
|
||
|
||
/**
|
||
* @param $str
|
||
* @param $space
|
||
* @return string
|
||
*/
|
||
public static function labelize($str, $space = ' ')
|
||
{
|
||
return self::camelize($str, true, ' ');
|
||
}
|
||
|
||
/**
|
||
* @param $str
|
||
* @return array|string|string[]
|
||
*/
|
||
public static function dasherize($str)
|
||
{
|
||
return str_replace(array('_', ' '), '-', $str);
|
||
}
|
||
|
||
/**
|
||
* @param $str
|
||
* @return array|string|string[]
|
||
*/
|
||
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 $parm
|
||
* @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);
|
||
}
|
||
|
||
|
||
/**
|
||
* Remove Social Media Trackers from a $_GET array based on key matches.
|
||
* @param array $get
|
||
* @return array
|
||
*/
|
||
public static function removeTrackers($get = array())
|
||
{
|
||
$trackers = array('fbclid','utm_source','utm_medium','utm_content','utm_campaign','elan', 'msclkid', 'gclid', 'gad', 'gad_source', 'mcp_token');
|
||
|
||
foreach($trackers as $val)
|
||
{
|
||
if(isset($get[$val]))
|
||
{
|
||
unset($get[$val]);
|
||
}
|
||
}
|
||
|
||
return $get;
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* Duplicates the value from a title form field into the meta-title form field.
|
||
* @param str $titleID eg. news-title
|
||
* @param str $metaTitleID eg. news-meta-title
|
||
* @return void
|
||
*/
|
||
public static function syncSEOTitle($titleID, $metaTitleID)
|
||
{
|
||
|
||
e107::js('footer-inline', '
|
||
|
||
$(window).on("load", function () {
|
||
|
||
if(!$("#'.$metaTitleID.'").val())
|
||
{
|
||
var title = $("#'.$titleID.'").val() + " | " + "'.SITENAME.'";
|
||
var charlimit = $("#'.$metaTitleID.'").attr("data-char-count");
|
||
|
||
$("#'.$metaTitleID.'").attr("placeholder",title);
|
||
|
||
if(title.length > charlimit)
|
||
{
|
||
$("#'.$metaTitleID.'").addClass("has-error");
|
||
}
|
||
}
|
||
|
||
});
|
||
|
||
|
||
$("#'.$metaTitleID.'").on("ready focus", function() {
|
||
|
||
var title = $("#'.$titleID.'").val() + " | " + "'.SITENAME.'";
|
||
|
||
if(!$(this).val())
|
||
{
|
||
$(this).val(title);
|
||
}
|
||
else
|
||
{
|
||
$(this).attr("placeholder",title);
|
||
}
|
||
|
||
});
|
||
|
||
|
||
$("#'.$titleID.'").on("input change focus", function()
|
||
{
|
||
var title = $("#'.$titleID.'").val() + " | " + "'.SITENAME.'";
|
||
$("#'.$metaTitleID.'").attr("placeholder",title);
|
||
|
||
});
|
||
|
||
');
|
||
|
||
|
||
}
|
||
|
||
|
||
}
|