extendableConstruct(); } /** * Extend this object properties upon construction. */ public static function extend(Closure $callback) { self::extendableExtendCallback($callback); } /** * Pass unhandled URLs to the CMS Controller, if it exists * * @param string $url * @return Response */ protected function passToCmsController($url) { if ( in_array('Cms', Config::get('cms.loadModules', [])) && class_exists('\Cms\Classes\Controller') ) { $this->cmsHandling = true; return App::make('Cms\Classes\Controller')->run($url); } else { return Response::make(View::make('backend::404'), 404); } } /** * Finds and serves the requested backend controller. * If the controller cannot be found, returns the Cms page with the URL /404. * If the /404 page doesn't exist, returns the system 404 page. * @param string $url Specifies the requested page URL. * If the parameter is omitted, the current URL used. * @return string Returns the processed page content. */ public function run($url = null) { $params = RouterHelper::segmentizeUrl($url); // Handle NotFoundHttpExceptions in the backend (usually triggered by abort(404)) Event::listen('exception.beforeRender', function ($exception, $httpCode, $request) { if (!$this->cmsHandling && $exception instanceof \Symfony\Component\HttpKernel\Exception\NotFoundHttpException) { return View::make('backend::404'); } }, 1); /* * Database check */ if (!App::hasDatabase()) { return Config::get('app.debug', false) ? Response::make(View::make('backend::no_database'), 200) : $this->passToCmsController($url); } /* * Look for a Module controller */ $module = $params[0] ?? 'backend'; $controller = $params[1] ?? 'index'; self::$action = $action = isset($params[2]) ? $this->parseAction($params[2]) : 'index'; self::$params = $controllerParams = array_slice($params, 3); $controllerClass = '\\'.$module.'\Controllers\\'.$controller; if ($controllerObj = $this->findController( $controllerClass, $action, base_path().'/modules' )) { return $controllerObj->run($action, $controllerParams); } /* * Look for a Plugin controller */ if (count($params) >= 2) { list($author, $plugin) = $params; $pluginCode = ucfirst($author) . '.' . ucfirst($plugin); if (PluginManager::instance()->isDisabled($pluginCode)) { return Response::make(View::make('backend::404'), 404); } $controller = $params[2] ?? 'index'; self::$action = $action = isset($params[3]) ? $this->parseAction($params[3]) : 'index'; self::$params = $controllerParams = array_slice($params, 4); $controllerClass = '\\'.$author.'\\'.$plugin.'\Controllers\\'.$controller; if ($controllerObj = $this->findController( $controllerClass, $action, plugins_path() )) { return $controllerObj->run($action, $controllerParams); } } /* * Fall back on Cms controller */ return $this->passToCmsController($url); } /** * This method is used internally. * Finds a backend controller with a callable action method. * @param string $controller Specifies a method name to execute. * @param string $action Specifies a method name to execute. * @param string $inPath Base path for class file location. * @return ControllerBase Returns the backend controller object */ protected function findController($controller, $action, $inPath) { /* * Workaround: Composer does not support case insensitivity. */ if (!class_exists($controller)) { $controller = Str::normalizeClassName($controller); $controllerFile = $inPath.strtolower(str_replace('\\', '/', $controller)) . '.php'; if ($controllerFile = File::existsInsensitive($controllerFile)) { include_once $controllerFile; } } if (!class_exists($controller)) { return false; } $controllerObj = App::make($controller); if ($controllerObj->actionExists($action)) { return $controllerObj; } return false; } /** * Process the action name, since dashes are not supported in PHP methods. * @param string $actionName * @return string */ protected function parseAction($actionName) { if (strpos($actionName, '-') !== false) { return camel_case($actionName); } return $actionName; } }