/** * Get the rendered HTML template for the advlink plugin. * * This needs to be a full controller because it requires some of core+'s functionality to determine pages. * * @deprecated v4.1.0 */ public function link() { $view = $this->getView(); // Since this will deal with mainly frontend data, it's doubtful that the admin would want to list admin pages. $pages = PageModel::GetPagesAsOptions('admin = 0'); // For each page, resolve the url to a full url for this site. Useful because I cannot guarantee correct // resolution after it goes through tinyMCE's logic. $pagesresolved = array(); foreach ($pages as $url => $title) { $pagesresolved[\Core\resolve_link($url)] = $title; } $tplname = Template::ResolveFile('pages/tinymce/link.phtml'); $view->overrideTemplate(new Core\Templates\Backends\PHTML()); $view->mastertemplate = false; $view->templatename = $tplname; $view->assign('pages', $pagesresolved); }
/** * Execute the controller and method this page request points to. */ public function execute() { \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Starting PageRequest->execute()'); if($this->isCacheable()){ $uakey = \Core\UserAgent::Construct()->getPseudoIdentifier(); $urlkey = $this->host . $this->uri; $expires = $this->getPageModel()->get('expires'); $key = 'page-cache-' . md5($urlkey . '-' . $uakey); $cached = \Core\Cache::Get($key, $expires); if($cached && $cached instanceof View){ $this->_pageview = $cached; $this->_cached = true; return; } } // Anything that needs to fire off *before* the page is rendered. // This includes widgets, script addons, and anything else that needs a CurrentPage. HookHandler::DispatchHook('/core/page/preexecute'); // Load the underlying controller. $pagedat = $this->splitParts(); /** @var View $view The valid view object for this page */ $view = $this->getView(); // The controller must exist first! // (note, the SplitParts logic already takes care of the "Is this a valid controller" logic) if (!(isset($pagedat['controller']) && $pagedat['controller'])) { $view->error = View::ERROR_NOTFOUND; return; } $component = Core::GetComponentByController($pagedat['controller']); ////////////////////////////////////////////////////////////////////////////// /// In this block of logic, either the page is executed and a view returned, /// or a view is generated with an error. ////////////////////////////////////////////////////////////////////////////// if (!$component) { // Not found $view->error = View::ERROR_NOTFOUND; return; } elseif(!is_a($component, 'Component_2_1')) { $view->error = View::ERROR_NOTFOUND; return; } // Any method that starts with a "_" is an internal-only method! if ($pagedat['method']{0} == '_') { $view->error = View::ERROR_NOTFOUND; return; } // It also must be a part of the class... obviously if (!method_exists($pagedat['controller'], $pagedat['method'])) { $view->error = View::ERROR_NOTFOUND; return; } /** @var $controller Controller_2_1 This will be a Controller object. */ $controller = Controller_2_1::Factory($pagedat['controller']); $view->baseurl = $this->getBaseURL(); $controller->setView($view); // Make sure that the controller can access this object. $controller->setPageRequest($this); // The main page object. $page = $this->getPageModel(); // Check the access string first, (if there is one) if ($controller->accessstring !== null) { // Update the page's access string, (just in case it's saved at the end of execution) $page->set('access', $controller->accessstring); // And if the user doesn't have access to it... if (!\Core\user()->checkAccess($controller->accessstring)) { $view->error = View::ERROR_ACCESSDENIED; return; } } if($page->get('password_protected')) { if(\Core\Session::Get('page-password-protected/' . $page->get('baseurl')) !== $page->get('password_protected')){ $view->templatename = '/pages/page/passwordprotected.tpl'; $form = new Form(); $form->set('callsmethod', 'PageRequest::PasswordProtectHandler'); $form->addElement( 'system', [ 'name' => 'page', 'value' => $page ] ); $form->addElement( 'password', [ 'name' => 'passinput', 'title' => 'Password', 'required' => 'required', 'maxlength' => 128 ] ); $form->addElement( 'submit', [ 'value' => 'Submit' ] ); $view->assign('form', $form); return; } } // If the parent Controller object has a method named $pagedat['method'], assume it's a security error! // This is because if the parent Controller object has a method, it's most likely a utility method // that shouldn't be called from the public web! foreach(get_class_methods('Controller_2_1') as $parentmethod){ $parentmethod = strtolower($parentmethod); if($parentmethod == $pagedat['method']){ $view->error = View::ERROR_BADREQUEST; return; } } // Additional security logic for existing pages in multi-site mode. // If this exact URL is registered to another site, then // don't allow this site to display it. if(!$page->exists() && Core::IsComponentAvailable('multisite') && MultiSiteHelper::IsEnabled()){ $site = MultiSiteHelper::GetCurrentSiteID(); $anypage = PageModel::Find(['baseurl = ' . $page->get('baseurl')], 1); if($anypage){ if($anypage->get('site') == -1){ // If this is a global page.... that's ok. // Just remap the page variable to this one! $page = $anypage; } elseif($anypage->get('site') == $site){ // Strange... it should have located this page... // Anyway, it's allowed, the site matches up. $page = $anypage; } else{ \Core\redirect($anypage->getResolvedURL()); } } } $return = call_user_func(array($controller, $pagedat['method'])); if (is_int($return)) { // A generic error code was returned. Create a View with that code and return that instead. $view->error = $return; //return; } elseif(is_a($return, 'View') && $return != $view){ // The controller method changed the view, (which is allowed), // but this needs to be remapped to this object so render knows about it. $this->_pageview = $view = $return; } elseif ($return === null) { // Hopefully it's setup! $return = $controller->getView(); if($return != $view){ $this->_pageview = $view = $return; } } elseif(!is_a($return, 'View')){ if(DEVELOPMENT_MODE){ var_dump('Controller method returned', $return); die('Sorry, but this controller did not return a valid object. Please ensure that your method returns either an integer, null, or a View object!'); } else{ $view->error = View::ERROR_SERVERERROR; return; } } // No else needed, else it's a valid object. // You may be asking why $view is one object, but $return is the return from page execution. // GREAT QUESTION, The $view is the original view object created from the page request. That is passed into // the controller and exposed via $this->getView(). The return can be a view, int, or other status indicator. // However since the controller can return a different view, that view should be used instead! ///** @var $return View */ // Allow the controller to assign controls via a shortcut function. if($view->error == View::ERROR_NOERROR){ $controls = $controller->getControls(); // This method may do absolutely nothing, add the controls to the view itself, or return an array of them. if(is_array($controls)){ foreach($controls as $control){ $view->addControl($control); } } } // For some of the options, there may be some that can be used for a fuzzy page, ie: a page's non-fuzzy template, // title, or meta information. if($view->error == View::ERROR_NOERROR){ if ($page->exists()) { $defaultpage = $page; } else { $defaultpage = null; $url = $view->baseurl; while ($url != '') { $url = substr($url, 0, strrpos($url, '/')); $p = PageModel::Find(array('baseurl' => $url, 'fuzzy' => 1), 1); if ($p === null) continue; if ($p->exists()) { $defaultpage = $p; break; } } if ($defaultpage === null) { // Fine.... $defaultpage = $page; } } $defaultmetas = $defaultpage->getLink('PageMeta'); // Make a list of the existing ones so I know which ones not to overwrite! // Just the key will suffice quite nicely. $currentmetas = array(); foreach($view->meta as $k => $meta){ $currentmetas[] = $k; } // Load some of the page information into the view now! foreach($defaultmetas as $meta){ /** @var $meta PageMetaModel */ $key = $meta->get('meta_key'); $viewmeta = $meta->getViewMetaObject(); // again, allow the executed controller have the final say on meta information. if ($meta->get('meta_value_title') && !in_array($key, $currentmetas)) { $view->meta[$key] = $viewmeta; } } // Since the controller already ran, do not overwrite the title. if ($view->title === null){ $view->title = $defaultpage->get('title'); } // Tracker to see if this page, (or a parent's page), is an admin-level page. // This is required because "admin" pages may have a different skin and should always have the dashboard as the top-level breadcrumb. /** @var boolean $isadmin */ $isadmin = ($page->get('admin') == '1'); $parents = array(); $parenttree = $page->getParentTree(); foreach ($parenttree as $parent) { /** @var PageModel $parent */ $parents[] = array( 'title' => $parent->get('title'), 'link' => $parent->getResolvedURL() ); // Since I'm here, check if this page is an admin page. if($parent->get('admin')){ $isadmin = true; } } $view->breadcrumbs = array_merge($parents, $view->breadcrumbs); if($isadmin && $view->baseurl != '/admin'){ // Make sure that admin is the top breadcrumb. // This block doesn't need to apply for the actual admin page itself, as that doesn't need its own breadcrumb :/ $adminlink = \Core\resolve_link('/admin'); if(!isset($view->breadcrumbs[0])){ // Nothing is even set! $view->breadcrumbs[] = ['title' => 'Administration', 'link' => $adminlink]; } elseif($view->breadcrumbs[0]['link'] != $adminlink){ // It's set, but not to admin. $view->breadcrumbs = array_merge([['title' => 'Administration', 'link' => $adminlink]], $view->breadcrumbs); } } } else{ $defaultpage = null; $isadmin = false; } if( $view->mode == View::MODE_PAGEORAJAX && $this->isAjax() && $view->jsondata !== null && $view->templatename === null ){ // Allow the content type to be overridden for ajax pages that have JSON data embedded in them. $view->contenttype = View::CTYPE_JSON; } if($view->mode == View::MODE_NOOUTPUT){ $view->mastertemplate = false; $view->templatename = null; } elseif( $view->error == View::ERROR_NOERROR && $view->contenttype == View::CTYPE_HTML && $view->templatename === null ){ // Try to guess the templatename if it wasn't set. // This $cnameshort = (strpos($pagedat['controller'], 'Controller') == strlen($pagedat['controller']) - 10) ? substr($pagedat['controller'], 0, -10) : $pagedat['controller']; $view->templatename = strtolower('/pages/' . $cnameshort . '/' . $pagedat['method'] . '.tpl'); } elseif( $view->error == View::ERROR_NOERROR && $view->contenttype == View::CTYPE_XML && $view->templatename === null ){ $cnameshort = (strpos($pagedat['controller'], 'Controller') == strlen($pagedat['controller']) - 10) ? substr($pagedat['controller'], 0, -10) : $pagedat['controller']; $view->templatename = Template::ResolveFile(strtolower('pages/' . $cnameshort . '/' . $pagedat['method'] . '.xml.tpl')); } // In addition to the autogeneration, also support the page_template from the datastore. if($defaultpage && $defaultpage->get('page_template')){ // Switch the template over to that custom one. // Some legacy data will have the fully resolved path for this template. // This has been switched to just the basename of the custom template, // but legacy data be legacy, 'yo. 0.o $base = substr($view->templatename, 0, -4); $override = $defaultpage->get('page_template'); if($base && strpos($override, $base) === 0){ $view->templatename = $override; } elseif($base){ $view->templatename = $base . '/' . $override; } } // Guess which theme skin (mastertemplate) should be used if one wasn't specified. if($view->mastertemplate == 'admin'){ // If the master template is set explictly to be the admin skin, then transpose that to the set admin skin. // This is useful for the pages that may not be under the "/admin" umbrella, but still rendered with the admin UI. $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif($view->mastertemplate){ // No change needed, just skip the below cases. } elseif($view->mastertemplate === false){ // If the master template is explictly set to false, the page wanted no master template! } elseif($isadmin){ // This page doesn't have a master template set, but it or a parent is set as an admin-level page. $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif ($defaultpage && $defaultpage->get('theme_template')) { // Master template set in the database? $view->mastertemplate = $defaultpage->get('theme_template'); } elseif($defaultpage && $defaultpage->exists() && $defaultpage->get('admin')){ // Or an admin level page? $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } elseif(sizeof($view->breadcrumbs) && $view->breadcrumbs[0]['title'] == 'Administration'){ // Whatever, close e-damn-nough! // This happens for pages that don't actually exist, like "edit".... $view->mastertemplate = ConfigHandler::Get('/theme/default_admin_template'); } else{ $view->mastertemplate = ConfigHandler::Get('/theme/default_template'); } // First of all, if the current theme is not available, reset back to the first theme available! if(!($theme = ThemeHandler::GetTheme())){ /** @var \Theme\Theme $theme */ $theme = ThemeHandler::GetTheme('base-v2'); $view->mastertemplate = 'basic.tpl'; \Core\set_message('t:MESSAGE_ERROR_INVALID_THEME_SELECTED'); } // Make sure the selected mastertemplate actually exists! if($view->mastertemplate !== false){ $themeskins = $theme->getSkins(); $mastertplgood = false; foreach($themeskins as $skin){ if($skin['file'] == $view->mastertemplate){ // It's located! $mastertplgood =true; break; } } // A few special cases. if($view->mastertemplate == 'blank.tpl'){ // This is acceptable as a default one. $mastertplgood =true; } if(!$mastertplgood){ // Just use the first one instead! trigger_error('Invalid skin [' . $view->mastertemplate . '] selected for this page, skin is not located within the selected theme! Using first available instead.', E_USER_NOTICE); $view->mastertemplate = $themeskins[0]['file']; } } // Handle some of the new automatic meta data associated with Pages and the resulting View. if(\ConfigHandler::Get('/core/page/indexable') == 'deny'){ // Administratively set to noindex on all pages. $view->addMetaName('robots', 'noindex'); } elseif(!$page->get('indexable')){ // Bots have no business indexing user-action pages. $view->addMetaName('robots', 'noindex'); } if(!isset($view->meta['title'])){ $view->meta['title'] = $page->getSEOTitle(); } HookHandler::DispatchHook('/core/page/postexecute'); \Core\Utilities\Profiler\Profiler::GetDefaultProfiler()->record('Completed PageRequest->execute()'); }