Cleaned up route handler, partially incomplete
authorAlexander Ebert <ebert@woltlab.com>
Wed, 4 Nov 2015 12:51:41 +0000 (13:51 +0100)
committerAlexander Ebert <ebert@woltlab.com>
Wed, 4 Nov 2015 12:51:41 +0000 (13:51 +0100)
com.woltlab.wcf/option.xml
wcfsetup/install/files/js/WoltLab/WCF/Dom/Util.js
wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php [deleted file]
wcfsetup/install/files/lib/system/request/ControllerMap.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/request/FlexibleRoute.class.php
wcfsetup/install/files/lib/system/request/LinkHandler.class.php
wcfsetup/install/files/lib/system/request/RequestHandler.class.php

index fa5ea7b49c888323330185fca8210e610c2ac3ee..fea224943afe94d9e0bb070b59dce4dcf4f3b402 100644 (file)
                        </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>
index 5086b7d64a5251906315dc38f794f3dce678ec18..555fbebb14c77c7986cfff0f19a4463bfdea24dc 100644 (file)
@@ -95,7 +95,7 @@ define([], function() {
                 * 
                 * @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);
diff --git a/wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php
deleted file mode 100644 (file)
index 9234434..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-<?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;
-       }
-}
diff --git a/wcfsetup/install/files/lib/system/request/ControllerMap.class.php b/wcfsetup/install/files/lib/system/request/ControllerMap.class.php
new file mode 100644 (file)
index 0000000..408fcf5
--- /dev/null
@@ -0,0 +1,120 @@
+<?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
+               ];
+       }
+}
index dc636992591ff46919c6b49cc5667209a5dcadbc..301c0bbadec54e14cc5bde9b701e823578d6bf88 100644 (file)
@@ -23,12 +23,6 @@ class FlexibleRoute implements IRoute {
         */
        protected $buildSchema = array();
        
-       /**
-        * cached list of transformed controller names
-        * @var array<string>
-        */
-       protected $controllerNames = array();
-       
        /**
         * route is restricted to ACP
         * @var boolean
@@ -311,13 +305,6 @@ class FlexibleRoute implements IRoute {
         * @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);
        }
 }
index 353f7b7999316faf0318285908cdc99e8e28cbe2..9c51d97d91410f9a7b529078688e73763bc941f1 100644 (file)
@@ -155,10 +155,7 @@ class LinkHandler extends SingletonFactory {
                        
                        // 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']);
index 1e00a3b2d517d48657186f541b57c0a7c59f1072..2d423e7e923a2bedf5d686e86f757d7bfe608f7c 100644 (file)
@@ -1,7 +1,6 @@
 <?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;
@@ -29,16 +28,9 @@ class RequestHandler extends SingletonFactory {
        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
@@ -56,6 +48,8 @@ class RequestHandler extends SingletonFactory {
         * @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']) {
@@ -79,20 +73,6 @@ class RequestHandler extends SingletonFactory {
                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];
-                               }
-                       }
-               }
        }
        
        /**
@@ -159,14 +139,6 @@ class RequestHandler extends SingletonFactory {
                                                // 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'];
@@ -178,7 +150,8 @@ class RequestHandler extends SingletonFactory {
                                }
                                
                                // 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
@@ -199,40 +172,18 @@ class RequestHandler extends SingletonFactory {
                                                $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]);
@@ -242,6 +193,7 @@ class RequestHandler extends SingletonFactory {
                                        }
                                }
                        }
+                       */
                        
                        $this->activeRequest = new Request($classData['className'], $classData['controller'], $classData['pageType']);
                }
@@ -302,13 +254,7 @@ class RequestHandler extends SingletonFactory {
                // 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;
                }
@@ -322,71 +268,7 @@ class RequestHandler extends SingletonFactory {
                // 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']);
        }
        
        /**
@@ -417,44 +299,11 @@ class RequestHandler extends SingletonFactory {
        }
        
        /**
-        * 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;
        }
 }