pickles/classes/Controller.php
Josh Sherman 3c2c936cee Added fluid variable to the Module class
As a developer you still need to code for it (it's shown at work in the bootstrap) but you can use the variable to toggle fluid and fixed right from the module itself
2012-10-14 17:53:23 -04:00

449 lines
12 KiB
PHP

<?php
/**
* Single Entry Controller
*
* PHP version 5
*
* Licensed under The MIT License
* Redistribution of these files must retain the above copyright notice.
*
* @author Josh Sherman <pickles@joshtronic.com>
* @copyright Copyright 2007-2012, Josh Sherman
* @license http://www.opensource.org/licenses/mit-license.html
* @package PICKLES
* @link https://github.com/joshtronic/pickles
*/
/**
* Controller Class
*
* The heavy lifter of PICKLES, makes the calls to get the session and
* configuration loaded. Loads modules, serves up user authentication when the
* module asks for it, and loads the viewer that the module requested. Default
* values are present to make things easier on the user.
*
* @usage <code>new Controller($config);</code>
*/
class Controller extends Object
{
/**
* Pass Thru
*
* Whether or not the page being loaded is simple a pass thru for an
* internal PICKLES file. The point of this variable is to suppress the
* profiler report in the destructor.
*
* @access private
* @var boolean
*/
private $passthru = false;
/**
* Constructor
*
* To make life a bit easier when using PICKLES, the Controller logic is
* executed automatically via use of a constructor.
*/
public function __construct()
{
parent::__construct();
// Generate a generic "site down" message if the site is set to be disabled
if (isset($this->config->pickles['disabled']) && $this->config->pickles['disabled'] == true)
{
Error::fatal($_SERVER['SERVER_NAME'] . ' is currently<br />down for maintenance');
}
$_REQUEST['request'] = trim($_REQUEST['request']);
// Checks the passed request for validity
if ($_REQUEST['request'])
{
// Catches requests that aren't lowercase
$lowercase_request = strtolower($_REQUEST['request']);
if ($_REQUEST['request'] != $lowercase_request)
{
header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301);
exit;
}
$request = $_REQUEST['request'];
}
// Loads the default module information if we don't have a valid request
else
{
$request = isset($this->config->pickles['module']) ? $this->config->pickles['module'] : 'home';
}
// Loads the module's information
list($module_class, $module_filename, $template_basename, $css_class, $js_basename) = $this->prepareVariables($request);
unset($request);
$module_exists = (isset($module_filename) && $module_filename != null && file_exists($module_filename));
// Instantiates an instance of the module
if ($module_exists)
{
require_once $module_filename;
// Checks that our class exists
if (class_exists($module_class))
{
$module = new $module_class;
}
else
{
if ($this->config->pickles['logging'] === true)
{
Log::warning('Class named ' . $module_class . ' was not found in ' . $module_filename);
}
}
}
// If a new module object wasn't created, create a generic one
if (!isset($module))
{
$module = new Module();
}
// Determines if the module is private and should be, well, private
if ($module->private == true)
{
header('Location: /');
exit;
}
// Determines if we need to serve over HTTP or HTTPS
if ($module->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])
{
header('Location: http://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false))
{
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit;
}
// Establishes the session
if (ini_get('session.auto_start') == 0)
{
if ($module->session)
{
if (session_id() == '')
{
session_start();
}
}
}
// Validates security level
if ($module->security !== false)
{
$is_authenticated = false;
if (is_array($module->security))
{
$module_security = $module->security;
$security_check_class = 'isLevel';
// Checks the type and validates it
if (isset($module_security['type']))
{
$security_check_type = strtoupper($module_security['type']);
if (in_array($security_check_type, array('IS', 'HAS', 'BETWEEN')))
{
$security_check_class = $security_check_type;
}
unset($security_check_type, $module_security['type']);
}
$module_security_levels = array();
// If there's a level(s) key use it
foreach (array('level', 'levels') as $security_level_key)
{
if (isset($module_security[$security_level_key]))
{
if (is_array($module_security[$security_level_key]))
{
array_merge($module_security_levels, $module_security[$security_level_key]);
}
else
{
$module_security_levels[] = $module_security[$security_level_key];
}
unset($module_security[$security_level_key]);
}
}
// Assume everything left in the array is a level and add it to the array
array_merge($module_security_levels, $module_security);
$security_level_count = count($module_security_levels);
switch ($security_check_class)
{
case 'BETWEEN':
if ($security_level_count >= 2)
{
$is_authenticated = Security::betweenLevel($module_security_levels[0], array_pop($module_security_levels));
}
break;
case 'HAS':
if ($security_level_count > 0)
{
$is_authenticated = Security::hasLevel($module_security_levels);
}
break;
case 'IS':
if ($security_level_count > 0)
{
$is_authenticated = Security::isLevel($module_security_levels);
}
break;
}
}
else
{
$is_authenticated = Security::isLevel($module->security);
}
if ($is_authenticated == false)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST')
{
exit('{ "status": "error", "message": "You are not properly authenticated" }');
}
else
{
// Sets variable for the destination
$_SESSION['__pickles']['login']['destination'] = $_REQUEST['request'] ? $_REQUEST['request'] : '/';
// Redirect to login page, potentially configured in the config, else /login
header('Location: /' . (isset($this->config->security['login']) ? $this->config->security['login'] : 'login'));
exit;
}
}
}
// Validates the rendering engine
$engines = is_array($module->engine) ? array_values($module->engine) : array($module->engine);
$engines = array_combine($engines, $engines);
$engine = current($engines);
// Possibly overrides the engine with the passed return type
if (isset($return_type))
{
$return_type = strtoupper($return_type);
// Validates the return type against the module
if (in_array($return_type, array('JSON', 'RSS', 'XML')) && in_array($return_type, $engines))
{
$engine = $return_type;
}
unset($return_type);
}
// Starts up the display engine
$display_class = 'Display_' . $engine;
$display = new $display_class();
// Assigns the template / template variables
$display->setTemplateVariables($module->template, $template_basename, $css_class, $js_basename, $module->fluid);
// Checks the templates
$template_exists = $display->templateExists();
// If there is no valid module or template, then redirect
if (!$module_exists && !$template_exists)
{
if (!$_REQUEST['request'])
{
Error::fatal('Way to go, you\'ve successfully created an infinite redirect loop. Good thing I was here or you would have been served with a pretty ugly browser error.<br /><br />So here\'s the deal, no templates were able to be loaded. Make sure your parent and child templates actually exist and if you\'re using non-default values, make sure they\'re defined correctly in your config.');
}
else
{
$redirect_url = '/';
if (isset($this->config->pickles['404']) && $_REQUEST['request'] != $this->config->pickles['404'])
{
$redirect_url .= $this->config->pickles['404'];
}
header('Location: ' . $redirect_url, 404);
exit;
}
}
// Gets the profiler status
$profiler = $this->config->pickles['profiler'];
// Attempts to execute the default method
if (method_exists($module, '__default'))
{
if (isset($requested_id))
{
$module->setRequest(array('id' => $requested_id));
}
// Starts a timer before the module is executed
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('module __default');
}
$valid_request = false;
$valid_security_hash = false;
$error_message = 'An unexpected error has occurred';
// Determines if the request method is valid for this request
if ($module->method != false)
{
$methods = (is_array($module->method) ? $module->method : array($module->method));
$request_method = $_SERVER['REQUEST_METHOD'];
foreach ($methods as $method)
{
if ($request_method == strtoupper($method))
{
$valid_request = true;
break;
}
}
if ($valid_request == false)
{
$error_message = 'There was a problem with your request method';
}
unset($methods, $request_method, $method);
}
else
{
$valid_request = true;
}
// Validates the hash if applicable
if ($module->hash != false)
{
if (isset($_REQUEST['security_hash']))
{
$hash_value = ($module->hash === true ? get_class($module) : $module->hash);
if (Security::generateHash($hash_value) == $_REQUEST['security_hash'])
{
$valid_security_hash = true;
}
else
{
$error_message = 'Invalid security hash';
}
unset($hash_value);
}
else
{
$error_message = 'Missing security hash';
}
}
else
{
$valid_security_hash = true;
}
/**
* Note to Self: When building in caching will need to let the
* module know to use the cache, either passing in a variable
* or setting it on the object
*/
$display->setModuleReturn($valid_request && $valid_security_hash ? $module->__default() : array('status' => 'error', 'message' => $error_message));
unset($error_message);
// Stops the module timer
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('module __default');
}
// Sets meta data from the module
$display->setMetaData(array(
'title' => $module->title,
'description' => $module->description,
'keywords' => $module->keywords
));
}
// Starts a timer for the display rendering
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('display render');
}
// Renders the content
$display->render();
// Steps the display timer
if ($profiler === true || stripos($profiler, 'timers') !== false)
{
Profiler::timer('display render');
}
}
/**
* Destructor
*
* Dumps out the Profiler's report if applicable.
*/
public function __destruct()
{
parent::__destruct();
// Display the Profiler's report is the stars are aligned
if ($this->config->pickles['profiler'] != false && $this->passthru == false)
{
Profiler::report();
}
}
/**
* Prepare Variables
*
* Processes the request variable and creates all the variables that the
* Controller needs to load the page.
*
* @param string $basename the requested page
* @return array the resulting variables
*/
public function prepareVariables($basename)
{
// Sets up all of our variables
$module_class = strtr($basename, '/', '_');
$module_filename = SITE_MODULE_PATH . $basename . '.php';
$template_basename = $basename;
$css_class = $module_class;
$js_basename = $basename;
// Scrubs class names with hyphens
if (strpos($module_class, '-') !== false)
{
$module_class = preg_replace('/(-(.{1}))/e', 'strtoupper("$2")', $module_class);
}
return array($module_class, $module_filename, $template_basename, $css_class, $js_basename);
}
}
?>