diff --git a/_config.php b/_config.php index 3c8c49a66..748106b2b 100644 --- a/_config.php +++ b/_config.php @@ -17,22 +17,6 @@ * @subpackage core */ -// Default director -Director::addRules(10, array( - 'Security//$Action/$ID/$OtherID' => 'Security', - //'Security/$Action/$ID' => 'Security', - '$Controller//$Action/$ID/$OtherID' => '*', - 'api/v1/live' => 'VersionedRestfulServer', - 'api/v1' => 'RestfulServer', - 'soap/v1' => 'SOAPModelAccess', - 'dev' => 'DevelopmentAdmin', - 'interactive' => 'SapphireREPL', -)); -// Add default routing unless 'cms' module is present (which overrules this with a CMSMain controller) -Director::addRules(20, array( - 'admin//$action/$ID/$OtherID' => '->admin/security' -)); - /** * PHP 5.2 introduced a conflict with the Datetime field type, which was renamed to SSDatetime. This was later renamed * to SS_Datetime to be consistent with other namespaced classes. diff --git a/_config/RequestProcessor.yml b/_config/RequestProcessor.yml deleted file mode 100644 index 9309f0265..000000000 --- a/_config/RequestProcessor.yml +++ /dev/null @@ -1,6 +0,0 @@ -name: RequestProcessor ---- -# Providing an empty config so it can be overridden at a later point -Injector: - RequestProcessor: - 0: \ No newline at end of file diff --git a/_config/PasswordEncryptor.yml b/_config/encryptors.yml similarity index 93% rename from _config/PasswordEncryptor.yml rename to _config/encryptors.yml index 6ffe30dad..8bb563a70 100644 --- a/_config/PasswordEncryptor.yml +++ b/_config/encryptors.yml @@ -1,4 +1,5 @@ -name: PasswordEncryptor +--- +name: coreencryptors --- PasswordEncryptor: encryptors: diff --git a/_config/HTTP.yml b/_config/mimetypes.yml similarity index 99% rename from _config/HTTP.yml rename to _config/mimetypes.yml index fc2529d22..f5e6c5fe0 100644 --- a/_config/HTTP.yml +++ b/_config/mimetypes.yml @@ -1,4 +1,5 @@ -name: HTTP +--- +name: basicmimetypes --- HTTP: MimeTypes: diff --git a/_config/routes.yml b/_config/routes.yml new file mode 100644 index 000000000..e31876da8 --- /dev/null +++ b/_config/routes.yml @@ -0,0 +1,23 @@ +--- +Name: coreroutes +After: cms/routes#modelascontrollerroutes +Before: '*' +--- +Director: + rules: + 'Security//$Action/$ID/$OtherID': 'Security' + '$Controller//$Action/$ID/$OtherID': '*' + 'api/v1/live': 'VersionedRestfulServer' + 'api/v1': 'RestfulServer' + 'soap/v1': 'SOAPModelAccess' + 'dev': 'DevelopmentAdmin' + 'interactive': 'SapphireREPL' +--- +Name: adminroutes +After: framework/routes#coreroutes +--- +Director: + rules: + 'admin': 'AdminRootController' + '': 'RootURLController' + 'dev/buildcache/$Action': 'RebuildStaticCacheTask' diff --git a/admin/_config.php b/admin/_config.php index 84f3f01b4..cf8c14086 100644 --- a/admin/_config.php +++ b/admin/_config.php @@ -1,14 +1,4 @@ 'RootURLController', - 'processes//$Action/$ID/$Batch' => 'BatchProcess_Controller', - 'admin/help//$Action/$ID' => 'CMSHelp', - 'admin/bulkload//$Action/$ID/$OtherID' => 'BulkLoaderAdmin', - 'dev/buildcache/$Action' => 'RebuildStaticCacheTask', -)); - -CMSMenu::add_director_rules(); - // Default CMS HTMLEditorConfig HtmlEditorConfig::get('cms')->setOptions(array( 'friendly_name' => 'Default CMS', diff --git a/admin/code/AdminRootController.php b/admin/code/AdminRootController.php new file mode 100644 index 000000000..cffe01437 --- /dev/null +++ b/admin/code/AdminRootController.php @@ -0,0 +1,94 @@ + controller k/v pairs, the same as Director::rules. However this is built + * dynamically from introspecting on all the classes that derive from LeftAndMain. + * + * Don't access this directly - always access via the rules() accessor below, which will build this array + * the first time it's accessed + */ + private static $_rules = null; + + /** + * Gets a list of url_pattern => controller k/v pairs for each LeftAndMain derived controller + */ + public static function rules() { + if (self::$_rules === null) { + self::$_rules = array(); + + // Build an array of class => url_priority k/v pairs + $classes = array(); + foreach (CMSMenu::get_cms_classes() as $class) { + $classes[$class] = Config::inst()->get($class, 'url_priority', Config::FIRST_SET); + } + + // Sort them so highest priority item is first + arsort($classes, SORT_NUMERIC); + + // Map over the array calling add_rule_for_controller on each + array_map(array(__CLASS__, 'add_rule_for_controller'), array_keys($classes)); + } + + return self::$_rules; + } + + /** + * Add the appropriate k/v pair to self::$rules for the given controller. + */ + protected static function add_rule_for_controller($controllerClass) { + $urlSegment = Config::inst()->get($controllerClass, 'url_segment', Config::FIRST_SET); + $urlRule = Config::inst()->get($controllerClass, 'url_rule', Config::FIRST_SET); + + if($urlSegment || $controllerClass == 'CMSMain') { + // Make director rule + if($urlRule[0] == '/') $urlRule = substr($urlRule,1); + $rule = $urlSegment . '//' . $urlRule; + + self::$_rules[$rule] = $controllerClass; + } + } + + function handleRequest(SS_HTTPRequest $request, DataModel $model) { + // If this is the final portion of the request (i.e. the URL is just /admin), direct to the default panel + if ($request->allParsed()) { + $base = $this->config()->url_base; + $segment = Config::inst()->get($this->config()->default_panel, 'url_segment'); + + $this->response = new SS_HTTPResponse(); + $this->redirect(Controller::join_links($base, $segment)); + return $this->response; + } + + // Otherwise + else { + $rules = self::rules(); + + foreach($rules as $pattern => $controller) { + if(($arguments = $request->match($pattern, true)) !== false) { + $controllerObj = Injector::inst()->create($controller); + $controllerObj->setSession($this->session); + + return $controllerObj->handleRequest($request, $model); + } + } + } + } +} \ No newline at end of file diff --git a/admin/code/CMSMenu.php b/admin/code/CMSMenu.php index 1e133e2ca..324179c0a 100644 --- a/admin/code/CMSMenu.php +++ b/admin/code/CMSMenu.php @@ -34,13 +34,6 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider self::$menu_is_cleared = false; } - /** - * Add Director rules for all of the CMS controllers. - */ - public static function add_director_rules() { - array_map(array('self','add_director_rule_for_controller'), self::get_cms_classes()); - } - /** * Add a LeftAndMain controller to the CMS menu. * @@ -76,27 +69,7 @@ class CMSMenu extends Object implements IteratorAggregate, i18nEntityProvider return new CMSMenuItem($menuTitle, $link, $controllerClass, $menuPriority); } - - /** - * Add the appropriate Director rules for the given controller. - */ - protected static function add_director_rule_for_controller($controllerClass) { - $urlBase = Config::inst()->get($controllerClass, 'url_base', Config::FIRST_SET); - $urlSegment = Config::inst()->get($controllerClass, 'url_segment', Config::FIRST_SET); - $urlRule = Config::inst()->get($controllerClass, 'url_rule', Config::FIRST_SET); - $urlPriority = Config::inst()->get($controllerClass, 'url_priority', Config::FIRST_SET); - if($urlSegment || $controllerClass == 'CMSMain') { - $link = Controller::join_links($urlBase, $urlSegment) . '/'; - - // Make director rule - if($urlRule[0] == '/') $urlRule = substr($urlRule,1); - $rule = $link . '/' . $urlRule; // the / will combine with the / on the end of $link to make a // - Director::addRules($urlPriority, array( - $rule => $controllerClass - )); - } - } /** * Add an arbitrary URL to the CMS menu. diff --git a/control/Director.php b/control/Director.php index 335cffd77..29c8443d9 100644 --- a/control/Director.php +++ b/control/Director.php @@ -41,7 +41,11 @@ class Director implements TemplateGlobalProvider { * We recommend priority 100 for your site's rules. The built-in rules are priority 10, standard modules are priority 50. */ static function addRules($priority, $rules) { - Director::$rules[$priority] = isset(Director::$rules[$priority]) ? array_merge($rules, (array)Director::$rules[$priority]) : $rules; + if ($priority != 100) { + Deprecation::notice('3.0', 'Priority argument is now ignored - use the default of 100. You should really be setting routes via _config yaml fragments though.'); + } + + Config::inst()->update('Director', 'rules', $rules); } /** @@ -242,48 +246,46 @@ class Director implements TemplateGlobalProvider { * @return SS_HTTPResponse|string */ protected static function handleRequest(SS_HTTPRequest $request, Session $session, DataModel $model) { - krsort(Director::$rules); + $rules = Config::inst()->get('Director', 'rules'); - if(isset($_REQUEST['debug'])) Debug::show(Director::$rules); - foreach(Director::$rules as $priority => $rules) { - foreach($rules as $pattern => $controllerOptions) { - if(is_string($controllerOptions)) { - if(substr($controllerOptions,0,2) == '->') $controllerOptions = array('Redirect' => substr($controllerOptions,2)); - else $controllerOptions = array('Controller' => $controllerOptions); + if(isset($_REQUEST['debug'])) Debug::show($rules); + + foreach($rules as $pattern => $controllerOptions) { + if(is_string($controllerOptions)) { + if(substr($controllerOptions,0,2) == '->') $controllerOptions = array('Redirect' => substr($controllerOptions,2)); + else $controllerOptions = array('Controller' => $controllerOptions); + } + + if(($arguments = $request->match($pattern, true)) !== false) { + // controllerOptions provide some default arguments + $arguments = array_merge($controllerOptions, $arguments); + + // Find the controller name + if(isset($arguments['Controller'])) $controller = $arguments['Controller']; + + // Pop additional tokens from the tokeniser if necessary + if(isset($controllerOptions['_PopTokeniser'])) { + $request->shift($controllerOptions['_PopTokeniser']); } - - if(($arguments = $request->match($pattern, true)) !== false) { - // controllerOptions provide some default arguments - $arguments = array_merge($controllerOptions, $arguments); - - // Find the controller name - if(isset($arguments['Controller'])) $controller = $arguments['Controller']; - - // Pop additional tokens from the tokeniser if necessary - if(isset($controllerOptions['_PopTokeniser'])) { - $request->shift($controllerOptions['_PopTokeniser']); + + // Handle redirections + if(isset($arguments['Redirect'])) { + return "redirect:" . Director::absoluteURL($arguments['Redirect'], true); + + } else { + Director::$urlParams = $arguments; + $controllerObj = Injector::inst()->create($controller); + $controllerObj->setSession($session); + + try { + $result = $controllerObj->handleRequest($request, $model); + } catch(SS_HTTPResponse_Exception $responseException) { + $result = $responseException->getResponse(); } + if(!is_object($result) || $result instanceof SS_HTTPResponse) return $result; - // Handle redirections - if(isset($arguments['Redirect'])) { - return "redirect:" . Director::absoluteURL($arguments['Redirect'], true); - - } else { - Director::$urlParams = $arguments; - $controllerObj = Injector::inst()->create($controller); - $controllerObj->setSession($session); - - try { - $result = $controllerObj->handleRequest($request, $model); - } catch(SS_HTTPResponse_Exception $responseException) { - $result = $responseException->getResponse(); - } - if(!is_object($result) || $result instanceof SS_HTTPResponse) return $result; - - user_error("Bad result from url " . $request->getURL() . " handled by " . - get_class($controllerObj)." controller: ".get_class($result), E_USER_WARNING); - - } + user_error("Bad result from url " . $request->getURL() . " handled by " . + get_class($controllerObj)." controller: ".get_class($result), E_USER_WARNING); } } } diff --git a/core/manifest/ConfigManifest.php b/core/manifest/ConfigManifest.php index 9a8efad18..fcf894a26 100644 --- a/core/manifest/ConfigManifest.php +++ b/core/manifest/ConfigManifest.php @@ -485,12 +485,7 @@ class SS_ConfigManifest { */ function mergeInYamlFragment(&$into, $fragment) { foreach ($fragment as $k => $v) { - if (is_array($v) || is_object($v)) { - if (isset($into[$k])) { $sub = $into[$k]; $this->mergeInYamlFragment($sub, $v); $into[$k] = $sub; } - else $into[$k] = $v; - } - else if (is_numeric($k)) $into[] = $v; - else $into[$k] = $v; + Config::merge_high_into_low($into[$k], $v); } }