/** * 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); /* * Look for a Module controller */ $module = isset($params[0]) ? $params[0] : 'backend'; $controller = isset($params[1]) ? $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; $controller = isset($params[2]) ? $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 App::make('Cms\\Classes\\Controller')->run($url); }
/** * Finds a page by its URL. Returns the page object and sets the $parameters property. * @param string $url The requested URL string. * @return \Cms\Classes\Page Returns \Cms\Classes\Page object or null if the page cannot be found. */ public function findByUrl($url) { $this->url = $url; $url = RouterHelper::normalizeUrl($url); $apiResult = Event::fire('cms.router.beforeRoute', [$url], true); if ($apiResult !== null) { return $apiResult; } for ($pass = 1; $pass <= 2; $pass++) { $fileName = null; $urlList = []; $cacheable = Config::get('cms.enableRoutesCache'); if ($cacheable) { $fileName = $this->getCachedUrlFileName($url, $urlList); if (is_array($fileName)) { list($fileName, $this->parameters) = $fileName; } } /* * Find the page by URL and cache the route */ if (!$fileName) { $router = $this->getRouterObject(); if ($router->match($url)) { $this->parameters = $router->getParameters(); $fileName = $router->matchedRoute(); if ($cacheable) { if (!$urlList || !is_array($urlList)) { $urlList = []; } $urlList[$url] = !empty($this->parameters) ? [$fileName, $this->parameters] : $fileName; $key = $this->getUrlListCacheKey(); Cache::put($key, base64_encode(serialize($urlList)), Config::get('cms.urlCacheTtl', 1)); } } } /* * Return the page */ if ($fileName) { if (($page = Page::loadCached($this->theme, $fileName)) === null) { /* * If the page was not found on the disk, clear the URL cache * and repeat the routing process. */ if ($pass == 1) { $this->clearCache(); continue; } return null; } return $page; } return null; } }
/** * Returns the base backend URL */ public function baseUrl($path = null) { $backendUri = $this->uri(); $baseUrl = Request::getBaseUrl(); if ($path === null) { return $baseUrl . '/' . $backendUri; } $path = RouterHelper::normalizeUrl($path); return $baseUrl . '/' . $backendUri . $path; }
/** * Returns the base backend URL */ public function baseUrl($path = null) { $backendUri = Config::get('cms.backendUri'); $baseUrl = Request::getBaseUrl(); if ($path === null) { return $baseUrl . '/' . $backendUri; } $path = RouterHelper::normalizeUrl($path); return $baseUrl . '/' . $backendUri . $path; }
/** * Looks up a path to a skin-based file, if it doesn't exist, the default path is used. * @param string $path * @param boolean $isPublic * @return string */ public function getPath($path = null, $isPublic = false) { $path = RouterHelper::normalizeUrl($path); $assetFile = $this->skinPath . $path; if (File::isFile($assetFile)) { return $isPublic ? $this->publicSkinPath . $path : $this->skinPath . $path; } else { return $isPublic ? $this->defaultPublicSkinPath . $path : $this->defaultSkinPath . $path; } }
public function testDefaultValue() { $value = Helper::getSegmentDefaultValue(':my_param_name'); $this->assertFalse($value); $value = Helper::getSegmentDefaultValue(':my_param_name?'); $this->assertFalse($value); $value = Helper::getSegmentDefaultValue(':my_param_name?default value'); $this->assertEquals('default value', $value); $value = Helper::getSegmentDefaultValue(':my_param_name|^[a-z]+[0-9]?$|^[a-z]{3}$'); $this->assertFalse($value); $value = Helper::getSegmentDefaultValue(':my_param_name?default value|^[a-z]+[0-9]?$'); $this->assertEquals('default value', $value); }
/** * Finds a page by its URL. Returns the page object and sets the $parameters property. * @param string $url The requested URL string. * @return \Cms\Classes\Page Returns \Cms\Classes\Page object or null if the page cannot be found. */ public function findByUrl($url) { $url = RouterHelper::normalizeUrl($url); for ($pass = 1; $pass <= 2; $pass++) { $fileName = null; $urlList = []; $cacheable = Config::get('cms.enableRoutesCache') && in_array(Config::get('cache.driver'), ['apc', 'memcached', 'redis', 'array']); if ($cacheable) { $fileName = $this->getCachedUrlFileName($url, $urlList); } /* * Find the page by URL and cache the route */ if (!$fileName) { $router = $this->getRouterObject(); if ($router->match($url)) { $this->parameters = $router->getParameters(); $fileName = $router->matchedRoute(); if ($cacheable) { if (!$urlList || !is_array($urlList)) { $urlList = []; } $urlList[$url] = $fileName; $key = $this->getUrlListCacheKey(); Cache::put($key, serialize($urlList), Config::get('cms.urlCacheTtl', 1)); } } } /* * Return the page */ if ($fileName) { if (($page = Page::loadCached($this->theme, $fileName)) === null) { /* * If the page was not found on the disk, clear the URL cache * and repeat the routing process. */ if ($pass == 1) { $this->clearCache(); continue; } return null; } return $page; } return null; } }
private static function getTagRenderUrl($theme, $item) { $tag = Tag::where('name', $item->reference)->first(); $page = CmsPage::loadCached($theme, $item->cmsPage); // Always check if the page can be resolved if (!$page) { return; } $url = null; if (!$tag) { $options = ['filter' => null, 'slug' => null]; } else { $options = ['filter' => 'tag', 'slug' => $tag->slug]; } // Generate the URL $url = CmsPage::url($page->getBaseFileName(), $options, false); $url = URL::to(Str::lower(RouterHelper::normalizeUrl($url))) . '/'; return $url; }
/** * Returns a Redirect object based on supplied context and parses the model primary key. * @param string $context Redirect context, eg: create, update, delete * @param Model $model The active model to parse in it's ID and attributes. * @return Redirect */ public function makeRedirect($context = null, $model = null) { $redirectUrl = null; if (post('close') && !ends_with($context, '-close')) { $context .= '-close'; } if (post('redirect', true)) { $redirectUrl = Backend::url($this->getRedirectUrl($context)); } if ($model && $redirectUrl) { $redirectUrl = RouterHelper::parseValues($model, array_keys($model->getAttributes()), $redirectUrl); } return $redirectUrl ? Redirect::to($redirectUrl) : null; }
/** * Builds and caches a menu item tree. * This method is used internally for menu items and breadcrumbs. * @param \Cms\Classes\Theme $theme Specifies the current theme. * @return array Returns an array containing the page information */ public static function buildMenuTree($theme) { if (self::$menuTreeCache !== null) { return self::$menuTreeCache; } $key = crc32($theme->getPath()) . 'static-page-menu-tree'; $cached = Cache::get($key, false); $unserialized = $cached ? @unserialize($cached) : false; if ($unserialized !== false) { return self::$menuTreeCache = $unserialized; } $menuTree = ['--root-pages--' => []]; $iterator = function ($items, $parent, $level) use(&$menuTree, &$iterator) { $result = []; foreach ($items as $item) { $viewBag = $item->page->getViewBag(); $pageCode = $item->page->getBaseFileName(); $itemData = ['url' => Str::lower(RouterHelper::normalizeUrl($viewBag->property('url'))), 'title' => $viewBag->property('title'), 'mtime' => $item->page->mtime, 'items' => $iterator($item->subpages, $pageCode, $level + 1), 'parent' => $parent, 'navigation_hidden' => $viewBag->property('navigation_hidden')]; if ($level == 0) { $menuTree['--root-pages--'][] = $pageCode; } $result[] = $pageCode; $menuTree[$pageCode] = $itemData; } return $result; }; $pageList = new PageList($theme); $iterator($pageList->getPageTree(), null, 0); self::$menuTreeCache = $menuTree; Cache::put($key, serialize($menuTree), Config::get('cms.parsedPageCacheTTL', 10)); return self::$menuTreeCache; }
/** * {@inheritDoc} */ public function getPath($path = null, $isPublic = false) { $path = RouterHelper::normalizeUrl($path); return $isPublic ? $this->defaultPublicSkinPath . $path : $this->defaultSkinPath . $path; }
/** * Loads the URL map - a list of page file names and corresponding URL patterns. * The URL map can is cached. The clearUrlMap() method resets the cache. By default * the map is updated every time when a page is saved in the back-end, or * when the interval defined with the cms.urlCacheTtl expires. * @return boolean Returns true if the URL map was loaded from the cache. Otherwise returns false. */ protected function loadUrlMap() { $key = $this->getCacheKey('static-page-url-map'); $cacheable = Config::get('cms.enableRoutesCache'); $cached = $cacheable ? Cache::get($key, false) : false; if (!$cached || ($unserialized = @unserialize($cached)) === false) { /* * The item doesn't exist in the cache, create the map */ $pageList = new PageList($this->theme); $pages = $pageList->listPages(); $map = ['urls' => [], 'files' => [], 'titles' => []]; foreach ($pages as $page) { $url = $page->getViewBag()->property('url'); if (!$url) { continue; } $url = Str::lower(RouterHelper::normalizeUrl($url)); $file = $page->getBaseFileName(); $map['urls'][$url] = $file; $map['files'][$file] = $url; $map['titles'][$file] = $page->getViewBag()->property('title'); } self::$urlMap = $map; if ($cacheable) { Cache::put($key, serialize($map), Config::get('cms.urlCacheTtl', 1)); } return false; } self::$urlMap = $unserialized; return true; }
/** * Register the service provider. * * @return void */ public function register() { /* * Register self */ parent::register('system'); /* * Register core providers */ App::register('October\\Rain\\Config\\ConfigServiceProvider'); App::register('October\\Rain\\Translation\\TranslationServiceProvider'); /* * Define path constants */ if (!defined('PATH_APP')) { define('PATH_APP', app_path()); } if (!defined('PATH_BASE')) { define('PATH_BASE', base_path()); } if (!defined('PATH_PUBLIC')) { define('PATH_PUBLIC', public_path()); } if (!defined('PATH_STORAGE')) { define('PATH_STORAGE', storage_path()); } if (!defined('PATH_PLUGINS')) { define('PATH_PLUGINS', base_path() . Config::get('cms.pluginsDir', '/plugins')); } /* * Register singletons */ App::singleton('string', function () { return new \October\Rain\Support\Str(); }); App::singleton('backend.helper', function () { return new \Backend\Classes\BackendHelper(); }); App::singleton('backend.menu', function () { return \Backend\Classes\NavigationManager::instance(); }); App::singleton('backend.auth', function () { return \Backend\Classes\AuthManager::instance(); }); /* * Check for CLI or system/updates route and disable any plugin initialization * @todo This should be moved to middleware */ $requestPath = \October\Rain\Router\Helper::normalizeUrl(\Request::path()); $systemPath = \October\Rain\Router\Helper::normalizeUrl(Config::get('cms.backendUri') . '/system/updates'); if (stripos($requestPath, $systemPath) === 0) { PluginManager::$noInit = true; } $updateCommands = ['october:up', 'october:update']; if (App::runningInConsole() && count(array_intersect($updateCommands, Request::server('argv'))) > 0) { PluginManager::$noInit = true; } /* * Register all plugins */ $pluginManager = PluginManager::instance(); $pluginManager->registerAll(); /* * Error handling for uncaught Exceptions */ App::error(function (\Exception $exception, $httpCode) { $handler = new ErrorHandler(); return $handler->handleException($exception, $httpCode); }); /* * Write all log events to the database */ Event::listen('illuminate.log', function ($level, $message, $context) { if (!DbDongle::hasDatabase()) { return; } EventLog::add($message, $level); }); /* * Register basic Twig */ App::bindShared('twig', function ($app) { $twig = new Twig_Environment(new TwigLoader(), ['auto_reload' => true]); $twig->addExtension(new TwigExtension()); return $twig; }); /* * Register .htm extension for Twig views */ App::make('view')->addExtension('htm', 'twig', function () { return new TwigEngine(App::make('twig')); }); /* * Register Twig that will parse strings */ App::bindShared('twig.string', function ($app) { $twig = $app['twig']; $twig->setLoader(new Twig_Loader_String()); return $twig; }); /* * Override system mailer with mail settings */ Event::listen('mailer.beforeRegister', function () { if (MailSettings::isConfigured()) { MailSettings::applyConfigValues(); } }); /* * Override standard Mailer content with template */ Event::listen('mailer.beforeAddContent', function ($mailer, $message, $view, $plain, $data) { if (MailTemplate::addContentToMailer($message, $view, $data)) { return false; } }); /* * Register other module providers */ foreach (Config::get('cms.loadModules', []) as $module) { if (strtolower(trim($module)) == 'system') { continue; } App::register('\\' . $module . '\\ServiceProvider'); } /* * Register navigation */ BackendMenu::registerCallback(function ($manager) { $manager->registerMenuItems('October.System', ['system' => ['label' => 'system::lang.settings.menu_label', 'icon' => 'icon-cog', 'url' => Backend::url('system/settings'), 'permissions' => ['backend.manage_users', 'system.*'], 'order' => 1000]]); }); /* * Register report widgets */ WidgetManager::instance()->registerReportWidgets(function ($manager) { $manager->registerReportWidget('System\\ReportWidgets\\Status', ['label' => 'backend::lang.dashboard.status.widget_title_default', 'context' => 'dashboard']); }); /* * Register permissions */ BackendAuth::registerCallback(function ($manager) { $manager->registerPermissions('October.System', ['system.manage_updates' => ['label' => 'system::lang.permissions.manage_software_updates', 'tab' => 'system::lang.permissions.name'], 'system.manage_mail_settings' => ['label' => 'system::lang.permissions.manage_mail_settings', 'tab' => 'system::lang.permissions.name'], 'system.manage_mail_templates' => ['label' => 'system::lang.permissions.manage_mail_templates', 'tab' => 'system::lang.permissions.name']]); }); /* * Register markup tags */ MarkupManager::instance()->registerCallback(function ($manager) { $manager->registerFunctions(['input' => 'input', 'post' => 'post', 'get' => 'get', 'link_to' => 'link_to', 'link_to_asset' => 'link_to_asset', 'link_to_route' => 'link_to_route', 'link_to_action' => 'link_to_action', 'asset' => 'asset', 'action' => 'action', 'url' => 'url', 'route' => 'route', 'secure_url' => 'secure_url', 'secure_asset' => 'secure_asset', 'str_*' => ['Str', '*'], 'url_*' => ['URL', '*'], 'html_*' => ['HTML', '*'], 'form_*' => ['Form', '*'], 'form_macro' => ['Form', '__call']]); $manager->registerFilters(['slug' => ['Str', 'slug'], 'plural' => ['Str', 'plural'], 'singular' => ['Str', 'singular'], 'finish' => ['Str', 'finish'], 'snake' => ['Str', 'snake'], 'camel' => ['Str', 'camel'], 'studly' => ['Str', 'studly'], 'trans' => ['Lang', 'get'], 'transchoice' => ['Lang', 'choice'], 'md' => ['October\\Rain\\Support\\Markdown', 'parse']]); }); /* * Register settings */ SettingsManager::instance()->registerCallback(function ($manager) { $manager->registerSettingItems('October.System', ['administrators' => ['label' => 'backend::lang.user.menu_label', 'description' => 'backend::lang.user.menu_description', 'category' => SettingsManager::CATEGORY_SYSTEM, 'icon' => 'icon-users', 'url' => Backend::url('backend/users'), 'permissions' => ['backend.manage_users'], 'order' => 600], 'updates' => ['label' => 'system::lang.updates.menu_label', 'description' => 'system::lang.updates.menu_description', 'category' => SettingsManager::CATEGORY_SYSTEM, 'icon' => 'icon-cloud-download', 'url' => Backend::url('system/updates'), 'permissions' => ['system.manage_updates'], 'order' => 700], 'event_logs' => ['label' => 'system::lang.event_log.menu_label', 'description' => 'system::lang.event_log.menu_description', 'category' => SettingsManager::CATEGORY_LOGS, 'icon' => 'icon-exclamation-triangle', 'url' => Backend::url('system/eventlogs'), 'permissions' => ['system.access_event_logs'], 'order' => 800], 'request_logs' => ['label' => 'system::lang.request_log.menu_label', 'description' => 'system::lang.request_log.menu_description', 'category' => SettingsManager::CATEGORY_LOGS, 'icon' => 'icon-file-o', 'url' => Backend::url('system/requestlogs'), 'permissions' => ['system.access_request_logs'], 'order' => 800], 'mail_settings' => ['label' => 'system::lang.mail.menu_label', 'description' => 'system::lang.mail.menu_description', 'category' => SettingsManager::CATEGORY_MAIL, 'icon' => 'icon-envelope', 'class' => 'System\\Models\\MailSettings', 'permissions' => ['system.manage_mail_settings'], 'order' => 400], 'mail_templates' => ['label' => 'system::lang.mail_templates.menu_label', 'description' => 'system::lang.mail_templates.menu_description', 'category' => SettingsManager::CATEGORY_MAIL, 'icon' => 'icon-envelope-square', 'url' => Backend::url('system/mailtemplates'), 'permissions' => ['system.manage_mail_templates'], 'order' => 500]]); }); /* * Register console commands */ $this->registerConsoleCommand('october.up', 'System\\Console\\OctoberUp'); $this->registerConsoleCommand('october.down', 'System\\Console\\OctoberDown'); $this->registerConsoleCommand('october.update', 'System\\Console\\OctoberUpdate'); $this->registerConsoleCommand('october.util', 'System\\Console\\OctoberUtil'); $this->registerConsoleCommand('plugin.install', 'System\\Console\\PluginInstall'); $this->registerConsoleCommand('plugin.remove', 'System\\Console\\PluginRemove'); $this->registerConsoleCommand('plugin.refresh', 'System\\Console\\PluginRefresh'); /* * Override clear cache command */ App::bindShared('command.cache.clear', function ($app) { return new \System\Console\CacheClear($app['cache'], $app['files']); }); /* * Register the sidebar for the System main menu */ BackendMenu::registerContextSidenavPartial('October.System', 'system', '@/modules/system/partials/_system_sidebar.htm'); }
/** * Captures and removes every segment of a URL after a wildcard * pattern segment is detected, until both collections of segments * are the same size. * @param array $urlSegments * @return array */ protected function captureWildcardSegments(&$urlSegments) { $wildSegments = []; $patternSegments = $this->segments; $segmentDiff = count($urlSegments) - count($patternSegments); $wildMode = false; $wildCount = 0; foreach ($urlSegments as $index => $urlSegment) { if ($wildMode) { if ($wildCount < $segmentDiff) { $wildSegments[] = $urlSegment; $wildCount++; unset($urlSegments[$index]); continue; } else { break; } } $patternSegment = $patternSegments[$index]; if (Helper::segmentIsWildcard($patternSegment)) { $wildMode = true; } } // Reset array index $urlSegments = array_values($urlSegments); return $wildSegments; }
/** * Returns URL of a category page. */ protected static function getCategoryPageUrl($pageCode, $category, $theme) { $page = CmsPage::loadCached($theme, $pageCode); if (!$page) { return; } $properties = $page->getComponentProperties('blogPosts'); if (!isset($properties['categoryFilter'])) { return; } $filter = substr($properties['categoryFilter'], 1); $url = CmsPage::url($page->getBaseFileName(), [$filter => $category->slug], false); return Str::lower(RouterHelper::normalizeUrl($url)); }
/** * Check for CLI or system/updates route and disable any plugin initialization */ protected function registerPrivilegedActions() { $requests = ['/combine', '@/system/updates', '@/system/install', '@/backend/auth']; $commands = ['october:up', 'october:update']; /* * Requests */ $path = RouterHelper::normalizeUrl(Request::path()); $backendUri = RouterHelper::normalizeUrl(Config::get('cms.backendUri', 'backend')); foreach ($requests as $request) { if (substr($request, 0, 1) == '@') { $request = $backendUri . substr($request, 1); } if (stripos($path, $request) === 0) { PluginManager::$noInit = true; } } /* * CLI */ if (App::runningInConsole() && count(array_intersect($commands, Request::server('argv'))) > 0) { PluginManager::$noInit = true; } }
/** * Returns the onclick event for a list row. * @param Model $record * @return string */ public function getRecordOnClick($record) { if (!isset($this->recordOnClick)) { return null; } $columns = array_keys($record->getAttributes()); $recordOnClick = RouterHelper::parseValues($record, $columns, $this->recordOnClick); return Html::attributes(['onclick' => $recordOnClick]); }
/** * Checks whether a given URL matches a given pattern. * @param string $url The URL to check. * @param array $parameters A reference to a PHP array variable to return the parameter list fetched from URL. * @return boolean Returns true if the URL matches the pattern. Otherwise returns false. */ public function resolveUrl($url, &$parameters) { $parameters = array(); $patternSegments = $this->segments; $patternSegmentNum = count($patternSegments); $urlSegments = Helper::segmentizeUrl($url); /* * If the number of URL segments is more than the number of pattern segments - return false */ if (count($urlSegments) > count($patternSegments)) { return false; } /* * Compare pattern and URL segments */ foreach ($patternSegments as $index => $patternSegment) { $patternSegmentLower = mb_strtolower($patternSegment); if (strpos($patternSegment, ':') !== 0) { /* * Static segment */ if (!array_key_exists($index, $urlSegments) || $patternSegmentLower != mb_strtolower($urlSegments[$index])) { return false; } } else { /* * Dynamic segment. Initialize the parameter */ $paramName = Helper::getParameterName($patternSegment); $parameters[$paramName] = false; /* * Determine whether it is optional */ $optional = Helper::segmentIsOptional($patternSegment); /* * Check if the optional segment has no required segments following it */ if ($optional && $index < $patternSegmentNum - 1) { for ($i = $index + 1; $i < $patternSegmentNum; $i++) { if (!Helper::segmentIsOptional($patternSegments[$i])) { $optional = false; break; } } } /* * If the segment is optional and there is no corresponding value in the URL, assign the default value (if provided) * and skip to the next segment. */ $urlSegmentExists = array_key_exists($index, $urlSegments); if ($optional && !$urlSegmentExists) { $parameters[$paramName] = Helper::getSegmentDefaultValue($patternSegment); continue; } /* * If the segment is not optional and there is no corresponding value in the URL, return false */ if (!$optional && !$urlSegmentExists) { return false; } /* * Validate the value with the regular expression */ $regexp = Helper::getSegmentRegExp($patternSegment); if ($regexp) { try { if (!preg_match($regexp, $urlSegments[$index])) { return false; } } catch (\Exception $ex) { } } /* * Set the parameter value */ $parameters[$paramName] = $urlSegments[$index]; } } return true; }