dataRecord. Any unrecognised method calls, for example, Title() * and Content(), will be passed along to the data record, * * Subclasses of ContentController are generally instantiated by ModelAsController; this will create * a controller based on the URLSegment action variable, by looking in the SiteTree table. * * @todo Can this be used for anything other than SiteTree controllers? */ class ContentController extends Controller { /** * @var SiteTree */ protected $dataRecord; private static $extensions = [ OldPageRedirector::class, ]; private static $allowed_actions = [ 'successfullyinstalled', 'deleteinstallfiles', // secured through custom code 'LoginForm', ]; private static $casting = [ 'SilverStripeNavigator' => 'HTMLFragment', ]; /** * The ContentController will take the URLSegment parameter from the URL and use that to look * up a SiteTree record. * * @param SiteTree $dataRecord */ public function __construct($dataRecord = null) { if (!$dataRecord) { $dataRecord = new SiteTree(); if ($this->hasMethod("Title")) { $dataRecord->Title = $this->Title(); } $dataRecord->URLSegment = static::class; $dataRecord->ID = -1; } $this->dataRecord = $dataRecord; parent::__construct(); $this->setFailover($this->dataRecord); } /** * Return the link to this controller, but force the expanded link to be returned so that form methods and * similar will function properly. * * @param string|null $action Action to link to. * @return string */ public function Link($action = null) { return $this->data()->Link(($action ? $action : true)); } //----------------------------------------------------------------------------------// // These flexible data methods remove the need for custom code to do simple stuff /** * Return the children of a given page. The parent reference can either be a page link or an ID. * * @param string|int $parentRef * @return SS_List */ public function ChildrenOf($parentRef) { $parent = SiteTree::get_by_link($parentRef); if (!$parent && is_numeric($parentRef)) { $parent = DataObject::get_by_id(SiteTree::class, $parentRef); } if ($parent) { return $parent->Children(); } return null; } /** * @param string $link * @return SiteTree */ public function Page($link) { return SiteTree::get_by_link($link); } protected function init() { parent::init(); // In the CMS Preview or draft contexts, we never want to cache page output. if ($this->getRequest()->getVar('CMSPreview') === '1' || $this->getRequest()->getVar('stage') === Versioned::DRAFT ) { HTTPCacheControlMiddleware::singleton()->disableCache(true); } // If we've accessed the homepage as /home/, then we should redirect to /. if ($this->dataRecord instanceof SiteTree && RootURLController::should_be_on_root($this->dataRecord) && (!isset($this->urlParams['Action']) || !$this->urlParams['Action']) && !$_POST && !$_FILES && !$this->redirectedTo() ) { $getVars = $_GET; unset($getVars['url']); if ($getVars) { $url = "?" . http_build_query($getVars ?? []); } else { $url = ""; } $this->redirect($url, 301); return; } if ($this->dataRecord) { $this->dataRecord->extend('contentcontrollerInit', $this); } else { SiteTree::singleton()->extend('contentcontrollerInit', $this); } if ($this->redirectedTo()) { return; } // Check page permissions if ($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) { Security::permissionFailure($this); return; } } /** * This acts the same as {@link Controller::handleRequest()}, but if an action cannot be found this will attempt to * fall over to a child controller in order to provide functionality for nested URLs. * * @throws HTTPResponse_Exception */ public function handleRequest(HTTPRequest $request): HTTPResponse { /** @var SiteTree $child */ $child = null; $action = $request->param('Action'); // If nested URLs are enabled, and there is no action handler for the current request then attempt to pass // control to a child controller. This allows for the creation of chains of controllers which correspond to a // nested URL. if ($action && SiteTree::config()->nested_urls && !$this->hasAction($action)) { $filter = URLSegmentFilter::create(); // look for a page with this URLSegment $child = SiteTree::get()->filter([ 'ParentID' => $this->ID, // url encode unless it's multibyte (already pre-encoded in the database) 'URLSegment' => $filter->getAllowMultibyte() ? $action : rawurlencode($action), ])->first(); } // we found a page with this URLSegment. if ($child) { $request->shiftAllParams(); $request->shift(); $response = ModelAsController::controller_for($child)->handleRequest($request); } else { Director::set_current_page($this->data()); try { $response = parent::handleRequest($request); Director::set_current_page(null); } catch (HTTPResponse_Exception $e) { $this->popCurrent(); Director::set_current_page(null); throw $e; } } return $response; } /** * Get the project name * * @return string */ public function project() { return ModuleManifest::config()->get('project'); } /** * Returns the associated database record */ public function data() { return $this->dataRecord; } /*--------------------------------------------------------------------------------*/ /** * Returns a fixed navigation menu of the given level. * @param int $level Menu level to return. * @return ArrayList */ public function getMenu($level = 1) { if ($level == 1) { $result = SiteTree::get()->filter([ "ShowInMenus" => 1, "ParentID" => 0, ]); } else { $parent = $this->data(); $stack = [$parent]; if ($parent) { while (($parent = $parent->Parent()) && $parent->exists()) { array_unshift($stack, $parent); } } if (isset($stack[$level - 2])) { $result = $stack[$level - 2]->Children(); } } $visible = []; // Remove all entries the can not be viewed by the current user // We might need to create a show in menu permission if (isset($result)) { foreach ($result as $page) { /** @var SiteTree $page */ if ($page->canView()) { $visible[] = $page; } } } return new ArrayList($visible); } public function Menu($level) { return $this->getMenu($level); } /** * Returns the default log-in form. * * @todo Check if here should be returned just the default log-in form or * all available log-in forms (also OpenID...) * @return \SilverStripe\Security\MemberAuthenticator\MemberLoginForm */ public function LoginForm() { return Injector::inst()->get(MemberAuthenticator::class)->getLoginHandler($this->Link())->loginForm(); } public function SilverStripeNavigator() { $member = Security::getCurrentUser(); $items = ''; $message = ''; if (Director::isDev() || Permission::check('CMS_ACCESS_CMSMain') || Permission::check('VIEW_DRAFT_CONTENT')) { if ($this->dataRecord) { Requirements::css('silverstripe/cms: client/dist/styles/SilverStripeNavigator.css'); Requirements::javascript('silverstripe/admin: thirdparty/jquery/jquery.js'); Requirements::javascript('silverstripe/cms: client/dist/js/SilverStripeNavigator.js'); $return = $nav = SilverStripeNavigator::get_for_record($this->dataRecord); $items = $return['items']; $message = $return['message']; } if ($member) { $firstname = Convert::raw2xml($member->FirstName); $surname = Convert::raw2xml($member->Surname); $logInMessage = _t(__CLASS__ . '.LOGGEDINAS', 'Logged in as') . " {$firstname} {$surname} - " . _t(__CLASS__ . '.LOGOUT', 'Log out') . ""; } else { $logInMessage = sprintf( '%s - %s', _t(__CLASS__ . '.NOTLOGGEDIN', 'Not logged in'), Security::config()->login_url, _t(__CLASS__ . '.LOGIN', 'Login') . "" ); } $viewPageIn = _t(__CLASS__ . '.VIEWPAGEIN', 'View Page in:'); return <<