Fixed some issues with URLs, added case-insensitive controller cache
authorAlexander Ebert <ebert@woltlab.com>
Sat, 9 Aug 2014 09:38:53 +0000 (11:38 +0200)
committerAlexander Ebert <ebert@woltlab.com>
Sat, 9 Aug 2014 09:38:53 +0000 (11:38 +0200)
wcfsetup/install/files/lib/system/WCFACP.class.php
wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php [new file with mode: 0644]
wcfsetup/install/files/lib/system/request/RequestHandler.class.php
wcfsetup/install/files/lib/system/request/Route.class.php
wcfsetup/install/files/lib/system/request/RouteHandler.class.php

index a05d127b8842d6094e1494a18b5d041d0baae8b5..22733613e93f6e2e9e6f0364d0e06f9a8a282576 100644 (file)
@@ -85,7 +85,10 @@ class WCFACP extends WCF {
                                        $pageURL = $application->getPageURL();
                                }
                                
-                               $path = $pageURL . 'acp/?Login/' . SID_ARG_2ND . '&url=' . rawurlencode(RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . WCF::getSession()->requestURI);
+                               // drop session id
+                               $redirectURI = preg_replace('~[&\?]s=[a-f0-9]{40}(&|$)~', '', WCF::getSession()->requestURI);
+                               
+                               $path = $pageURL . 'acp/?Login/' . SID_ARG_2ND_NOT_ENCODED . '&url=' . rawurlencode(RouteHandler::getProtocol() . $_SERVER['HTTP_HOST'] . $redirectURI);
                                
                                HeaderUtil::redirect($path);
                                exit;
diff --git a/wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php b/wcfsetup/install/files/lib/system/cache/builder/ControllerCacheBuilder.class.php
new file mode 100644 (file)
index 0000000..03ee393
--- /dev/null
@@ -0,0 +1,70 @@
+<?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-2014 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 . '/';
+               
+               foreach(glob($path . '*' . ucfirst($type) . '.class.php') as $file) {
+                       $file = basename($file);
+                       if (preg_match('~^([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)(Action|Form|Page)\.class\.php$~', $file, $match)) {
+                               if ($match[1] === 'I') {
+                                       continue;
+                               }
+                               
+                               $controller = mb_strtolower($match[1]);
+                               $fqn = '\\' . $abbreviation . '\\' . ($isACP ? 'acp\\' : '') . $type . '\\' . $match[1] . $match[2];
+                               
+                               $controllers[$controller] = $fqn;
+                       }
+               }
+               
+               return $controllers;
+       }
+}
index 87679b3ad757ac9d145efe4ad6b2f2fe1606b005..5be79cf224d7cbe1978383e0a8c46ea705c09dd6 100644 (file)
@@ -1,6 +1,7 @@
 <?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;
@@ -27,6 +28,12 @@ class RequestHandler extends SingletonFactory {
         */
        protected $activeRequest = null;
        
+       /**
+        * list of known controllers grouped by application and type
+        * @var array<array>
+        */
+       protected $controllers = null;
+       
        /**
         * true, if current domain mismatch any known domain
         * @var boolean
@@ -62,6 +69,16 @@ class RequestHandler extends SingletonFactory {
                        // when using cli, no rescue mode is provided
                        $this->inRescueMode = false;
                }
+               
+               if (class_exists('wcf\system\WCFACP', false)) {
+                       $this->isACPRequest = true;
+               }
+               
+               if (PACKAGE_ID) {
+                       $this->controllers = ControllerCacheBuilder::getInstance()->getData(array(
+                               'environment' => ($this->isACPRequest ? 'admin' : 'user')
+                       ));
+               }
        }
        
        /**
@@ -223,10 +240,23 @@ class RequestHandler extends SingletonFactory {
         * @return      array
         */
        protected function getClassData($controller, $pageType, $application) {
-               $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);
+               $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;
                }
@@ -244,6 +274,25 @@ class RequestHandler extends SingletonFactory {
                );
        }
        
+       /**
+        * 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 (isset($this->controllers[$application][$pageType][$ciController])) {
+                               return $this->controllers[$application][$pageType][$ciController];
+                       }
+               }
+               
+               return false;
+       }
+       
        /**
         * Returns the active request object.
         * 
index 85e69e5f92d38883105bb519a38d9fc7e0211b6b..4559b1cc7a7927f4636a96ff36c0c9b03c3fe1ac 100644 (file)
@@ -314,6 +314,8 @@ class Route {
                
                if (!empty($components)) {
                        if (strpos($link, '?') === false) $link .= '?';
+                       else $link .= '&';
+                       
                        $link .= http_build_query($components, '', '&');
                }
                
index 14b71041fa7406eff80e3f017cc59b20da78aac5..47df34ce828bddc200863e450418ed9d1c04d041 100644 (file)
@@ -264,7 +264,7 @@ class RouteHandler extends SingletonFactory {
                        self::$pathInfo = '';
                        
                        // WCF 2.0: index.php/Foo/Bar/
-                       if (URL_LEGACY_MODE) {
+                       if (URL_LEGACY_MODE && !RequestHandler::getInstance()->isACPRequest()) {
                                if (isset($_SERVER['PATH_INFO'])) {
                                        self::$pathInfo = $_SERVER['PATH_INFO'];
                                }