Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 438 lines (370 sloc) 15.609 kB
<?php
/**
* @package core
*/
/**
* The Administration class is an instance of Symphony that controls
* all backend pages. These pages are HTMLPages are usually generated
* using XMLElement before being rendered as HTML. These pages do not
* use XSLT. The Administration is only accessible by logged in Authors
*/
require_once(CORE . '/class.symphony.php');
require_once(TOOLKIT . '/class.htmlpage.php');
require_once(TOOLKIT . '/class.ajaxpage.php');
Class Administration extends Symphony{
/**
* The path of the current page, ie. '/blueprints/sections/'
* @var string
*/
private $_currentPage = null;
/**
* An associative array of the page's callback, including the keys
* 'driver', which is a lowercase version of `$this->_currentPage`
* with any slashes removed, 'classname', which is the name of the class
* for this page, 'pageroot', which is the root page for the given page, (ie.
* excluding /saved/, /created/ or any sub pages of the current page that are
* handled using the _switchboard function.
*
* @see toolkit.AdministrationPage#__switchboard()
* @var array
*/
private $_callback = null;
/**
* The class representation of the current Symphony backend page,
* which is a subclass of the HTMLPage class. Symphony uses a convention
* of prefixing backend page classes with 'content'. ie. 'contentBlueprintsSections'
* @var HTMLPage
*/
public $Page;
/**
* This function returns an instance of the Administration
* class. It is the only way to create a new Administration, as
* it implements the Singleton interface
*
* @return Administration
*/
public static function instance(){
if(!(self::$_instance instanceof Administration)) {
self::$_instance = new Administration;
}
return self::$_instance;
}
/**
* The constructor for Administration calls the parent Symphony
* constructor.
*
* @see core.Symphony#__construct()
* @deprecated The constructor creates backwards compatible references
* to `$this->Database`, `$this->ExtensionManager` and `$this->Configuration`
* that act as alias for `Symphony::Database()`, `Symphony::ExtensionManager()`
* and `Symphony::Configuration()`. These will be removed in the
* next Symphony release
*/
protected function __construct(){
parent::__construct();
// Need this part for backwards compatiblity
$this->Database = Symphony::Database();
$this->Configuration = Symphony::Configuration();
$this->ExtensionManager = Symphony::ExtensionManager();
}
/**
* Returns the current Page path, excluding the domain and Symphony path.
*
* @return string
* The path of the current page, ie. '/blueprints/sections/'
*/
public function getCurrentPageURL(){
return $this->_currentPage;
}
/**
* Overrides the Symphony isLoggedIn function to allow Authors
* to become logged into the backend when `$_REQUEST['auth-token']`
* is present. This logs an Author in using the loginFromToken function.
* A token may be 6 or 8 characters in length in the backend. A 6 character token
* is used for forget password requests, whereas the 8 character token is used to login
* an Author into the page
*
* @see core.Symphony#loginFromToken()
* @return boolean
*/
public function isLoggedIn(){
if (isset($_REQUEST['auth-token']) && $_REQUEST['auth-token'] && in_array(strlen($_REQUEST['auth-token']), array(6, 8))) {
return $this->loginFromToken($_REQUEST['auth-token']);
}
return parent::isLoggedIn();
}
/**
* Given the URL path of a Symphony backend page, this function will
* attempt to resolve the URL to a Symphony content page in the backend
* or a page provided by an extension. This function checks to ensure a user
* is logged in, otherwise it will direct them to the login page
*
* @param string $page
* The URL path after the root of the Symphony installation, including a starting
* slash, such as '/login/'
* @return HTMLPage
*/
private function __buildPage($page){
$is_logged_in = $this->isLoggedIn();
if(empty($page) || is_null($page)){
if(!$is_logged_in) {
$page = "/login";
}
else {
// Will redirect an Author to their default area of the Backend
// Integers are indicative of section's, text is treated as the path
// to the page after `SYMPHONY_URL`
$default_area = null;
if(is_numeric($this->Author->get('default_area'))) {
$section_handle = Symphony::Database()->fetchVar('handle', 0, "SELECT `handle` FROM `tbl_sections` WHERE `id` = '".$this->Author->get('default_area')."' LIMIT 1");
if(!$section_handle){
$section_handle = Symphony::Database()->fetchVar('handle', 0, "SELECT `handle` FROM `tbl_sections` ORDER BY `sortorder` LIMIT 1");
}
if(!is_null($section_handle)) {
$default_area = "/publish/{$section_handle}/";
}
}
else if(!is_null($this->Author->get('default_area'))) {
$default_area = preg_replace('/^' . preg_quote(SYMPHONY_URL, '/') . '/i', '', $this->Author->get('default_area'));
}
if(is_null($default_area)) {
if($this->Author->isDeveloper()) {
$section_handle = Symphony::Database()->fetchVar('handle', 0, "SELECT `handle` FROM `tbl_sections` ORDER BY `sortorder` LIMIT 1");
if(!is_null($section_handle)) {
// If there are sections created, redirect to the first one (sortorder)
redirect(SYMPHONY_URL . "/publish/{$section_handle}/");
}
else {
// If there are no sections created, default to the Section page
redirect(SYMPHONY_URL . '/blueprints/sections/');
}
}
else {
redirect(SYMPHONY_URL . "/system/authors/edit/".$this->Author->get('id')."/");
}
}
else {
redirect(SYMPHONY_URL . $default_area);
}
}
}
if(!$this->_callback = $this->getPageCallback($page)){
$this->errorPageNotFound();
}
include_once((isset($this->_callback['driverlocation']) ? $this->_callback['driverlocation'] : CONTENT) . '/content.' . $this->_callback['driver'] . '.php');
$this->Page = new $this->_callback['classname']($this);
if(!$is_logged_in && $this->_callback['driver'] != 'login'){
if(is_callable(array($this->Page, 'handleFailedAuthorisation'))) $this->Page->handleFailedAuthorisation();
else{
include_once(CONTENT . '/content.login.php');
$this->Page = new contentLogin($this);
$this->Page->build();
}
}
else {
if (!is_array($this->_callback['context'])) $this->_callback['context'] = array();
// Check for update Alert
if(file_exists(DOCROOT . '/update.php') && $this->__canAccessAlerts()) {
if(file_exists(DOCROOT . '/README.markdown') && is_readable(DOCROOT . '/README.markdown')) {
$readme = file(DOCROOT . '/README.markdown', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$readme = trim(str_replace('- Version:', '', $readme[1]));
$current_version = Symphony::Configuration()->get('version', 'symphony');
// The updater contains a version higher than the current Symphony version.
if(version_compare($current_version, $readme, '<')) {
$message = __('Run the updater to update Symphony to %s. <a href="%s">View Update</a>', array($readme, URL . "/update.php"));
}
// The updater contains a version lower than the current Symphony version.
// The updater is the same version as the current Symphony install.
else {
$message = __('Your Symphony installation is up to date, but an updater script was still detected. For security reasons, it should be removed. <a href="%s/update.php?action=remove">Remove Update Script</a>', array(URL));
}
}
// Can't detect update Symphony version
else {
$message = __('An updater script has been found in your installation. <a href="%s">View Update</a>', array(URL . "/update.php"));
}
$this->Page->pageAlert($message, Alert::NOTICE);
}
// Do any extensions need updating?
$extensions = Symphony::ExtensionManager()->listInstalledHandles();
if(is_array($extensions) && !empty($extensions) && $this->__canAccessAlerts()) {
foreach($extensions as $name) {
$about = Symphony::ExtensionManager()->about($name);
if($about['status'] == EXTENSION_REQUIRES_UPDATE) {
$this->Page->pageAlert(
__('An extension requires updating. <a href="%s">View Extensions</a>', array(SYMPHONY_URL . '/system/extensions/'))
);
break;
}
}
}
$this->Page->build($this->_callback['context']);
}
return $this->Page;
}
/**
* This function determines whether an administrative alert can be
* displayed on the current page. It ensures that the page exists,
* and the user is logged in and a developer
*
* @since Symphony 2.2
* @return boolean
*/
private function __canAccessAlerts() {
if($this->Page instanceof AdministrationPage && $this->isLoggedIn() && Administration::instance()->Author->isDeveloper()) {
return true;
}
else {
return false;
}
}
/**
* This function resolves the string of the page to the relevant
* backend page class. The path to the backend page is split on
* the slashes and the resulting pieces used to determine if the page
* is provided by an extension, is a section (index or entry creation)
* or finally a standard Symphony content page. If no page driver can
* be found, this function will return false
*
* @param string $page
* The full path (including the domain) of the Symphony backend page
* @return array|boolean
* If successful, this function will return an associative array that at the
* very least will return the page's classname, pageroot, driver and
* context, otherwise this will return false.
*/
public function getPageCallback($page = null){
if(!$page && $this->_callback) return $this->_callback;
elseif(!$page && !$this->_callback) trigger_error(__('Cannot request a page callback without first specifying the page.'));
$this->_currentPage = URL . preg_replace('/\/{2,}/', '/', '/symphony' . $page);
$bits = preg_split('/\//', trim($page, '/'), 3, PREG_SPLIT_NO_EMPTY);
$callback = array(
'driver' => null,
'context' => null,
'classname' => null,
'pageroot' => null
);
if($bits[0] == 'login'){
$callback = array(
'driver' => 'login',
'context' => preg_split('/\//', $bits[1] . '/' . $bits[2], -1, PREG_SPLIT_NO_EMPTY),
'classname' => 'contentLogin',
'pageroot' => '/login/'
);
}
elseif($bits[0] == 'extension' && isset($bits[1])){
$extension_name = $bits[1];
$bits = preg_split('/\//', trim($bits[2], '/'), 2, PREG_SPLIT_NO_EMPTY);
$callback['driverlocation'] = EXTENSIONS . '/' . $extension_name . '/content/';
$callback['driver'] = 'index';
$callback['classname'] = 'contentExtension' . ucfirst($extension_name) . 'Index';
$callback['pageroot'] = '/extension/' . $extension_name. '/';
if(isset($bits[0])){
$callback['driver'] = $bits[0];
$callback['classname'] = 'contentExtension' . ucfirst($extension_name) . ucfirst($bits[0]);
$callback['pageroot'] .= $bits[0] . '/';
}
if(isset($bits[1])) $callback['context'] = preg_split('/\//', $bits[1], -1, PREG_SPLIT_NO_EMPTY);
if(!is_file($callback['driverlocation'] . '/content.' . $callback['driver'] . '.php')) return false;
}
elseif($bits[0] == 'publish'){
if(!isset($bits[1])) return false;
$callback = array(
'driver' => 'publish',
'context' => array(
'section_handle' => $bits[1],
'page' => null,
'entry_id' => null,
'flag' => null
),
'pageroot' => '/' . $bits[0] . '/' . $bits[1] . '/',
'classname' => 'contentPublish'
);
if(isset($bits[2])){
$extras = preg_split('/\//', $bits[2], -1, PREG_SPLIT_NO_EMPTY);
$callback['context']['page'] = $extras[0];
if(isset($extras[1])) $callback['context']['entry_id'] = intval($extras[1]);
if(isset($extras[2])) $callback['context']['flag'] = $extras[2];
}
else $callback['context']['page'] = 'index';
}
else{
$callback['driver'] = ucfirst($bits[0]);
$callback['pageroot'] = '/' . $bits[0] . '/';
if(isset($bits[1])){
$callback['driver'] = $callback['driver'] . ucfirst($bits[1]);
$callback['pageroot'] .= $bits[1] . '/';
}
if(isset($bits[2])) $callback['context'] = preg_split('/\//', $bits[2], -1, PREG_SPLIT_NO_EMPTY);
$callback['classname'] = 'content' . $callback['driver'];
$callback['driver'] = strtolower($callback['driver']);
if(!is_file(CONTENT . '/content.' . $callback['driver'] . '.php')) return false;
}
## TODO: Add delegate for custom callback creation
return $callback;
}
/**
* Called by index.php, this function is responsible for rendering the current
* page on the Frontend. Two delegates are fired, AdminPagePreGenerate and
* AdminPagePostGenerate. This function runs the Profiler for the page build
* process.
*
* @uses AdminPagePreGenerate
* @uses AdminPagePostGenerate
* @see core.Symphony#__buildPage()
* @see boot.getCurrentPage()
* @param string $page
* The result of getCurrentPage, which returns the $_GET['symphony-page']
* variable.
* @return string
* The HTML of the page to return
*/
public function display($page){
$this->Profiler->sample('Page build process started');
$this->__buildPage($page);
/**
* Immediately before generating the admin page. Provided with the page object
* @delegate AdminPagePreGenerate
* @param string $context
* '/backend/'
* @param HTMLPage $oPage
* An instance of the current page to be rendered, this will usually be a class that
* extends HTMLPage. The Symphony backend uses a convention of contentPageName
* as the class that extends the HTMLPage
*/
$this->ExtensionManager->notifyMembers('AdminPagePreGenerate', '/backend/', array('oPage' => &$this->Page));
$output = $this->Page->generate();
/**
* Immediately after generating the admin page. Provided with string containing page source
* @delegate AdminPagePostGenerate
* @param string $context
* '/backend/'
* @param string $output
* The resulting backend page HTML as a string, passed by reference
*/
$this->ExtensionManager->notifyMembers('AdminPagePostGenerate', '/backend/', array('output' => &$output));
$this->Profiler->sample('Page built');
return $output;
}
/**
* Writes the current Symphony Configuration object to a file in the
* CONFIG directory. This will overwrite any existing configuration
* file every time this function is called.
*
* @see core.Configuration#__toString()
* @return boolean
* True if the Configuration object was successfully written, false otherwise
*/
public function saveConfig(){
$string = "<?php\n\t\$settings = ".(string)self::Configuration().";\n";
return General::writeFile(CONFIG, $string, self::Configuration()->get('write_mode', 'file'));
}
/**
* If a page is not found in the Symphony backend, this function should
* be called which will raise a customError to display the default Symphony
* page not found template
*/
public function errorPageNotFound(){
$this->customError(__('Page Not Found'), __('The page you requested does not exist.'), 'error', array('header' => 'HTTP/1.0 404 Not Found'));
}
}
Jump to Line
Something went wrong with that request. Please try again.