Source for file AlphaController.inc
Documentation is available at AlphaController.inc
require_once $config->get('sysRoot'). 'alpha/util/AlphaErrorHandlers.inc';
require_once $config->get('sysRoot'). 'alpha/controller/front/FrontController.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Date.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Timestamp.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Double.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Integer.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/String.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Text.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Enum.inc';
require_once $config->get('sysRoot'). 'alpha/model/types/Boolean.inc';
require_once $config->get('sysRoot'). 'alpha/model/PersonObject.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FailedUnitCommitException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/SecurityException.inc';
require_once $config->get('sysRoot'). 'alpha/exceptions/FileNotFoundException.inc';
require_once $config->get('sysRoot'). 'alpha/util/helpers/AlphaValidator.inc';
* The master controller class for the Alpha Framework.
* @package alpha::controller
* @author John Collins <dev@alphaframework.org>
* @version $Id: AlphaController.inc 1341 2011-03-17 15:02:02Z johnc $
* @license http://www.opensource.org/licenses/bsd-license.php The BSD License
* @copyright Copyright (c) 2011, John Collins (founder of Alpha Framework).
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the
* following conditions are met:
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
* * Neither the name of the Alpha Framework nor the names
* of its contributors may be used to endorse or promote
* products derived from this software without specific
* prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* The name of the controller
* Used to set access privileages for the controller to the name of the rights group
* allowed to access it. 'Public' by default.
* Optionally, a BO may be set for the default validation form handling code to load in the displayPageHead()
* method. The definition of this BO class will need to be included in the child controller.
* Used to determine if the controller is part of a unit of work sequence
* (either empty or the name of the unit).
* Stores the start time of a unit of work transaction.
* Stores the end time of a unit of work transaction.
* Stores the maximum allowed time duration (in seconds) of the unit of work.
* The name of the first controller that is used in this unit of work.
* The name of the next controller that is used in this unit of work.
* The name of the previous controller that is used in this unit of work.
* The name of the last controller that is used in this unit of work.
* An array for storing dirty objects in a session (i.e. persistent business
* objects that have not been updated in the database yet).
* An array for storing new objects in a session (transient business objects that
* The title to be displayed on the controller page
* Meta keywords for the controller page, generally populated from tags
* Meta description for the controller page.
* Used to set status update messages to display to the user (messages stored between requests
* in _SESSION). Useful for when you want to display a message to a user after POSTing a request,
* or when moving from one page to the next.
private static $logger = null;
* Constructor for the AlphaController that starts a new session if required, and handles
* the population of new/dirty objects from the session when available. Accepts the name
* of the rights group that has access to this controller, 'Public' by default.
* @param string $visibility The name of the rights group that can access this controller.
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>__construct(visibility=['. $visibility. '])');
// kick off new session, or reuse existing one
// set the access rights to the group name indicated
// check the current user's rights on access to the page controller
// no more execution should take place
// if configured to do so, force redirect to the front controller
if($config->get('sysForceFC') && basename($_SERVER['PHP_SELF']) != 'index.php') {
// set the correct HTTP header for the response
header('HTTP/1.1 301 Moved Permanently');
if(empty($_SERVER['QUERY_STRING'])) {
self::$logger->debug('<<__construct');
header('Location: '. $config->get('sysURL'). '?act='. get_class($this). '&'. $_SERVER['QUERY_STRING']);
self::$logger->debug('<<__construct');
self::$logger->debug('<<__construct');
// uses controller class name as the job name
if(isset ($_SESSION['unitOfWork']) && is_array($_SESSION['unitOfWork']))
if(isset ($_SESSION['dirtyObjects']) && is_array($_SESSION['dirtyObjects']))
if(isset ($_SESSION['newObjects']) && is_array($_SESSION['newObjects']))
if(isset ($_SESSION['statusMessage']))
self::$logger->debug('<<__construct');
* Get the BO for this controller (if any).
public function getBO() {
self::$logger->debug('>>getBO()');
self::$logger->debug('<<getBO ['. var_export($this->BO, true). ']');
* Setter for the BO for this controller.
public function setBO($BO) {
self::$logger->debug('>>setBO(BO=['. var_export($BO, true). '])');
// if the BO has tags, use these as the meta keywords for this controller
if($this->BO->isTagged()) {
$tags = $this->BO->getPropObject('tags')->getRelatedObjects();
$keywords .= ','. $tag->get('content');
self::$logger->debug('<<setBO');
* Get the name of the unit of work job.
self::$logger->debug('>>getName()');
self::$logger->debug('<<getName ['. $this->name. ']');
* Setter for the unit of work job name.
self::$logger->debug('>>setName(name=['. $name. '])');
self::$logger->debug('<<setName');
* Get the name of the rights group that has access to this controller.
self::$logger->debug('>>getVisibility()');
self::$logger->debug('<<getVisibility ['. $this->visibility. ']');
* Setter for the name of the rights group that has access to this controller.
* @param string $visibility
self::$logger->debug('>>setVisibility(visibility=['. $visibility. '])');
self::$logger->debug('<<setVisibility');
* Gets the name of the first job in this unit of work.
self::$logger->debug('>>getFirstJob()');
self::$logger->debug('<<getFirstJob ['. $this->firstJob. ']');
* Gets the name of the next job in this unit of work
self::$logger->debug('>>getNextJob()');
self::$logger->debug('<<getNextJob ['. $this->nextJob. ']');
* Gets the name of the previous job in this unit of work
self::$logger->debug('>>getPreviousJob()');
self::$logger->debug('<<getPreviousJob ['. $this->previousJob. ']');
* Gets the name of the last job in this unit of work.
self::$logger->debug('>>getLastJob()');
self::$logger->debug('<<getLastJob ['. $this->lastJob. ']');
* Sets the name of the controller job sequence to the values in the supplied
* array (and stores the array in the session).
* @param array $jobs The names of the controllers in this unit of work sequence.
* @throws IllegalArguementException
self::$logger->debug('>>setUnitOfWork(jobs=['. var_export($jobs, true). '])');
$this->before_setUnitOfWork_callback();
self::$logger->debug('<<setUnitOfWork');
// validate that each controller name in the array actually exists
if(!AlphaController::checkControllerDefExists($job))
// clear out any previous unit of work from the session
$_SESSION['unitOfWork'] = null;
$numOfJobs = count($jobs);
for($i= 0; $i< $numOfJobs; $i++ ) {
// the first job in the sequence
self::$logger->debug('First job ['. $this->firstJob. ']');
if($this->name == $jobs[$i]) {
// set the previous job if it exists
self::$logger->debug('Previous job ['. $this->previousJob. ']');
// set the next job if it exists
self::$logger->debug('Next job ['. $this->nextJob. ']');
// the last job in the sequence
$_SESSION['unitOfWork'] = $jobs;
if(method_exists($this, 'after_setUnitOfWork_callback'))
$this->after_setUnitOfWork_callback();
self::$logger->debug('<<setUnitOfWork');
* Getter for the unit start time.
self::$logger->debug('>>getStartTime()');
self::$logger->debug('<<getStartTime ['. $this->unitStartTime. ']');
* Setter for the unit start time (value will be stored in the session as key unitStartTime).
public function setUnitStartTime($year, $month, $day, $hour, $minute, $second) {
self::$logger->debug('>>setUnitStartTime(year=['. $year. '], month=['. $month. '], day=['. $day. '], hour=['. $hour. '], minute=['. $minute. '],
$this->unitStartTime->setTimestampValue($year, $month, $day, $hour, $minute, $second);
self::$logger->debug('<<setUnitStartTime');
* Getter for the unit end time.
self::$logger->debug('>>getEndTime()');
self::$logger->debug('<<getEndTime ['. $this->unitEndTime. ']');
* Setter for the unit end time (value will be stored in the session as key unitEndTime).
public function setUnitEndTime($year, $month, $day, $hour, $minute, $second) {
self::$logger->debug('>>setUnitEndTime(year=['. $year. '], month=['. $month. '], day=['. $day. '], hour=['. $hour. '], minute=['. $minute. '],
$this->unitEndTime->setTimestampValue($year, $month, $day, $hour, $minute, $second);
$_SESSION['unitEndTime'] = $this->unitEndTime->getValue();
self::$logger->debug('<<setUnitEndTime');
* Getter for the unit of work MAX duration.
self::$logger->debug('>>getMAXDuration()');
* Setter for the unit MAX duration.
* @param integer $duration The desired duration in seconds.
self::$logger->debug('>>setUnitMAXDuration(duration=['. $duration. '])');
self::$logger->debug('<<setUnitMAXDuration');
* Calculates and returns the unit of work current duration in seconds.
self::$logger->debug('>>getUnitDuration()');
self::$logger->debug('<<getUnitDuration ['. $intEndTime- $intStartTime. ']');
return $intEndTime- $intStartTime;
* Adds the supplied business object to the dirtyObjects array in the session.
* @param AlphaDAO $object
self::$logger->debug('>>markDirty(object=['. var_export($object, true). '])');
$this->before_markDirty_callback();
$this->after_markDirty_callback();
self::$logger->debug('<<markDirty');
* Getter for the dirty objects array.
self::$logger->debug('>>getDirtyObjects()');
self::$logger->debug('<<getDirtyObjects ['. var_export($this->dirtyObjects, true). ']');
* Adds a newly created business object to the newObjects array in the session.
* @param AlphaDAO $object
self::$logger->debug('>>markNew(object=['. var_export($object, true). '])');
$this->before_markNew_callback();
$this->after_markNew_callback();
self::$logger->debug('<<markNew');
* Getter for the new objects array.
self::$logger->debug('>>getNewObjects()');
self::$logger->debug('<<getNewObjects ['. var_export($this->newObjects, true). ']');
* Commits (saves) all of the new and modified (dirty) objects in the unit of work to the database.
* @throws FailedUnitCommitException
self::$logger->debug('>>commit()');
if(method_exists($this, 'before_commit_callback'))
$this->before_commit_callback();
$count = count($newObjects);
for ($i = 0; $i < $count; $i++ ) {
self::$logger->error('Failed to save new object of type ['. get_class($newObjects[$i]). '], aborting...');
self::$logger->error('Failed to save new object of type ['. get_class($newObjects[$i]). '], aborting...');
$count = count($dirtyObjects);
for ($i = 0; $i < $count; $i++ ) {
$dirtyObjects[$i]->save();
self::$logger->error('Failed to save OID ['. $dirtyObjects[$i]->getID(). '] of type ['. get_class($dirtyObjects[$i]). '], aborting...');
self::$logger->error('Failed to save OID ['. $dirtyObjects[$i]->getID(). '] of type ['. get_class($dirtyObjects[$i]). '], aborting...');
$this->clearUnitOfWorkAttributes();
$this->after_commit_callback();
self::$logger->debug('<<commit');
}catch (FailedSaveException $e) {
self::$logger->debug('<<commit');
* Method to clearup a cancelled unit of work.
public function abort() {
self::$logger->debug('>>abort()');
if(method_exists($this, 'before_abort_callback'))
$this->before_abort_callback();
$this->clearUnitOfWorkAttributes();
$this->after_abort_callback();
self::$logger->debug('<<abort');
}catch (AlphaException $e) {
throw new AlphaException('Failed to rollback the transaction, error is ['. $e->getMessage(). ']');
self::$logger->debug('<<abort');
* Clears the session and object attributes related to unit of work sessions
private function clearUnitOfWorkAttributes() {
$_SESSION['unitOfWork'] = null;
$_SESSION['dirtyObjects'] = null;
$_SESSION['newObjects'] = null;
* Getter for the page title.
self::$logger->debug('>>getTitle()');
self::$logger->debug('<<getTitle ['. $this->title. ']');
* Setter for the page title.
self::$logger->debug('>>setTitle(title=['. $title. '])');
self::$logger->debug('<<setTitle');
* Getter for the page description.
self::$logger->debug('>>getDescription()');
self::$logger->debug('<<getDescription ['. $this->description. ']');
* Setter for the page description.
* @param string $description
self::$logger->debug('>>setDescription(description=['. $description. '])');
self::$logger->debug('<<setDescription');
* Getter for the page keywords.
self::$logger->debug('>>getKeywords()');
self::$logger->debug('<<getKeywords ['. $this->keywords. ']');
* Setter for the page keywords, should pass a comma-seperated list as a string.
* @param string $keywords
self::$logger->debug('>>setKeywords(keywords=['. $keywords. '])');
self::$logger->debug('<<setKeywords');
* Method to display an access error for trespassing users. HTTP response header code will be 403.
self::$logger->debug('>>accessError()');
if(method_exists($this, 'before_accessError_callback'))
$this->before_accessError_callback();
if(isset ($_SESSION['currentUser']))
self::$logger->warn('The user ['. $_SESSION['currentUser']->get('email'). '] attempted to access the resource ['. $_SERVER['REQUEST_URI']. '] but was denied due to insufficient rights');
self::$logger->warn('An unknown user attempted to access the resource ['. $_SERVER['REQUEST_URI']. '] but was denied due to insufficient rights');
header('HTTP/1.1 403 Forbidden');
echo AlphaView::renderErrorPage(403, 'You do not have the correct access rights to view this page. If you have not logged in yet, try going back to the home page and logging in from there.');
$this->after_accessError_callback();
self::$logger->debug('<<accessError');
* Checks the user rights of the currently logged-in person against the page
* visibility set for this controller. Will return false if the user has
* not got the correct rights.
self::$logger->debug('>>checkRights()');
if(method_exists($this, 'before_checkRights_callback'))
$this->before_checkRights_callback();
// firstly if the page is Public then there is no issue
$this->after_checkRights_callback();
self::$logger->debug('<<checkRights [true]');
// the person is logged in?
if (isset ($_SESSION['currentUser'])) {
// checking for admins (can access everything)
if ($_SESSION['currentUser']->inGroup('Admin')) {
$this->after_checkRights_callback();
self::$logger->debug('<<checkRights [true]');
} elseif ($_SESSION['currentUser']->inGroup($this->getVisibility())) {
$this->after_checkRights_callback();
self::$logger->debug('<<checkRights [true]');
// the person is editing their own profile which is allowed
} elseif (get_class($this->BO) == 'PersonObject' && $_SESSION['currentUser']->getDisplayName() == $this->BO->getDisplayName()) {
$this->after_checkRights_callback();
self::$logger->debug('<<checkRights [true]');
self::$logger->debug('<<checkRights [false]');
}else{ // the person is NOT logged in
self::$logger->debug('<<checkRights [false]');
* Method to check the validity of the two hidden form security
* fields which aim to ensure that a post to the controller is being sent from
* the same server that is hosting it.
if(self::$logger == null)
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>checkSecurityFields()');
// the server hostname + today's date
$var1 = md5($_SERVER['HTTP_HOST']. date("Ymd"));
// the server's IP plus $var1
$var2 = md5($_SERVER['REMOTE_ADDR']. $var1);
if(empty($_REQUEST['var1']) || empty($_REQUEST['var2'])) {
self::$logger->warn('The required var1/var2 params where not provided on the HTTP request');
self::$logger->debug('<<checkSecurityFields [false]');
if ($var1 == $_REQUEST['var1'] && $var2 == $_REQUEST['var2']) {
self::$logger->debug('<<checkSecurityFields [true]');
* Here we are implementing a "grace period" of one hour if the time is < 1:00AM, we will accept
* a match for yesterday's date in the security fields
// the server hostname + today's date less 1 hour (i.e. yesterday where time is < 1:00AM)
$var1 = md5($_SERVER['HTTP_HOST']. date("Ymd", (time()- 3600)));
// the server's IP plus $var1
$var2 = md5($_SERVER['REMOTE_ADDR']. $var1);
if ($var1 == $_REQUEST['var1'] && $var2 == $_REQUEST['var2']) {
self::$logger->debug('<<checkSecurityFields [true]');
self::$logger->warn('The var1/var2 params provided are invalid, values: var1=['. $_REQUEST['var1']. '] var2=['. $_REQUEST['var2']. ']');
self::$logger->debug('<<checkSecurityFields [false]');
* Generates the two security fields to prevent remote form processing.
* @return array An array containing the two fields
if(self::$logger == null)
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>generateSecurityFields()');
// the server hostname + today's date
$var1 = md5($_SERVER['HTTP_HOST']. date("Ymd"));
// the server's IP plus $var1
$var2 = md5($_SERVER['REMOTE_ADDR']. $var1);
self::$logger->debug('<<generateSecurityFields [array('. $var1. ', '. $var2. ')]');
return array($var1, $var2);
* Returns the name of a custom controller if one is found, otherwise returns null.
* @param string $BOName The classname of the business object
* @param string $mode The mode of the controller (create, view, edit)
if(self::$logger == null)
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>getCustomControllerName(BOName=['. $BOName. '], mode=['. $mode. '])');
// strip the Object part from the class name
$BOName = substr($BOName, 0, strpos($BOName, 'Object'));
// uppercase the first letter of each word, e.g. create cart becomes Create Cart
$controllerName = ucwords($mode. ' '. $BOName);
$controllerName = str_replace(' ', '', $controllerName);
self::$logger->debug('Custom controller name is ['. $controllerName. ']');
if (file_exists($config->get('sysRoot'). 'controller/'. $controllerName. '.php')) {
self::$logger->debug('<<getCustomControllerName');
}elseif (file_exists($config->get('sysRoot'). 'alpha/controller/'. $controllerName. '.php')) {
self::$logger->debug('<<getCustomControllerName');
self::$logger->debug('<<getCustomControllerName');
* Does a HTTP redirect to a custom controller if one is found.
* @param string $BOName The classname of the business object
* @param string $mode The mode of the controller (create, view, edit)
* @throws FileNotFoundException
self::$logger->debug('>>loadCustomController(BOName=['. $BOName. '], mode=['. $mode. '])');
// strip the Object part from the class name
$BOName = substr($BOName, 0, strpos($BOName, 'Object'));
// uppercase the first letter of each word, e.g. create cart becomes Create Cart
$controllerName = ucwords($mode. ' '. $BOName);
$controllerName = str_replace(' ', '', $controllerName);
self::$logger->debug('Custom controller name is ['. $controllerName. ']');
// just making sure that we are not already using the custom controller
if(get_class($this) != $controllerName) {
if (file_exists($config->get('sysRoot'). 'controller/'. $controllerName. '.php')) {
self::$logger->debug('Custom controller found, redirecting...');
$params = FrontController::decodeQueryParams($_GET['tk']);
$params = preg_replace('/act=.*\&/', 'act='. $controllerName. '&', $params);
self::$logger->debug('Params are ['. $params. ']');
$url = FrontController::generateSecureURL($params);
self::$logger->debug('Redirecting to ['. $url. ']');
header('Location: '. $url);
self::$logger->debug('<<loadCustomController');
$url = $config->get('sysURL'). 'controller/'. $controllerName. '.php?'. $_SERVER['QUERY_STRING'];
self::$logger->debug('Redirecting to ['. $url. ']');
header('Location: '. $url);
self::$logger->debug('<<loadCustomController');
}elseif (file_exists($config->get('sysRoot'). 'alpha/controller/'. $controllerName. '.php')) {
self::$logger->debug('Custom controller found, redirecting...');
if(self::checkIfAccessingFromSecureURL()) {
$start = strpos($_SERVER['REQUEST_URI'], '/tk/')+ 3;
$end = strlen($_SERVER['REQUEST_URI']);
$tk = substr($_SERVER['REQUEST_URI'], $start+ 1, $end- ($start+ 1));
$params = preg_replace('/act=.*\&/', 'act='. $controllerName. '&', $params);
self::$logger->debug('Params are ['. $params. ']');
$url = FrontController::generateSecureURL($params);
self::$logger->debug('Redirecting to ['. $url. ']');
header('Location: '. $url);
self::$logger->debug('<<loadCustomController');
$url = $config->get('sysURL'). 'alpha/controller/'. $controllerName. '.php?'. $_SERVER['QUERY_STRING'];
self::$logger->debug('Redirecting to ['. $url. ']');
header('Location: '. $url);
self::$logger->debug('<<loadCustomController');
// throw an exception if we have gotten this far and no custom controller was found
throw new FileNotFoundException('The controller ['. $controllerName. '] could not be loaded as it does not exist');
self::$logger->debug('<<loadCustomController');
* Set the status message in the _SESSION to the value provided.
$_SESSION['statusMessage'] = $message;
* Gets the current status message for this controller. Note that by getting the current
* status message, you clear out the value stored in _SESSION so this method can only be used
* to get the status message once for display purposes.
$_SESSION['statusMessage'] = null;
* Checks that the definition for the controller classname provided exists. Will also return true
* if you pass "/" for the root of the web application.
* @param string $controllerName
if(self::$logger == null)
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>checkControllerDefExists(controllerName=['. $controllerName. '])');
if($controllerName == '/')
if(file_exists($config->get('sysRoot'). 'controller/'. $controllerName. '.php'))
if(file_exists($config->get('sysRoot'). 'alpha/controller/'. $controllerName. '.php'))
self::$logger->debug('<<checkControllerDefExists ['. $exists. ']');
* Loads the definition for the controller classname provided.
* @param string $controllerName
* @throws IllegalArguementException
if(self::$logger == null)
self::$logger = new Logger('AlphaController');
self::$logger->debug('>>loadControllerDef(controllerName=['. $controllerName. '])');
if(file_exists($config->get('sysRoot'). 'controller/'. $controllerName. '.php'))
require_once $config->get('sysRoot'). 'controller/'. $controllerName. '.php';
elseif(file_exists($config->get('sysRoot'). 'alpha/controller/'. $controllerName. '.php'))
require_once $config->get('sysRoot'). 'alpha/controller/'. $controllerName. '.php';
self::$logger->debug('<<loadControllerDef');
* Static function for determining if the current request URL is a secure one (has a tk string or not)
* @return boolean True if the current URL contains a tk value, false otherwise
if (isset ($_GET['tk']) || strpos($_SERVER['REQUEST_URI'], '/tk/') !== false)
|