</option>
<option name="url_legacy_mode">
- <categoryname>general.page.seo</categoryname>
+ <categoryname>hidden</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
- <enableoptions>!url_controller_replacement</enableoptions>
</option>
<option name="url_omit_index_php">
<categoryname>general.page.seo</categoryname>
<optiontype>boolean</optiontype>
<defaultvalue>0</defaultvalue>
</option>
- <option name="url_controller_replacement">
- <categoryname>general.page.seo</categoryname>
- <optiontype>urlControllerReplacement</optiontype>
- </option>
<option name="url_title_component_replacement">
<categoryname>general.page.seo</categoryname>
<optiontype>textarea</optiontype>
*
* @param {Element} el element
* @param {CSSStyleDeclaration=} styles result of window.getComputedStyle()
- * @return {integer} outer height in px
+ * @return {int} outer height in px
*/
outerHeight: function(el, styles) {
styles = styles || window.getComputedStyle(el);
+++ /dev/null
-<?php
-namespace wcf\system\cache\builder;
-use wcf\data\package\Package;
-use wcf\data\package\PackageList;
-
-/**
- * Caches available controllers for case-insensitive lookup.
- *
- * @author Alexander Ebert
- * @copyright 2001-2015 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
- * @package com.woltlab.wcf
- * @subpackage system.cache.builder
- * @category Community Framework
- */
-class ControllerCacheBuilder extends AbstractCacheBuilder {
- /**
- * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
- */
- public function rebuild(array $parameters) {
- $data = array();
- $isACP = ($parameters['environment'] == 'admin');
-
- $packageList = new PackageList();
- $packageList->getConditionBuilder()->add("isApplication = ?", array(1));
- $packageList->readObjects();
- foreach ($packageList as $package) {
- $abbreviation = Package::getAbbreviation($package->package);
- $path = WCF_DIR . $package->packageDir . 'lib/' . ($isACP ? 'acp/' : '');
-
- $data[$abbreviation] = array(
- 'action' => $this->getControllers($path, $abbreviation, 'action', $isACP),
- 'form' => $this->getControllers($path, $abbreviation, 'form', $isACP),
- 'page' => $this->getControllers($path, $abbreviation, 'page', $isACP)
- );
- }
-
- return $data;
- }
-
- /**
- * Returns a list of case-insensitive controllers with their fully-qualified namespace grouped by type.
- *
- * @param string $path
- * @param string $abbreviation
- * @param string $type
- * @param boolean $isACP
- * @return array<string>
- */
- protected function getControllers($path, $abbreviation, $type, $isACP) {
- $controllers = array();
- $path .= $type . '/';
-
- $lowercaseType = $type;
- $type = ucfirst($type);
- $files = glob($path . '*' . $type . '.class.php');
- if ($files === false) {
- return array();
- }
-
- foreach ($files as $file) {
- $file = basename($file);
- if (preg_match('~^([A-Z][A-Za-z0-9]*)' . $type . '\.class\.php$~', $file, $match)) {
- if ($match[1] === 'I') {
- continue;
- }
-
- $controller = mb_strtolower($match[1]);
- $fqn = '\\' . $abbreviation . '\\' . ($isACP ? 'acp\\' : '') . $lowercaseType . '\\' . $match[1] . $type;
-
- $controllers[$controller] = $fqn;
- }
- }
-
- return $controllers;
- }
-}
--- /dev/null
+<?php
+namespace wcf\system\request;
+use wcf\system\exception\SystemException;
+
+/**
+ * Resolves incoming requests and performs lookups for controller to url mappings.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.request
+ * @category Community Framework
+ */
+class ControllerMap {
+ /**
+ * list of <ControllerName> to <controller-name> mappings
+ * @var array<string>
+ */
+ protected $lookupCache = [];
+
+ public function __construct() {
+ // TODO: initialize custom controller mappings
+ }
+
+ /**
+ * Resolves class data for given controller.
+ *
+ * @param string $application application identifier
+ * @param string $controller url controller
+ * @param boolean $isAcpRequest true if this is an ACP request
+ * @return array<string> className, controller and pageType
+ */
+ public function resolve($application, $controller, $isAcpRequest) {
+ // validate controller
+ if (!preg_match('~^[a-z][a-z0-9]+(?:\-[a-z][a-z0-9]+)*$~', $controller)) {
+ throw new SystemException("Malformed controller name '" . $controller . "'");
+ }
+
+ $parts = explode('-', $controller);
+ $parts = array_map('ucfirst', $parts);
+ $controller = implode('', $parts);
+
+ if ($controller === 'AjaxProxy') $controller = 'AJAXProxy';
+
+ $classData = $this->getClassData($application, $controller, $isAcpRequest, 'page');
+ if ($classData === null) $classData = $this->getClassData($application, $controller, $isAcpRequest, 'form');
+ if ($classData === null) $classData = $this->getClassData($application, $controller, $isAcpRequest, 'action');
+
+ if ($classData === null) {
+ // TODO: check custom controller mappings
+
+ throw new SystemException("Unknown controller '" . $controller . "'");
+ }
+ else {
+ // TODO: check if controller was aliased and force a redirect
+ }
+
+ return $classData;
+ }
+
+ /**
+ * Transforms given controller into its url representation.
+ *
+ * @param string $controller controller class, e.g. 'MembersList'
+ * @return string url representation of controller, e.g. 'members-list'
+ */
+ public function lookup($controller) {
+ if (isset($this->lookupCache[$controller])) {
+ return $this->lookupCache[$controller];
+ }
+
+ $parts = preg_split('~([A-Z][a-z0-9]+)~', $controller, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ $parts = array_map('strtolower', $parts);
+
+ $urlController = implode('-', $parts);
+
+ // TODO: lookup custom controller mappings
+
+ $this->lookupCache[$controller] = $urlController;
+
+ return $urlController;
+ }
+
+ /**
+ * Returns the class data for the active request or null if for the given
+ * configuration no proper class exist.
+ *
+ * @param string $application application identifier
+ * @param string $controller controller name
+ * @param boolean $isAcpRequest true if this is an ACP request
+ * @param string $pageType page type, e.g. 'form' or 'action'
+ * @return array<string> className, controller and pageType
+ */
+ protected function getClassData($application, $controller, $isAcpRequest, $pageType) {
+ $className = $application . '\\' . ($isAcpRequest ? 'acp\\' : '') . $pageType . '\\' . $controller . ucfirst($pageType);
+ if (!class_exists($className)) {
+ if ($application === 'wcf') {
+ return null;
+ }
+
+ $className = 'wcf\\' . ($isAcpRequest ? 'acp\\' : '') . $pageType . '\\' . $controller . ucfirst($pageType);
+ if (!class_exists($className)) {
+ return null;
+ }
+ }
+
+ // check for abstract classes
+ $reflectionClass = new \ReflectionClass($className);
+ if ($reflectionClass->isAbstract()) {
+ return null;
+ }
+
+ return [
+ 'className' => $className,
+ 'controller' => $controller,
+ 'pageType' => $pageType
+ ];
+ }
+}
*/
protected $buildSchema = array();
- /**
- * cached list of transformed controller names
- * @var array<string>
- */
- protected $controllerNames = array();
-
/**
* route is restricted to ACP
* @var boolean
* @return string
*/
protected function getControllerName($application, $controller) {
- if (!isset($this->controllerNames[$controller])) {
- $controllerName = RequestHandler::getTokenizedController($controller);
- $alias = (!$this->isACP) ? RequestHandler::getInstance()->getAliasByController($controllerName) : null;
-
- $this->controllerNames[$controller] = ($alias) ?: $controllerName;
- }
-
- return $this->controllerNames[$controller];
+ return RequestHandler::getInstance()->getControllerMap()->lookup($controller);
}
}
// trim to 80 characters
$parameters['title'] = rtrim(mb_substr($parameters['title'], 0, 80), '-');
-
- if (!URL_LEGACY_MODE) {
- $parameters['title'] = mb_strtolower($parameters['title']);
- }
+ $parameters['title'] = mb_strtolower($parameters['title']);
// encode title
if ($encodeTitle) $parameters['title'] = rawurlencode($parameters['title']);
<?php
namespace wcf\system\request;
use wcf\system\application\ApplicationHandler;
-use wcf\system\cache\builder\ControllerCacheBuilder;
use wcf\system\exception\AJAXException;
use wcf\system\exception\IllegalLinkException;
use wcf\system\exception\SystemException;
protected $activeRequest = null;
/**
- * list of known controllers grouped by application and type
- * @var array<array>
+ * @var \wcf\system\request\ControllerMap
*/
- protected $controllers = null;
-
- /**
- * list of controller aliases
- * @var array<string>
- */
- protected $controllerAliases = array();
+ protected $controllerMap = null;
/**
* true, if current domain mismatch any known domain
* @see \wcf\system\SingletonFactory::init()
*/
protected function init() {
+ $this->controllerMap = new ControllerMap();
+
if (isset($_SERVER['HTTP_HOST'])) {
foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
if ($application->domainName == $_SERVER['HTTP_HOST']) {
if (class_exists('wcf\system\WCFACP', false)) {
$this->isACPRequest = true;
}
-
- if (PACKAGE_ID) {
- $this->controllers = ControllerCacheBuilder::getInstance()->getData(array(
- 'environment' => ($this->isACPRequest ? 'admin' : 'user')
- ));
-
- if (!URL_LEGACY_MODE && URL_CONTROLLER_REPLACEMENT) {
- $controllerAliases = explode("\n", URL_CONTROLLER_REPLACEMENT);
- for ($i = 0, $length = count($controllerAliases); $i < $length; $i++) {
- $tmp = explode('=', $controllerAliases[$i]);
- $this->controllerAliases[$tmp[0]] = $tmp[1];
- }
- }
- }
}
/**
// build URL, e.g. http://example.net/forum/
$url = FileUtil::addTrailingSlash(RouteHandler::getProtocol() . $applicationObject->domainName . RouteHandler::getPath());
- if (URL_LEGACY_MODE) {
- // add path info, e.g. index.php/Board/2/
- $pathInfo = RouteHandler::getPathInfo();
- if (!empty($pathInfo)) {
- $url .= 'index.php' . $pathInfo;
- }
- }
-
// query string, e.g. ?foo=bar
if (!empty($_SERVER['QUERY_STRING'])) {
$url .= '?' . $_SERVER['QUERY_STRING'];
}
// handle controller aliasing
- if (empty($routeData['isImplicitController']) && !URL_LEGACY_MODE && isset($routeData['controller'])) {
+ /*
+ if (empty($routeData['isImplicitController']) && isset($routeData['controller'])) {
$ciController = mb_strtolower($routeData['controller']);
// aliased controller, redirect to new URL
$routeData['controller'] = $controller;
}
}
+ */
}
else if (empty($routeData['controller'])) {
- $routeData['controller'] = 'Index';
+ $routeData['controller'] = 'index';
}
$controller = $routeData['controller'];
- // validate class name
- if (!preg_match('~^[a-z0-9-]+$~i', $controller)) {
- throw new SystemException("Illegal class name '".$controller."'");
- }
-
- // work-around for WCFSetup
- if (!PACKAGE_ID) {
- $parts = explode('-', $controller);
- $parts = array_map(function($part) {
- return ucfirst($part);
- }, $parts);
- $controller = implode('', $parts);
- }
-
- // find class
- $classData = $this->getClassData($controller, 'page', $application);
- if ($classData === null) $classData = $this->getClassData($controller, 'form', $application);
- if ($classData === null) $classData = $this->getClassData($controller, 'action', $application);
-
- if ($classData === null) {
- throw new SystemException("unable to find class for controller '".$controller."'");
- }
- else if (!class_exists($classData['className'])) {
- throw new SystemException("unable to find class '".$classData['className']."'");
- }
+ $classData = $this->controllerMap->resolve($application, $controller, $this->isACPRequest());
// check if controller was provided exactly as it should
+ /*
if (!URL_LEGACY_MODE && !$this->isACPRequest()) {
if (preg_match('~([A-Za-z0-9]+)(?:Action|Form|Page)$~', $classData['className'], $matches)) {
$realController = self::getTokenizedController($matches[1]);
}
}
}
+ */
$this->activeRequest = new Request($classData['className'], $classData['controller'], $classData['pageType']);
}
// check if currently invoked application matches the landing page
if ($landingPageApplication == $application) {
$routeData['controller'] = $landingPage->getController();
- if (!URL_LEGACY_MODE) $routeData['controller'] = self::getTokenizedController($routeData['controller']);
-
- // use alias if defined to prevent incorrect recognition
- $alias = $this->getAliasByController($routeData['controller']);
- if ($alias !== null && empty($routeData['isImplicitController'])) {
- $routeData['controller'] = $alias;
- }
+ $routeData['controller'] = $this->getControllerMap()->lookup($routeData['controller']);
return;
}
// set default controller
$applicationObj = WCF::getApplicationObject(ApplicationHandler::getInstance()->getApplication($application));
$routeData['controller'] = preg_replace('~^.*?\\\([^\\\]+)(?:Action|Form|Page)$~', '\\1', $applicationObj->getPrimaryController());
- if (!URL_LEGACY_MODE) $routeData['controller'] = self::getTokenizedController($routeData['controller']);
- }
-
- /**
- * Returns the class data for the active request or null if for the given
- * configuration no proper class exist.
- *
- * @param string $controller
- * @param string $pageType
- * @param string $application
- * @return array
- */
- protected function getClassData($controller, $pageType, $application) {
- $className = false;
- if ($this->controllers !== null) {
- $className = $this->lookupController($controller, $pageType, $application);
- if ($className === false && $application != 'wcf') {
- $className = $this->lookupController($controller, $pageType, 'wcf');
- }
- }
-
- // controller is either unknown or within WCFSetup
- if ($className === false) {
- $className = $application.'\\'.($this->isACPRequest() ? 'acp\\' : '').$pageType.'\\'.ucfirst($controller).ucfirst($pageType);
- if ($application != 'wcf' && !class_exists($className)) {
- $className = 'wcf\\'.($this->isACPRequest() ? 'acp\\' : '').$pageType.'\\'.ucfirst($controller).ucfirst($pageType);
- }
- }
-
- if (!class_exists($className)) {
- return null;
- }
-
- // check whether the class is abstract
- $reflectionClass = new \ReflectionClass($className);
- if ($reflectionClass->isAbstract()) {
- return null;
- }
-
- return array(
- 'className' => $className,
- 'controller' => $controller,
- 'pageType' => $pageType
- );
- }
-
- /**
- * Lookups a controller from the list of known controllers using a case-insensitive search.
- *
- * @param string $controller
- * @param string $pageType
- * @param string $application
- * @return boolean
- */
- protected function lookupController($controller, $pageType, $application) {
- if (isset($this->controllers[$application]) && isset($this->controllers[$application][$pageType])) {
- $ciController = mb_strtolower($controller);
- if (!URL_LEGACY_MODE || $this->isACPRequest) $ciController = str_replace('-', '', $ciController);
-
- if (isset($this->controllers[$application][$pageType][$ciController])) {
- return $this->controllers[$application][$pageType][$ciController];
- }
- }
-
- return false;
+ $routeData['controller'] = $this->getControllerMap()->lookup($routeData['controller']);
}
/**
}
/**
- * Returns the alias by controller or null if there is no match.
+ * Returns the controller map instance.
*
- * @param string $controller
- * @return string
+ * @return \wcf\system\request\ControllerMap
*/
- public function getAliasByController($controller) {
- if (isset($this->controllerAliases[$controller])) {
- return $this->controllerAliases[$controller];
- }
-
- return null;
- }
-
- /**
- * Returns the controller by alias or null if there is no match.
- *
- * @param string $alias
- * @return string
- */
- public function getControllerByAlias($alias) {
- $controller = array_search($alias, $this->controllerAliases);
- if ($controller !== false) {
- return $controller;
- }
-
- return null;
- }
-
- /**
- * Returns the tokenized controller name, e.g. BoardList -> board-list.
- *
- * @param string controller
- * @return string
- */
- public static function getTokenizedController($controller) {
- $parts = preg_split('~([A-Z][a-z0-9]+)~', $controller, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
- $parts = array_map('strtolower', $parts);
-
- return implode('-', $parts);
+ public function getControllerMap() {
+ return $this->controllerMap;
}
}