876 lines
29 KiB
PHP
876 lines
29 KiB
PHP
|
<?php
|
||
|
/*
|
||
|
* 2007-2014 PrestaShop
|
||
|
*
|
||
|
* NOTICE OF LICENSE
|
||
|
*
|
||
|
* This source file is subject to the Open Software License (OSL 3.0)
|
||
|
* that is bundled with this package in the file LICENSE.txt.
|
||
|
* It is also available through the world-wide-web at this URL:
|
||
|
* http://opensource.org/licenses/osl-3.0.php
|
||
|
* If you did not receive a copy of the license and are unable to
|
||
|
* obtain it through the world-wide-web, please send an email
|
||
|
* to license@prestashop.com so we can send you a copy immediately.
|
||
|
*
|
||
|
* DISCLAIMER
|
||
|
*
|
||
|
* Do not edit or add to this file if you wish to upgrade PrestaShop to newer
|
||
|
* versions in the future. If you wish to customize PrestaShop for your
|
||
|
* needs please refer to http://www.prestashop.com for more information.
|
||
|
*
|
||
|
* @author PrestaShop SA <contact@prestashop.com>
|
||
|
* @copyright 2007-2014 PrestaShop SA
|
||
|
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
|
||
|
* International Registered Trademark & Property of PrestaShop SA
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* @since 1.5.0
|
||
|
*/
|
||
|
class DispatcherCore
|
||
|
{
|
||
|
/**
|
||
|
* List of available front controllers types
|
||
|
*/
|
||
|
const FC_FRONT = 1;
|
||
|
const FC_ADMIN = 2;
|
||
|
const FC_MODULE = 3;
|
||
|
|
||
|
/**
|
||
|
* @var Dispatcher
|
||
|
*/
|
||
|
public static $instance = null;
|
||
|
|
||
|
/**
|
||
|
* @var array List of default routes
|
||
|
*/
|
||
|
public $default_routes = array(
|
||
|
'category_rule' => array(
|
||
|
'controller' => 'category',
|
||
|
'rule' => '{id}-{rewrite}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_category'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
'supplier_rule' => array(
|
||
|
'controller' => 'supplier',
|
||
|
'rule' => '{id}__{rewrite}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_supplier'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
'manufacturer_rule' => array(
|
||
|
'controller' => 'manufacturer',
|
||
|
'rule' => '{id}_{rewrite}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_manufacturer'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
'cms_rule' => array(
|
||
|
'controller' => 'cms',
|
||
|
'rule' => 'content/{id}-{rewrite}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_cms'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
'cms_category_rule' => array(
|
||
|
'controller' => 'cms',
|
||
|
'rule' => 'content/category/{id}-{rewrite}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_cms_category'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
'module' => array(
|
||
|
'controller' => null,
|
||
|
'rule' => 'module/{module}{/:controller}',
|
||
|
'keywords' => array(
|
||
|
'module' => array('regexp' => '[_a-zA-Z0-9_-]+', 'param' => 'module'),
|
||
|
'controller' => array('regexp' => '[_a-zA-Z0-9_-]+', 'param' => 'controller'),
|
||
|
),
|
||
|
'params' => array(
|
||
|
'fc' => 'module',
|
||
|
),
|
||
|
),
|
||
|
'product_rule' => array(
|
||
|
'controller' => 'product',
|
||
|
'rule' => '{category:/}{id}-{rewrite}{-:ean13}.html',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_product'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'ean13' => array('regexp' => '[0-9\pL]*'),
|
||
|
'category' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'categories' => array('regexp' => '[/_a-zA-Z0-9-\pL]*'),
|
||
|
'reference' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'manufacturer' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'supplier' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'price' => array('regexp' => '[0-9\.,]*'),
|
||
|
'tags' => array('regexp' => '[a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
// Must be after the product and category rules in order to avoid conflict
|
||
|
'layered_rule' => array(
|
||
|
'controller' => 'category',
|
||
|
'rule' => '{id}-{rewrite}{/:selected_filters}',
|
||
|
'keywords' => array(
|
||
|
'id' => array('regexp' => '[0-9]+', 'param' => 'id_category'),
|
||
|
/* Selected filters is used by the module blocklayered */
|
||
|
'selected_filters' => array('regexp' => '.*', 'param' => 'selected_filters'),
|
||
|
'rewrite' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_keywords' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
'meta_title' => array('regexp' => '[_a-zA-Z0-9-\pL]*'),
|
||
|
),
|
||
|
),
|
||
|
);
|
||
|
|
||
|
/**
|
||
|
* @var bool If true, use routes to build URL (mod rewrite must be activated)
|
||
|
*/
|
||
|
protected $use_routes = false;
|
||
|
|
||
|
protected $multilang_activated = false;
|
||
|
|
||
|
/**
|
||
|
* @var array List of loaded routes
|
||
|
*/
|
||
|
protected $routes = array();
|
||
|
|
||
|
/**
|
||
|
* @var string Current controller name
|
||
|
*/
|
||
|
protected $controller;
|
||
|
|
||
|
/**
|
||
|
* @var string Current request uri
|
||
|
*/
|
||
|
protected $request_uri;
|
||
|
|
||
|
/**
|
||
|
* @var array Store empty route (a route with an empty rule)
|
||
|
*/
|
||
|
protected $empty_route;
|
||
|
|
||
|
/**
|
||
|
* @var string Set default controller, which will be used if http parameter 'controller' is empty
|
||
|
*/
|
||
|
protected $default_controller;
|
||
|
protected $use_default_controller = false;
|
||
|
|
||
|
/**
|
||
|
* @var string Controller to use if found controller doesn't exist
|
||
|
*/
|
||
|
protected $controller_not_found = 'pagenotfound';
|
||
|
|
||
|
/**
|
||
|
* @var string Front controller to use
|
||
|
*/
|
||
|
protected $front_controller = self::FC_FRONT;
|
||
|
|
||
|
/**
|
||
|
* Get current instance of dispatcher (singleton)
|
||
|
*
|
||
|
* @return Dispatcher
|
||
|
*/
|
||
|
public static function getInstance()
|
||
|
{
|
||
|
if (!self::$instance)
|
||
|
self::$instance = new Dispatcher();
|
||
|
return self::$instance;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Need to be instancied from getInstance() method
|
||
|
*/
|
||
|
protected function __construct()
|
||
|
{
|
||
|
$this->use_routes = (bool)Configuration::get('PS_REWRITING_SETTINGS');
|
||
|
|
||
|
// Select right front controller
|
||
|
if (defined('_PS_ADMIN_DIR_'))
|
||
|
{
|
||
|
$this->front_controller = self::FC_ADMIN;
|
||
|
$this->controller_not_found = 'adminnotfound';
|
||
|
}
|
||
|
elseif (Tools::getValue('fc') == 'module')
|
||
|
{
|
||
|
$this->front_controller = self::FC_MODULE;
|
||
|
$this->controller_not_found = 'pagenotfound';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$this->front_controller = self::FC_FRONT;
|
||
|
$this->controller_not_found = 'pagenotfound';
|
||
|
}
|
||
|
|
||
|
$this->setRequestUri();
|
||
|
|
||
|
// Switch language if needed (only on front)
|
||
|
if (in_array($this->front_controller, array(self::FC_FRONT, self::FC_MODULE)))
|
||
|
Tools::switchLanguage();
|
||
|
|
||
|
if (Language::isMultiLanguageActivated())
|
||
|
$this->multilang_activated = true;
|
||
|
|
||
|
$this->loadRoutes();
|
||
|
}
|
||
|
|
||
|
public function useDefaultController()
|
||
|
{
|
||
|
$this->use_default_controller = true;
|
||
|
if ($this->default_controller === null)
|
||
|
{
|
||
|
if (defined('_PS_ADMIN_DIR_'))
|
||
|
{
|
||
|
$this->default_controller = 'admindashboard';
|
||
|
if (isset(Context::getContext()->employee) && Validate::isLoadedObject(Context::getContext()->employee) && isset(Context::getContext()->employee->default_tab))
|
||
|
$this->default_controller = Tab::getClassNameById((int)Context::getContext()->employee->default_tab);
|
||
|
}
|
||
|
elseif (Tools::getValue('fc') == 'module')
|
||
|
$this->default_controller = 'default';
|
||
|
else
|
||
|
$this->default_controller = 'index';
|
||
|
}
|
||
|
return $this->default_controller;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Find the controller and instantiate it
|
||
|
*/
|
||
|
public function dispatch()
|
||
|
{
|
||
|
$controller_class = '';
|
||
|
|
||
|
// Get current controller
|
||
|
$this->getController();
|
||
|
if (!$this->controller)
|
||
|
$this->controller = $this->useDefaultController();
|
||
|
// Dispatch with right front controller
|
||
|
switch ($this->front_controller)
|
||
|
{
|
||
|
// Dispatch front office controller
|
||
|
case self::FC_FRONT :
|
||
|
$controllers = Dispatcher::getControllers(array(_PS_FRONT_CONTROLLER_DIR_, _PS_OVERRIDE_DIR_.'controllers/front/'));
|
||
|
|
||
|
$controllers['index'] = 'IndexController';
|
||
|
if (isset($controllers['auth']))
|
||
|
$controllers['authentication'] = $controllers['auth'];
|
||
|
if (isset($controllers['compare']))
|
||
|
$controllers['productscomparison'] = $controllers['compare'];
|
||
|
if (isset($controllers['contact']))
|
||
|
$controllers['contactform'] = $controllers['contact'];
|
||
|
|
||
|
if (!isset($controllers[strtolower($this->controller)]))
|
||
|
$this->controller = $this->controller_not_found;
|
||
|
$controller_class = $controllers[strtolower($this->controller)];
|
||
|
$params_hook_action_dispatcher = array('controller_type' => self::FC_FRONT, 'controller_class' => $controller_class, 'is_module' => 0);
|
||
|
break;
|
||
|
|
||
|
// Dispatch module controller for front office
|
||
|
case self::FC_MODULE :
|
||
|
$module_name = Validate::isModuleName(Tools::getValue('module')) ? Tools::getValue('module') : '';
|
||
|
$module = Module::getInstanceByName($module_name);
|
||
|
$controller_class = 'PageNotFoundController';
|
||
|
if (Validate::isLoadedObject($module) && $module->active)
|
||
|
{
|
||
|
$controllers = Dispatcher::getControllers(_PS_MODULE_DIR_.$module_name.'/controllers/front/');
|
||
|
if (isset($controllers[strtolower($this->controller)]))
|
||
|
{
|
||
|
include_once(_PS_MODULE_DIR_.$module_name.'/controllers/front/'.$this->controller.'.php');
|
||
|
$controller_class = $module_name.$this->controller.'ModuleFrontController';
|
||
|
}
|
||
|
}
|
||
|
$params_hook_action_dispatcher = array('controller_type' => self::FC_FRONT, 'controller_class' => $controller_class, 'is_module' => 1);
|
||
|
break;
|
||
|
|
||
|
// Dispatch back office controller + module back office controller
|
||
|
case self::FC_ADMIN :
|
||
|
if ($this->use_default_controller && !Tools::getValue('token') && Validate::isLoadedObject(Context::getContext()->employee) && Context::getContext()->employee->isLoggedBack())
|
||
|
Tools::redirectAdmin('index.php?controller='.$this->controller.'&token='.Tools::getAdminTokenLite($this->controller));
|
||
|
|
||
|
$tab = Tab::getInstanceFromClassName($this->controller, Configuration::get('PS_LANG_DEFAULT'));
|
||
|
$retrocompatibility_admin_tab = null;
|
||
|
|
||
|
if ($tab->module)
|
||
|
{
|
||
|
if (file_exists(_PS_MODULE_DIR_.$tab->module.'/'.$tab->class_name.'.php'))
|
||
|
$retrocompatibility_admin_tab = _PS_MODULE_DIR_.$tab->module.'/'.$tab->class_name.'.php';
|
||
|
else
|
||
|
{
|
||
|
$controllers = Dispatcher::getControllers(_PS_MODULE_DIR_.$tab->module.'/controllers/admin/');
|
||
|
if (!isset($controllers[strtolower($this->controller)]))
|
||
|
{
|
||
|
$this->controller = $this->controller_not_found;
|
||
|
$controller_class = 'AdminNotFoundController';
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Controllers in modules can be named AdminXXX.php or AdminXXXController.php
|
||
|
include_once(_PS_MODULE_DIR_.$tab->module.'/controllers/admin/'.$controllers[strtolower($this->controller)].'.php');
|
||
|
$controller_class = $controllers[strtolower($this->controller)].(strpos($controllers[strtolower($this->controller)], 'Controller') ? '' : 'Controller');
|
||
|
}
|
||
|
}
|
||
|
$params_hook_action_dispatcher = array('controller_type' => self::FC_ADMIN, 'controller_class' => $controller_class, 'is_module' => 1);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
$controllers = Dispatcher::getControllers(array(_PS_ADMIN_DIR_.'/tabs/', _PS_ADMIN_CONTROLLER_DIR_, _PS_OVERRIDE_DIR_.'controllers/admin/'));
|
||
|
if (!isset($controllers[strtolower($this->controller)]))
|
||
|
{
|
||
|
// If this is a parent tab, load the first child
|
||
|
if (Validate::isLoadedObject($tab) && $tab->id_parent == 0 && ($tabs = Tab::getTabs(Context::getContext()->language->id, $tab->id)) && isset($tabs[0]))
|
||
|
Tools::redirectAdmin(Context::getContext()->link->getAdminLink($tabs[0]['class_name']));
|
||
|
$this->controller = $this->controller_not_found;
|
||
|
}
|
||
|
|
||
|
$controller_class = $controllers[strtolower($this->controller)];
|
||
|
$params_hook_action_dispatcher = array('controller_type' => self::FC_ADMIN, 'controller_class' => $controller_class, 'is_module' => 0);
|
||
|
|
||
|
if (file_exists(_PS_ADMIN_DIR_.'/tabs/'.$controller_class.'.php'))
|
||
|
$retrocompatibility_admin_tab = _PS_ADMIN_DIR_.'/tabs/'.$controller_class.'.php';
|
||
|
}
|
||
|
|
||
|
// @retrocompatibility with admin/tabs/ old system
|
||
|
if ($retrocompatibility_admin_tab)
|
||
|
{
|
||
|
include_once($retrocompatibility_admin_tab);
|
||
|
include_once(_PS_ADMIN_DIR_.'/functions.php');
|
||
|
runAdminTab($this->controller, !empty($_REQUEST['ajaxMode']));
|
||
|
return;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default :
|
||
|
throw new PrestaShopException('Bad front controller chosen');
|
||
|
}
|
||
|
|
||
|
// Instantiate controller
|
||
|
try
|
||
|
{
|
||
|
// Loading controller
|
||
|
$controller = Controller::getController($controller_class);
|
||
|
|
||
|
// Execute hook dispatcher
|
||
|
if (isset($params_hook_action_dispatcher))
|
||
|
Hook::exec('actionDispatcher', $params_hook_action_dispatcher);
|
||
|
|
||
|
// Running controller
|
||
|
$controller->run();
|
||
|
}
|
||
|
catch (PrestaShopException $e)
|
||
|
{
|
||
|
$e->displayMessage();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set request uri and iso lang
|
||
|
*/
|
||
|
protected function setRequestUri()
|
||
|
{
|
||
|
// Get request uri (HTTP_X_REWRITE_URL is used by IIS)
|
||
|
if (isset($_SERVER['REQUEST_URI']))
|
||
|
$this->request_uri = $_SERVER['REQUEST_URI'];
|
||
|
else if (isset($_SERVER['HTTP_X_REWRITE_URL']))
|
||
|
$this->request_uri = $_SERVER['HTTP_X_REWRITE_URL'];
|
||
|
$this->request_uri = rawurldecode($this->request_uri);
|
||
|
|
||
|
if (isset(Context::getContext()->shop) && is_object(Context::getContext()->shop))
|
||
|
$this->request_uri = preg_replace('#^'.preg_quote(Context::getContext()->shop->getBaseURI(), '#').'#i', '/', $this->request_uri);
|
||
|
|
||
|
// If there are several languages, get language from uri
|
||
|
if ($this->use_routes && Language::isMultiLanguageActivated())
|
||
|
if (preg_match('#^/([a-z]{2})(?:/.*)?$#', $this->request_uri, $m))
|
||
|
{
|
||
|
$_GET['isolang'] = $m[1];
|
||
|
$this->request_uri = substr($this->request_uri, 3);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Load default routes group by languages
|
||
|
*/
|
||
|
protected function loadRoutes($id_shop = null)
|
||
|
{
|
||
|
$context = Context::getContext();
|
||
|
|
||
|
// Load custom routes from modules
|
||
|
$modules_routes = Hook::exec('moduleRoutes', array('id_shop' => $id_shop), null, true, false);
|
||
|
if (is_array($modules_routes) && count($modules_routes))
|
||
|
foreach($modules_routes as $module_route)
|
||
|
{
|
||
|
if (is_array($module_route) && count($module_route))
|
||
|
foreach($module_route as $route => $route_details)
|
||
|
if (array_key_exists('controller', $route_details) && array_key_exists('rule', $route_details)
|
||
|
&& array_key_exists('keywords', $route_details) && array_key_exists('params', $route_details))
|
||
|
{
|
||
|
if (!isset($this->default_routes[$route]))
|
||
|
$this->default_routes[$route] = array();
|
||
|
$this->default_routes[$route] = array_merge($this->default_routes[$route], $route_details);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$languages = array();
|
||
|
if (isset($context->language) && !in_array($context->language->id, $languages = Language::getLanguages()))
|
||
|
{
|
||
|
$languages[] = (int)$context->language->id;
|
||
|
// Set default routes
|
||
|
foreach ($languages as $lang)
|
||
|
foreach ($this->default_routes as $id => $route)
|
||
|
$this->addRoute(
|
||
|
$id,
|
||
|
$route['rule'],
|
||
|
$route['controller'],
|
||
|
$lang['id_lang'],
|
||
|
$route['keywords'],
|
||
|
isset($route['params']) ? $route['params'] : array(),
|
||
|
$id_shop
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Load the custom routes prior the defaults to avoid infinite loops
|
||
|
if ($this->use_routes)
|
||
|
{
|
||
|
// Get iso lang
|
||
|
$iso_lang = Tools::getValue('isolang');
|
||
|
if (isset($context->language))
|
||
|
$id_lang = (int)$context->language->id;
|
||
|
if ((!empty($iso_lang) && Validate::isLanguageIsoCode($iso_lang)) || !isset($id_lang))
|
||
|
$id_lang = Language::getIdByIso($iso_lang);
|
||
|
|
||
|
// Load routes from meta table
|
||
|
$sql = 'SELECT m.page, ml.url_rewrite, ml.id_lang
|
||
|
FROM `'._DB_PREFIX_.'meta` m
|
||
|
LEFT JOIN `'._DB_PREFIX_.'meta_lang` ml ON (m.id_meta = ml.id_meta'.Shop::addSqlRestrictionOnLang('ml', $id_shop).')
|
||
|
ORDER BY LENGTH(ml.url_rewrite) DESC';
|
||
|
if ($results = Db::getInstance()->executeS($sql))
|
||
|
foreach ($results as $row)
|
||
|
{
|
||
|
if ($row['url_rewrite'])
|
||
|
$this->addRoute($row['page'], $row['url_rewrite'], $row['page'], $row['id_lang'], array(), array(), $id_shop);
|
||
|
}
|
||
|
|
||
|
// Set default empty route if no empty route (that's weird I know)
|
||
|
if (!$this->empty_route)
|
||
|
$this->empty_route = array(
|
||
|
'routeID' => 'index',
|
||
|
'rule' => '',
|
||
|
'controller' => 'index',
|
||
|
);
|
||
|
|
||
|
// Load custom routes
|
||
|
foreach ($this->default_routes as $route_id => $route_data)
|
||
|
if ($custom_route = Configuration::get('PS_ROUTE_'.$route_id, null, null, $id_shop))
|
||
|
{
|
||
|
if (isset($context->language) && !in_array($context->language->id, $languages = Language::getLanguages()))
|
||
|
$languages[] = (int)$context->language->id;
|
||
|
foreach ($languages as $lang)
|
||
|
$this->addRoute(
|
||
|
$route_id,
|
||
|
$custom_route,
|
||
|
$route_data['controller'],
|
||
|
$lang['id_lang'],
|
||
|
$route_data['keywords'],
|
||
|
isset($route_data['params']) ? $route_data['params'] : array(),
|
||
|
$id_shop
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
*
|
||
|
* @param string $route_id Name of the route (need to be uniq, a second route with same name will override the first)
|
||
|
* @param string $rule Url rule
|
||
|
* @param string $controller Controller to call if request uri match the rule
|
||
|
* @param int $id_lang
|
||
|
* @param int $id_shop
|
||
|
*/
|
||
|
public function addRoute($route_id, $rule, $controller, $id_lang = null, array $keywords = array(), array $params = array(), $id_shop = null)
|
||
|
{
|
||
|
if (isset(Context::getContext()->language) && $id_lang === null)
|
||
|
$id_lang = (int)Context::getContext()->language->id;
|
||
|
|
||
|
if (isset(Context::getContext()->shop) && $id_shop === null)
|
||
|
$id_shop = (int)Context::getContext()->shop->id;
|
||
|
|
||
|
$regexp = preg_quote($rule, '#');
|
||
|
if ($keywords)
|
||
|
{
|
||
|
$transform_keywords = array();
|
||
|
preg_match_all('#\\\{(([^{}]*)\\\:)?('.implode('|', array_keys($keywords)).')(\\\:([^{}]*))?\\\}#', $regexp, $m);
|
||
|
for ($i = 0, $total = count($m[0]); $i < $total; $i++)
|
||
|
{
|
||
|
$prepend = $m[2][$i];
|
||
|
$keyword = $m[3][$i];
|
||
|
$append = $m[5][$i];
|
||
|
$transform_keywords[$keyword] = array(
|
||
|
'required' => isset($keywords[$keyword]['param']),
|
||
|
'prepend' => stripslashes($prepend),
|
||
|
'append' => stripslashes($append),
|
||
|
);
|
||
|
|
||
|
$prepend_regexp = $append_regexp = '';
|
||
|
if ($prepend || $append)
|
||
|
{
|
||
|
$prepend_regexp = '('.preg_quote($prepend);
|
||
|
$append_regexp = preg_quote($append).')?';
|
||
|
}
|
||
|
|
||
|
if (isset($keywords[$keyword]['param']))
|
||
|
$regexp = str_replace($m[0][$i], $prepend_regexp.'(?P<'.$keywords[$keyword]['param'].'>'.$keywords[$keyword]['regexp'].')'.$append_regexp, $regexp);
|
||
|
else
|
||
|
$regexp = str_replace($m[0][$i], $prepend_regexp.'('.$keywords[$keyword]['regexp'].')'.$append_regexp, $regexp);
|
||
|
|
||
|
}
|
||
|
$keywords = $transform_keywords;
|
||
|
}
|
||
|
|
||
|
$regexp = '#^/'.$regexp.'(\?.*)?$#u';
|
||
|
if (!isset($this->routes[$id_shop]))
|
||
|
$this->routes[$id_shop] = array();
|
||
|
if (!isset($this->routes[$id_shop][$id_lang]))
|
||
|
$this->routes[$id_shop][$id_lang] = array();
|
||
|
|
||
|
$this->routes[$id_shop][$id_lang][$route_id] = array(
|
||
|
'rule' => $rule,
|
||
|
'regexp' => $regexp,
|
||
|
'controller' => $controller,
|
||
|
'keywords' => $keywords,
|
||
|
'params' => $params,
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a route exists
|
||
|
*
|
||
|
* @param string $route_id
|
||
|
* @param int $id_lang
|
||
|
* @param int $id_shop
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function hasRoute($route_id, $id_lang = null, $id_shop = null)
|
||
|
{
|
||
|
if (isset(Context::getContext()->language) && $id_lang === null)
|
||
|
$id_lang = (int)Context::getContext()->language->id;
|
||
|
if (isset(Context::getContext()->shop) && $id_shop === null)
|
||
|
$id_shop = (int)Context::getContext()->shop->id;
|
||
|
|
||
|
return isset($this->routes[$id_shop]) && isset($this->routes[$id_shop][$id_lang]) && isset($this->routes[$id_shop][$id_lang][$route_id]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a keyword is written in a route rule
|
||
|
*
|
||
|
* @param string $route_id
|
||
|
* @param int $id_lang
|
||
|
* @param string $keyword
|
||
|
* @param int $id_shop
|
||
|
* @return bool
|
||
|
*/
|
||
|
public function hasKeyword($route_id, $id_lang, $keyword, $id_shop = null)
|
||
|
{
|
||
|
if ($id_shop === null)
|
||
|
$id_shop = (int)Context::getContext()->shop->id;
|
||
|
|
||
|
if (!isset($this->routes[$id_shop]))
|
||
|
$this->loadRoutes($id_shop);
|
||
|
|
||
|
if (!isset($this->routes[$id_shop]) || !isset($this->routes[$id_shop][$id_lang]) || !isset($this->routes[$id_shop][$id_lang][$route_id]))
|
||
|
return false;
|
||
|
|
||
|
return preg_match('#\{([^{}]*:)?'.preg_quote($keyword, '#').'(:[^{}]*)?\}#', $this->routes[$id_shop][$id_lang][$route_id]['rule']);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check if a route rule contain all required keywords of default route definition
|
||
|
*
|
||
|
* @param string $route_id
|
||
|
* @param string $rule Rule to verify
|
||
|
* @param array $errors List of missing keywords
|
||
|
*/
|
||
|
public function validateRoute($route_id, $rule, &$errors = array())
|
||
|
{
|
||
|
$errors = array();
|
||
|
if (!isset($this->default_routes[$route_id]))
|
||
|
return false;
|
||
|
|
||
|
foreach ($this->default_routes[$route_id]['keywords'] as $keyword => $data)
|
||
|
if (isset($data['param']) && !preg_match('#\{([^{}]*:)?'.$keyword.'(:[^{}]*)?\}#', $rule))
|
||
|
$errors[] = $keyword;
|
||
|
|
||
|
return (count($errors)) ? false : true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create an url from
|
||
|
*
|
||
|
* @param string $route_id Name the route
|
||
|
* @param int $id_lang
|
||
|
* @param array $params
|
||
|
* @param bool $use_routes If false, don't use to create this url
|
||
|
* @param string $anchor Optional anchor to add at the end of this url
|
||
|
*/
|
||
|
public function createUrl($route_id, $id_lang = null, array $params = array(), $force_routes = false, $anchor = '', $id_shop = null)
|
||
|
{
|
||
|
if ($id_lang === null)
|
||
|
$id_lang = (int)Context::getContext()->language->id;
|
||
|
if ($id_shop === null)
|
||
|
$id_shop = (int)Context::getContext()->shop->id;
|
||
|
|
||
|
if (!isset($this->routes[$id_shop]))
|
||
|
$this->loadRoutes($id_shop);
|
||
|
|
||
|
if (!isset($this->routes[$id_shop][$id_lang][$route_id]))
|
||
|
{
|
||
|
$query = http_build_query($params, '', '&');
|
||
|
$index_link = $this->use_routes ? '' : 'index.php';
|
||
|
return ($route_id == 'index') ? $index_link.(($query) ? '?'.$query : '') : ((trim($route_id) == '') ? '' : 'index.php?controller='.$route_id).(($query) ? '&'.$query : '').$anchor;
|
||
|
}
|
||
|
$route = $this->routes[$id_shop][$id_lang][$route_id];
|
||
|
// Check required fields
|
||
|
$query_params = isset($route['params']) ? $route['params'] : array();
|
||
|
foreach ($route['keywords'] as $key => $data)
|
||
|
{
|
||
|
if (!$data['required'])
|
||
|
continue;
|
||
|
|
||
|
if (!array_key_exists($key, $params))
|
||
|
die('Dispatcher::createUrl() miss required parameter "'.$key.'" for route "'.$route_id.'"');
|
||
|
if (isset($this->default_routes[$route_id]))
|
||
|
$query_params[$this->default_routes[$route_id]['keywords'][$key]['param']] = $params[$key];
|
||
|
}
|
||
|
|
||
|
// Build an url which match a route
|
||
|
if ($this->use_routes || $force_routes)
|
||
|
{
|
||
|
|
||
|
$url = $route['rule'];
|
||
|
$add_param = array();
|
||
|
|
||
|
foreach ($params as $key => $value)
|
||
|
{
|
||
|
if (!isset($route['keywords'][$key]))
|
||
|
{
|
||
|
if (!isset($this->default_routes[$route_id]['keywords'][$key]))
|
||
|
$add_param[$key] = $value;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ($params[$key])
|
||
|
$replace = $route['keywords'][$key]['prepend'].$params[$key].$route['keywords'][$key]['append'];
|
||
|
else
|
||
|
$replace = '';
|
||
|
$url = preg_replace('#\{([^{}]*:)?'.$key.'(:[^{}]*)?\}#', $replace, $url);
|
||
|
}
|
||
|
}
|
||
|
$url = preg_replace('#\{([^{}]*:)?[a-z0-9_]+?(:[^{}]*)?\}#', '', $url);
|
||
|
if (count($add_param))
|
||
|
$url .= '?'.http_build_query($add_param, '', '&');
|
||
|
}
|
||
|
// Build a classic url index.php?controller=foo&...
|
||
|
else
|
||
|
{
|
||
|
$add_params = array();
|
||
|
foreach ($params as $key => $value)
|
||
|
if (!isset($route['keywords'][$key]) && !isset($this->default_routes[$route_id]['keywords'][$key]))
|
||
|
$add_params[$key] = $value;
|
||
|
|
||
|
if (!empty($route['controller']))
|
||
|
$query_params['controller'] = $route['controller'];
|
||
|
$query = http_build_query(array_merge($add_params, $query_params), '', '&');
|
||
|
if ($this->multilang_activated)
|
||
|
$query .= (!empty($query) ? '&' : '').'id_lang='.(int)$id_lang;
|
||
|
$url = 'index.php?'.$query;
|
||
|
|
||
|
}
|
||
|
|
||
|
return $url.$anchor;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Retrieve the controller from url or request uri if routes are activated
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getController($id_shop = null)
|
||
|
{
|
||
|
if (defined('_PS_ADMIN_DIR_'))
|
||
|
$_GET['controllerUri'] = Tools::getvalue('controller');
|
||
|
if ($this->controller)
|
||
|
{
|
||
|
$_GET['controller'] = $this->controller;
|
||
|
return $this->controller;
|
||
|
}
|
||
|
|
||
|
if (isset(Context::getContext()->shop) && $id_shop === null)
|
||
|
$id_shop = (int)Context::getContext()->shop->id;
|
||
|
|
||
|
$controller = Tools::getValue('controller');
|
||
|
|
||
|
if (isset($controller) && is_string($controller) && preg_match('/^([0-9a-z_-]+)\?(.*)=(.*)$/Ui', $controller, $m))
|
||
|
{
|
||
|
$controller = $m[1];
|
||
|
if (isset($_GET['controller']))
|
||
|
$_GET[$m[2]] = $m[3];
|
||
|
else if (isset($_POST['controller']))
|
||
|
$_POST[$m[2]] = $m[3];
|
||
|
}
|
||
|
|
||
|
if (!Validate::isControllerName($controller))
|
||
|
$controller = false;
|
||
|
|
||
|
// Use routes ? (for url rewriting)
|
||
|
if ($this->use_routes && !$controller && !defined('_PS_ADMIN_DIR_'))
|
||
|
{
|
||
|
if (!$this->request_uri)
|
||
|
return strtolower($this->controller_not_found);
|
||
|
$controller = $this->controller_not_found;
|
||
|
|
||
|
// If the request_uri matches a static file, then there is no need to check the routes, we keep "controller_not_found" (a static file should not go through the dispatcher)
|
||
|
if (!preg_match('/\.(gif|jpe?g|png|css|js|ico)$/i', parse_url($this->request_uri, PHP_URL_PATH)))
|
||
|
{
|
||
|
// Add empty route as last route to prevent this greedy regexp to match request uri before right time
|
||
|
if ($this->empty_route)
|
||
|
$this->addRoute($this->empty_route['routeID'], $this->empty_route['rule'], $this->empty_route['controller'], Context::getContext()->language->id, array(), array(), $id_shop);
|
||
|
|
||
|
if (isset($this->routes[$id_shop][Context::getContext()->language->id]))
|
||
|
foreach ($this->routes[$id_shop][Context::getContext()->language->id] as $route)
|
||
|
if (preg_match($route['regexp'], $this->request_uri, $m))
|
||
|
{
|
||
|
// Route found ! Now fill $_GET with parameters of uri
|
||
|
foreach ($m as $k => $v)
|
||
|
if (!is_numeric($k))
|
||
|
$_GET[$k] = $v;
|
||
|
|
||
|
$controller = $route['controller'] ? $route['controller'] : $_GET['controller'];
|
||
|
if (!empty($route['params']))
|
||
|
foreach ($route['params'] as $k => $v)
|
||
|
$_GET[$k] = $v;
|
||
|
|
||
|
// A patch for module friendly urls
|
||
|
if (preg_match('#module-([a-z0-9_-]+)-([a-z0-9_]+)$#i', $controller, $m))
|
||
|
{
|
||
|
$_GET['module'] = $m[1];
|
||
|
$_GET['fc'] = 'module';
|
||
|
$controller = $m[2];
|
||
|
}
|
||
|
|
||
|
if (isset($_GET['fc']) && $_GET['fc'] == 'module')
|
||
|
$this->front_controller = self::FC_MODULE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($controller == 'index' || preg_match('/^\/index.php(?:\?.*)?$/', $this->request_uri))
|
||
|
$controller = $this->useDefaultController();
|
||
|
}
|
||
|
|
||
|
$this->controller = str_replace('-', '', $controller);
|
||
|
$_GET['controller'] = $this->controller;
|
||
|
return $this->controller;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get list of all available FO controllers
|
||
|
*
|
||
|
* @var mixed $dirs
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function getControllers($dirs)
|
||
|
{
|
||
|
if (!is_array($dirs))
|
||
|
$dirs = array($dirs);
|
||
|
|
||
|
$controllers = array();
|
||
|
foreach ($dirs as $dir)
|
||
|
$controllers = array_merge($controllers, Dispatcher::getControllersInDirectory($dir));
|
||
|
return $controllers;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get list of all available Module Front controllers
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function getModuleControllers($type = 'all', $module = null)
|
||
|
{
|
||
|
$modules_controllers = array();
|
||
|
if (is_null($module))
|
||
|
$modules = Module::getModulesOnDisk(true);
|
||
|
else if (!is_array($module))
|
||
|
$modules = array(Module::getInstanceByName($module));
|
||
|
else
|
||
|
{
|
||
|
$modules = array();
|
||
|
foreach ($module as $_mod)
|
||
|
$modules[] = Module::getInstanceByName($_mod);
|
||
|
}
|
||
|
|
||
|
foreach ($modules as $mod)
|
||
|
{
|
||
|
foreach (Dispatcher::getControllersInDirectory(_PS_MODULE_DIR_.$mod->name.'/controllers/') as $controller)
|
||
|
{
|
||
|
if ($type == 'admin')
|
||
|
{
|
||
|
if (strpos($controller, 'Admin') !== false)
|
||
|
$modules_controllers[$mod->name][] = $controller;
|
||
|
}
|
||
|
else if ($type == 'front')
|
||
|
{
|
||
|
if (strpos($controller, 'Admin') === false)
|
||
|
$modules_controllers[$mod->name][] = $controller;
|
||
|
}
|
||
|
else
|
||
|
$modules_controllers[$mod->name][] = $controller;
|
||
|
}
|
||
|
}
|
||
|
return $modules_controllers;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get list of available controllers from the specified dir
|
||
|
*
|
||
|
* @param string dir directory to scan (recursively)
|
||
|
* @return array
|
||
|
*/
|
||
|
public static function getControllersInDirectory($dir)
|
||
|
{
|
||
|
if (!is_dir($dir))
|
||
|
return array();
|
||
|
|
||
|
$controllers = array();
|
||
|
$controller_files = scandir($dir);
|
||
|
foreach ($controller_files as $controller_filename)
|
||
|
{
|
||
|
if ($controller_filename[0] != '.')
|
||
|
{
|
||
|
if (!strpos($controller_filename, '.php') && is_dir($dir.$controller_filename))
|
||
|
$controllers += Dispatcher::getControllersInDirectory($dir.$controller_filename.DIRECTORY_SEPARATOR);
|
||
|
elseif ($controller_filename != 'index.php')
|
||
|
{
|
||
|
$key = str_replace(array('controller.php', '.php'), '', strtolower($controller_filename));
|
||
|
$controllers[$key] = basename($controller_filename, '.php');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $controllers;
|
||
|
}
|
||
|
}
|