diff --git a/src/classes/Controller.php b/src/classes/Controller.php
deleted file mode 100644
index 015df27..0000000
--- a/src/classes/Controller.php
+++ /dev/null
@@ -1,206 +0,0 @@
-
- * @copyright Copyright 2007-2014, 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 new Controller();
- */
-class Controller extends Object
-{
- /**
- * Constructor
- *
- * To save a few keystrokes, the Controller is executed as part of the
- * constructor instead of via a method. You either want the Controller or
- * you don't.
- */
- public function __construct()
- {
- parent::__construct();
-
- try
- {
- // Catches requests that aren't lowercase
- $lowercase_request = strtolower($_REQUEST['request']);
-
- if ($_REQUEST['request'] != $lowercase_request)
- {
- // @todo Rework the Browser class to handle the 301 (perhaps redirect301()) to not break other code
- header('Location: ' . substr_replace($_SERVER['REQUEST_URI'], $lowercase_request, 1, strlen($lowercase_request)), true, 301);
- throw new Exception();
- }
-
- // Grabs the requested page
- $request = $_REQUEST['request'];
-
- // Loads the module's information
- $module_class = strtr($request, '/', '_');
- $module_filename = SITE_MODULE_PATH . $request . '.php';
- $module_exists = file_exists($module_filename);
-
- // Attempts to instantiate the requested module
- if ($module_exists)
- {
- if (class_exists($module_class))
- {
- $module = new $module_class;
- }
- }
-
- // No module instantiated, load up a generic Module
- if (!isset($module))
- {
- $module = new Module();
- }
-
- // 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'], true, 301);
- throw new Exception();
- }
- elseif ($module->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false))
- {
- header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], true, 301);
- throw new Exception();
- }
-
- // Gets the profiler status
- $profiler = $this->config->pickles['profiler'];
- $profiler = $profiler === true || stripos($profiler, 'timers') !== false;
-
- $default_method = '__default';
- $role_method = null;
-
- // Attempts to execute the default method
- // @todo Seems a bit redundant, refactor
- if ($default_method == $role_method || method_exists($module, $default_method))
- {
- // Starts a timer before the module is executed
- if ($profiler)
- {
- Profiler::timer('module ' . $default_method);
- }
-
- $valid_request = false;
- $error_message = 'An unexpected error has occurred.';
-
- // Determines if the request method is valid for this request
- if ($module->method)
- {
- if (!is_array($module->method))
- {
- $module->method = [$module->method];
- }
-
- foreach ($module->method as $method)
- {
- if ($_SERVER['REQUEST_METHOD'] == $method)
- {
- $valid_request = true;
- break;
- }
- }
-
- if (!$valid_request)
- {
- // @todo Should probably utilize that AJAX flag to determine the type of return
- $error_message = 'There was a problem with your request method.';
- }
- }
- else
- {
- $valid_request = true;
- }
-
- $valid_form_input = true;
-
- if ($valid_request && $module->validate)
- {
- $validation_errors = $module->__validate();
-
- if ($validation_errors)
- {
- $error_message = implode(' ', $validation_errors);
- $valid_form_input = false;
- }
- }
-
- /**
- * 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
- */
- if ($valid_request && $valid_form_input)
- {
- $module_return = $module->$default_method();
-
- if (!is_array($module_return))
- {
- $module_return = $module->response;
- }
- else
- {
- $module_return = array_merge($module_return, $module->response);
- }
- }
-
- // Stops the module timer
- if ($profiler)
- {
- Profiler::timer('module ' . $default_method);
- }
-
- $display = new Display($module);
- }
-
- // Starts a timer for the display rendering
- if ($profiler)
- {
- Profiler::timer('display render');
- }
-
- // Renders the content
- $output = $display->render();
-
- // Stops the display timer
- if ($profiler)
- {
- Profiler::timer('display render');
- }
- }
- catch (Exception $e)
- {
- $output = $e->getMessage();
- }
-
- echo $output;
-
- // Display the Profiler's report if the stars are aligned
- if ($this->config->pickles['profiler'])
- {
- Profiler::report();
- }
- }
-}
-
diff --git a/src/classes/Display.php b/src/classes/Display.php
deleted file mode 100644
index be82a21..0000000
--- a/src/classes/Display.php
+++ /dev/null
@@ -1,96 +0,0 @@
-
- * @copyright Copyright 2007-2014, Josh Sherman
- * @license http://www.opensource.org/licenses/mit-license.html
- * @package PICKLES
- * @link https://github.com/joshtronic/pickles
- */
-
-/**
- * Display Class
- *
- * If you can see it then it probably happened in here.
- */
-class Display extends Object
-{
- /**
- * Module
- *
- * This is the module we are attempting to display output for.
- */
- public $module = null;
-
- public function __construct($module = null)
- {
- if ($module && $module instanceof Module)
- {
- $this->module = $module;
- }
- }
-
- public function render()
- {
- try
- {
- // Starts up the buffer so we can capture it
- ob_start();
-
- if (!is_array($this->module->response))
- {
- $this->module->response = [$this->module->response];
- }
-
- // Checks for the PHPSESSID in the query string
- if (stripos($_SERVER['REQUEST_URI'], '?PHPSESSID=') === false)
- {
- // XHTML compliancy stuff
- // @todo Wonder if this could be yanked now that we're in HTML5 land
- ini_set('arg_separator.output', '&');
- ini_set('url_rewriter.tags', 'a=href,area=href,frame=src,input=src,fieldset=');
-
- header('Content-type: text/html; charset=UTF-8');
- }
- else
- {
- // Redirect so Google knows to index the page without the session ID
- list($request_uri, $phpsessid) = explode('?PHPSESSID=', $_SERVER['REQUEST_URI'], 2);
- header('HTTP/1.1 301 Moved Permanently');
- header('Location: ' . $request_uri);
-
- throw new Exception('Requested URI contains PHPSESSID, redirecting.');
- }
-
- $response = [
- 'meta' => [
- 'status' => $this->module->status,
- 'message' => $this->module->message,
- ],
- ];
-
- if ($this->module->response)
- {
- $response['response'] = $this->module->response;
- }
-
- header('Content-type: application/json');
- $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false;
- echo json_encode($response, $pretty);
-
- return ob_get_clean();
- }
- catch (Exception $e)
- {
- return $e->getMessage();
- }
- }
-}
-
diff --git a/src/classes/Dynamic.php b/src/classes/Dynamic.php
deleted file mode 100644
index 2880abb..0000000
--- a/src/classes/Dynamic.php
+++ /dev/null
@@ -1,99 +0,0 @@
-
- * @copyright Copyright 2007-2014, Josh Sherman
- * @license http://www.opensource.org/licenses/mit-license.html
- * @package PICKLES
- * @link https://github.com/joshtronic/pickles
- */
-
-/**
- * Dynamic Class
- *
- * Handles generating links to static content that are a timestamp injected as
- * to avoid hard caching. Also minifies content where applicable.
- *
- * Note: you will want to add a mod_rewrite line to your .htaccess to support
- * the routing to the filenames with the timestamp injected:
- *
- * RewriteRule ^(.+)\.([\d]+)\.(css|js|gif|png|jpg|jpeg)$ /$1.$3 [NC,QSA]
- */
-class Dynamic extends Object
-{
- /**
- * Generate Reference
- *
- * Appends a dynamic piece of information to the passed reference in the
- * form of a UNIX timestamp added to the query string.
- *
- * @param string $reference URI reference of the file
- * @param string $failover URI reference to use if the reference can't be found
- * @return string URI reference reference with dynamic content
- */
- public function reference($reference, $failover = false)
- {
- // Checks if the URI reference is absolute, and not relative
- if (substr($reference, 0, 1) == '/')
- {
- $query_string = '';
-
- // Checks for ? and extracts query string
- if (strstr($reference, '?'))
- {
- list($reference, $query_string) = explode('?', $reference);
- }
-
- // Adds the dot so the file functions can find the file
- $file = '.' . $reference;
-
- if (file_exists($file))
- {
- // Replaces the extension with time().extension
- $parts = explode('.', $reference);
-
- if (count($parts) == 1)
- {
- throw new Exception('Filename must have an extension (e.g. /path/to/file.png)');
- }
- else
- {
- end($parts);
- $parts[key($parts)] = filemtime($file) . '.' . current($parts);
- $reference = implode('.', $parts);
- }
-
- // Adds the query string back
- if ($query_string != '')
- {
- $reference .= '?' . $query_string;
- }
- }
- else
- {
- if ($failover != false)
- {
- $reference = $failover;
- }
- else
- {
- throw new Exception('Supplied reference does not exist (' . $reference . ')');
- }
- }
- }
- else
- {
- throw new Exception('Reference value must be absolute (e.g. /path/to/file.png)');
- }
-
- return $reference;
- }
-}
-
diff --git a/src/classes/Module.php b/src/classes/Module.php
deleted file mode 100644
index 78ec6af..0000000
--- a/src/classes/Module.php
+++ /dev/null
@@ -1,292 +0,0 @@
-
- * @copyright Copyright 2007-2014, Josh Sherman
- * @license http://www.opensource.org/licenses/mit-license.html
- * @package PICKLES
- * @link https://github.com/joshtronic/pickles
- */
-
-/**
- * Module Class
- *
- * This is a parent class that all PICKLES modules should be extending. Each
- * module can specify it's own meta data and whether or not a user must be
- * properly authenticated to view the page. Currently any pages without a
- * template are treated as pages being requested via AJAX and the return will
- * be JSON encoded. In the future this may need to be changed out for logic
- * that allows the requested module to specify what display type(s) it can use.
- */
-abstract class Module extends Object
-{
- /**
- * Page Title
- *
- * @var string, null by default
- * @todo Abandon for $this->meta
- */
- public $title = null;
-
- /**
- * Meta Description
- *
- * @var string, null by default
- * @todo Abandon for $this->meta
- */
- public $description = null;
-
- /**
- * Meta Keywords (comma separated)
- *
- * @var string, null by default
- * @todo Abandon for $this->meta
- */
- public $keywords = null;
-
- /**
- * Meta Data
- *
- * @var array
- */
- public $meta = [
- 'title' => '',
- 'description' => '',
- 'keywords' => '',
- ];
-
- /**
- * Secure
- *
- * Whether or not the page should be loaded via SSL.
- *
- * @var boolean defaults to false
- */
- public $secure = false;
-
- /**
- * Security Settings
- *
- * @var boolean, null by default
- */
- public $security = null;
-
- /**
- * Method
- *
- * Request methods that are allowed to access the module.
- *
- * @var string or array, null by default
- */
- public $method = null;
-
- /**
- * Validate
- *
- * Variables to validate.
- *
- * @var array
- */
- public $validate = [];
-
- /**
- * Template
- *
- * This is the parent template that will be loaded if you are using the
- * 'template' return type in the Display class. Parent templates are found
- * in ./templates/__shared and use the phtml extension.
- *
- * @var string, 'index' by default
- */
- public $template = 'index';
-
- /**
- * Response
- *
- * Array of data that will be rendered as part of the display. This is
- * somewhat of a one way trip as you cannot get the variable unless you
- * reference the response array explicitly, $this->response['variable']
- *
- * @var array
- */
- public $response = [];
-
- /**
- * Output
- *
- * What should the class render as output? This can be a string or an array
- * containing either 'json', 'rss', 'template' or 'xml'. Default is to use
- * templates and if the template is not present, fall back to JSON.
- *
- * @var mixed string or array
- */
- public $output = ['template', 'json'];
-
- // @todo
- public $status = 200;
- public $message = 'OK';
- public $echo = false;
- public $limit = false;
- public $offset = false;
- public $errors = [];
-
- // @todo if $status != 200 && $message == 'OK' ...
-
- /**
- * Constructor
- *
- * The constructor does nothing by default but can be passed a boolean
- * variable to tell it to automatically run the __default() method. This is
- * typically used when a module is called outside of the scope of the
- * controller (the registration page calls the login page in this manner.
- *
- * @param boolean $autorun optional flag to autorun __default()
- @ @param boolean $filter optional flag to disable autorun filtering
- * @param boolean $valiate optional flag to disable autorun validation
- */
- public function __construct($autorun = false, $filter = true, $validate = true)
- {
- parent::__construct(['cache', 'db']);
-
- if ($autorun)
- {
- if ($filter)
- {
- // @todo
- //$this->__filter();
- }
-
- if ($validate)
- {
- $errors = $this->__validate();
-
- if (!$errors)
- {
- // @todo Fatal error perhaps?
- exit('Errors encountered, this is a @todo for form validation when calling modules from inside of modules');
- }
- }
-
- $this->__default();
- }
- }
-
- /**
- * Default "Magic" Method
- *
- * The __default() method is where you want to place any code that needs to
- * be executed at runtime.
- *
- * @abstract
- */
- abstract public function __default();
-
- /**
- * Magic Setter Method
- *
- * Places undefined properties into the response array as part of the
- * module's payload.
- *
- * @param string $variable name of the variable to be set
- * @param mixed $value value of the variable to be set
- */
- public function __set($variable, $value)
- {
- $this->response[$variable] = $value;
- }
-
- /**
- * Magic Getter Method
- *
- * Any variables not defined in this class are set in the response array
- * and default to false if not defined there.
- *
- * @param string $name name of the variable requested
- * @return mixed value of the variable or boolean false
- */
- public function __get($name)
- {
- if (!isset($this->response[$name]))
- {
- return false;
- }
- else
- {
- return $this->response[$name];
- }
- }
-
- /**
- * Validate
- *
- * Internal validation for data passed to a Module. Grabs the super global
- * based on the Module's request method and loops through the data using the
- * Module's validation array (if present) sanity checking each variable
- * against the rules.
- *
- * @return mixed boolean false if everything is fine or an array or errors
- */
- public function __validate()
- {
- $errors = [];
-
- if ($this->validate)
- {
- if (is_array($this->method))
- {
- $this->method = $this->method[0];
- }
-
- switch (strtoupper($this->method))
- {
- case 'GET':
- $global = &$_GET;
- break;
-
- case 'POST':
- $global = &$_POST;
- break;
-
- default:
- $global = &$_REQUEST;
- break;
- }
-
- foreach ($this->validate as $variable => $rules)
- {
- if (!is_array($rules) && $rules !== true)
- {
- $variable = $rules;
- $rules = true;
- }
-
- if (isset($global[$variable]) && !String::isEmpty($global[$variable]))
- {
- if (is_array($rules))
- {
- $rule_errors = Validate::isValid($global[$variable], $rules);
-
- if (is_array($rule_errors))
- {
- $errors = array_merge($errors, $rule_errors);
- }
- }
- }
- else
- {
- $errors[] = 'The ' . $variable . ' field is required.';
- }
- }
- }
-
- return $errors == [] ? false : $errors;
- }
-}
-
diff --git a/src/classes/Resource.php b/src/classes/Resource.php
new file mode 100644
index 0000000..4723468
--- /dev/null
+++ b/src/classes/Resource.php
@@ -0,0 +1,146 @@
+
+ * @copyright Copyright 2007-2014, Josh Sherman
+ * @license http://www.opensource.org/licenses/mit-license.html
+ * @package PICKLES
+ * @link https://github.com/joshtronic/pickles
+ */
+
+/**
+ * Resource Class
+ *
+ * This is a parent class that all PICKLES modules should be extending. Each
+ * module can specify it's own meta data and whether or not a user must be
+ * properly authenticated to view the page. Currently any pages without a
+ * template are treated as pages being requested via AJAX and the return will
+ * be JSON encoded. In the future this may need to be changed out for logic
+ * that allows the requested module to specify what display type(s) it can use.
+ */
+abstract class Resource extends Object
+{
+ /**
+ * Secure
+ *
+ * Whether or not the page should be loaded via SSL.
+ *
+ * @var boolean defaults to false
+ */
+ public $secure = false;
+
+ /**
+ * Filter
+ *
+ * Variables to filter.
+ *
+ * @var array
+ */
+ public $filter = [];
+
+ /**
+ * Validate
+ *
+ * Variables to validate.
+ *
+ * @var array
+ */
+ public $validate = [];
+
+ // @todo
+ public $status = 200;
+ public $message = 'OK';
+ public $echo = false;
+ public $limit = false;
+ public $offset = false;
+ public $errors = [];
+
+ // @todo if $status != 200 && $message == 'OK' ...
+
+ /**
+ * Constructor
+ *
+ * The constructor does nothing by default but can be passed a boolean
+ * variable to tell it to automatically run the __default() method. This is
+ * typically used when a module is called outside of the scope of the
+ * controller (the registration page calls the login page in this manner.
+ */
+ public function __construct()
+ {
+ parent::__construct(['cache', 'db']);
+ }
+
+ /**
+ * Validate
+ *
+ * Internal validation for data passed to a Module. Grabs the super global
+ * based on the Module's request method and loops through the data using the
+ * Module's validation array (if present) sanity checking each variable
+ * against the rules.
+ *
+ * @return mixed boolean false if everything is fine or an array or errors
+ */
+ public function __validate()
+ {
+ $errors = [];
+
+ if ($this->validate)
+ {
+ if (is_array($this->method))
+ {
+ $this->method = $this->method[0];
+ }
+
+ switch (strtoupper($this->method))
+ {
+ case 'GET':
+ $global = &$_GET;
+ break;
+
+ case 'POST':
+ $global = &$_POST;
+ break;
+
+ default:
+ $global = &$_REQUEST;
+ break;
+ }
+
+ foreach ($this->validate as $variable => $rules)
+ {
+ if (!is_array($rules) && $rules !== true)
+ {
+ $variable = $rules;
+ $rules = true;
+ }
+
+ if (isset($global[$variable]) && !String::isEmpty($global[$variable]))
+ {
+ if (is_array($rules))
+ {
+ $rule_errors = Validate::isValid($global[$variable], $rules);
+
+ if (is_array($rule_errors))
+ {
+ $errors = array_merge($errors, $rule_errors);
+ }
+ }
+ }
+ else
+ {
+ $errors[] = 'The ' . $variable . ' field is required.';
+ }
+ }
+ }
+
+ return $errors == [] ? false : $errors;
+ }
+}
+
diff --git a/src/classes/Response.php b/src/classes/Response.php
new file mode 100644
index 0000000..cea8f56
--- /dev/null
+++ b/src/classes/Response.php
@@ -0,0 +1,46 @@
+ $this->status,
+ 'message' => $this->message,
+ ];
+
+ foreach (['echo', 'limit', 'offset', 'errors'] as $variable)
+ {
+ if ($this->$variable)
+ {
+ $meta[$variable] = $this->$variable;
+ }
+ }
+
+ $response = ['meta' => $meta];
+
+ foreach (['response', 'profiler'] as $variable)
+ {
+ if ($this->$variable)
+ {
+ $response[$variable] = $this->$variable;
+ }
+ }
+
+ $pretty = isset($_REQUEST['pretty']) ? JSON_PRETTY_PRINT : false;
+
+ exit(json_encode($response, $pretty));
+ }
+}
+
diff --git a/src/classes/Router.php b/src/classes/Router.php
new file mode 100644
index 0000000..52c97db
--- /dev/null
+++ b/src/classes/Router.php
@@ -0,0 +1,148 @@
+
+ * @copyright Copyright 2007-2014, Josh Sherman
+ * @license http://www.opensource.org/licenses/mit-license.html
+ * @package PICKLES
+ * @link https://github.com/joshtronic/pickles
+ */
+
+/**
+ * Router 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 new Router();
+ */
+class Router extends Object
+{
+ /**
+ * Constructor
+ *
+ * To save a few keystrokes, the Controller is executed as part of the
+ * constructor instead of via a method. You either want the Controller or
+ * you don't.
+ */
+ public function __construct()
+ {
+ parent::__construct();
+
+ $response = new Response();
+
+ try
+ {
+ // Grabs the requested page
+ $request = $_REQUEST['request'];
+ $components = explode('/', $request);
+ $version = array_shift($components);
+ $nouns = [];
+ $uids = [];
+
+ // Loops through the components to determine nouns and IDs
+ foreach ($components as $index => $component)
+ {
+ if ($index % 2)
+ {
+ $uids[end($nouns)] = $component;
+ }
+ else
+ {
+ $nouns[] = $component;
+ }
+ }
+
+ // Creates our class name
+ array_unshift($nouns, $version);
+ $class = implode('_', $nouns);
+
+ // Creates our filename
+ array_unshift($nouns, SITE_MODULE_PATH);
+ $filename = implode('/', $nouns) . '.php';
+
+ if (!file_exists($filename))
+ {
+ throw new Exception('Cannot find the file ' . $filename);
+ }
+
+ if (!class_exists($class))
+ {
+ throw new Exception('Cannot find the class ' . $class);
+ }
+
+ $resource = new $class($uids);
+
+ // Determines if we need to serve over HTTP or HTTPS
+ if ($resource->secure == false && isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])
+ {
+ throw new Exception('This resource expects HTTPS communication.');
+ }
+ elseif ($resource->secure == true && (!isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == false))
+ {
+ throw new Exception('This resource expects HTTP communication.');
+ }
+
+ // Gets the profiler status
+ $profiler = $this->config->pickles['profiler'];
+ $profiler = $profiler === true || stripos($profiler, 'timers') !== false;
+
+ $method = strtolower($_SERVER['REQUEST_METHOD']);
+
+ if (!method_exists($resource, $method))
+ {
+ throw new Exception('Cannot find the method ' . $class . '::' . $method);
+ }
+
+ // Starts a timer before the resource is executed
+ if ($profiler)
+ {
+ Profiler::timer('resource ' . $method);
+ }
+
+ if ($resource->validate)
+ {
+ $validation_errors = $resource->__validate();
+
+ if ($validation_errors)
+ {
+ $response->status = 400;
+ $response->message = implode(' ', $validation_errors);
+ }
+ }
+
+ if ($response->status == 200)
+ {
+ $resource_return = $resource->$method();
+
+ if ($resource_return)
+ {
+ $response->response = $resource_return;
+ }
+ }
+
+ // Stops the resource timer
+ if ($profiler)
+ {
+ Profiler::timer('resource ' . $method);
+ }
+ }
+ catch (Exception $e)
+ {
+ $response->status = 500;
+ $response->message = $e->getMessage();
+ }
+
+ $response->respond();
+ }
+}
+