* Copyright 2001-2012 Strangecode, LLC
*
* This file is part of The Strangecode Codebase.
*
* The Strangecode Codebase is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* The Strangecode Codebase is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* The Strangecode Codebase. If not, see .
*/
/**
* PageSequence.inc.php
*
* The PageSequence class provides an interface to simplify the creation of a multi-step form.
*
* @requires This class requires Prefs.inc.php
* @author Quinn Comendant
* @version 1.01
*/
require_once dirname(__FILE__) . '/Prefs.inc.php';
class PageSequence
{
public $current_step_id = 0;
public $start_url;
public $sequence_title = '';
public $seq = '_sequence_';
public $idle_timeout;
/**
* Constructor. Sets the title of this sequence and initializes session variables.
*
* @param array $params An associative array of PageSequence parameters.
* @access public
*/
public function __construct($params)
{
if (isset($params['sequence_title'])) {
$this->sequence_title = $params['sequence_title'];
$this->seq .= $params['sequence_title'];
}
// How long before resetting session? 60 minutes.
$this->idle_timeout = isset($params['idle_timeout']) ? $params['idle_timeout'] : 3600;
// Where is user to be redirected after startOver function call?
$this->start_url = isset($params['start_url']) ? $params['start_url'] : $_SERVER['PHP_SELF'];
// Initialize vars if not set.
if (!isset($_SESSION[$this->seq]['steps'])) {
$_SESSION[$this->seq]['steps'] = array();
}
if (!isset($_SESSION[$this->seq]['data'])) {
$_SESSION[$this->seq]['data'] = array();
}
if (!isset($_SESSION[$this->seq]['defaults'])) {
$_SESSION[$this->seq]['defaults'] = array();
}
// Manage timeout.
$this->_auto_timeout();
}
/**
* Create a new step at the end (or specified position) of the $steps array.
*
* @param string $step_id Uniqie identifyer for this step.
* @param array $params Values for the creation of the step.
*
* @return boolean Success of the addition
* @access public
*/
public function addStep($step_id, $params)
{
// Keys for the steps array cannot be numeric.
if (is_numeric($step_id)) {
trigger_error('Step ID for function addStep cannot be numeric.', E_USER_WARNING);
}
// This step ID already exists.
if (!is_null($this->getPosition($step_id))) {
return false;
}
$_SESSION[$this->seq]['steps'][] = array(
'id' => $step_id,
'title' => isset($params['title']) ? $params['title'] : '',
'active' => isset($params['active']) ? $params['active'] : false,
'form_tpl' => isset($params['form_tpl']) ? $params['form_tpl'] : '',
'disp_tpl' => isset($params['disp_tpl']) ? $params['disp_tpl'] : '',
'required' => isset($params['required']) ? $params['required'] : false,
'completed' => isset($params['completed']) ? $params['completed'] : false,
'editable' => isset($params['editable']) ? $params['editable'] : true,
);
}
/**
* Set the features of a step. Current step if step_id not specified.
*
* @param string $page_id ID of page. Leave null if modifying current step features.
* @param array $feature Array of feature keys and values to set.
*
* @return bool true on success, false on failure
*/
public function setFeature($step_id=null, $features)
{
$pos = isset($step_id) ? $this->getPosition($step_id) : $this->getPosition();
if (isset($features) && is_array($features) && isset($_SESSION[$this->seq]['steps'][$pos])) {
$_SESSION[$this->seq]['steps'][$pos] = array_merge($_SESSION[$this->seq]['steps'][$pos], $features);
}
}
/**
* Returns a specified value from a registered step.
*
* @access public
*
* @param string $step_id Id of step with feature to return.
* @param string $key Which value to return.
* @param mixed $default Value to return if key not found in user_data.
*
* @return mixed Value stored in session.
*/
public function getFeature($step_id, $key, $default='')
{
$pos = isset($step_id) ? $this->getPosition($step_id) : $this->getPosition();
if (isset($_SESSION[$this->seq]['steps'][$pos][$key])) {
return $_SESSION[$this->seq]['steps'][$pos][$key];
} else {
return $default;
}
}
/**
* Set the current step id. Which step are we on?
*
* @param string $step_id ID or number of current step.
* @return string Actual step ID
* @access public
*/
public function setCurrent($step_id)
{
$app =& App::getInstance();
if (false !== ($pos = $this->getPosition($step_id))) {
// Specified step exists (even if numeric).
$this->current_step_id = $this->getID($pos);
} else {
// Step with specified key does not exist.
$app->logMsg(sprintf('Step %s not defined in sequence %s', $step_id, $this->sequence_title), LOG_INFO, __FILE__, __LINE__);
return false;
}
}
/**
* Get the current step id.
*
* @return int $pos Actual step position
* @access public
*/
public function getID($pos=null)
{
if (isset($pos)) {
return $_SESSION[$this->seq]['steps'][$pos]['id'];
} else {
return $this->current_step_id;
}
}
/**
* Get the current step number.
*
* @param string $step_id ID or number of step to convert to position.
*
* @return string Actual step number
* @access public
*/
public function getPosition($step_id=null)
{
// Get current step id if step not provided.
if (!isset($step_id)) {
$step_id = $this->current_step_id;
}
if (is_numeric($step_id) && isset($_SESSION[$this->seq]['steps'][$step_id])) {
// Step ID provided is a number...go directly to key.
return $step_id;
} else {
// Step ID is a string. Loop through searching for match.
foreach ($_SESSION[$this->seq]['steps'] as $pos=>$step) {
if ($step['id'] == $step_id) {
return $pos;
}
}
// Step not found matching ID.
return null;
}
}
/**
* Returns the ID of the step with required=true and completed=false
* and active=true that falls before specified step_id.
*
* @param string $curr_step_id ID or number of current step.
* @return string Prerequisite step ID or false if none.
* @access public
*/
public function getRequiredID($curr_step_id)
{
if ('' === $curr_step_id) {
return $this->current_step_id;
}
foreach ($_SESSION[$this->seq]['steps'] as $pos=>$step) {
if (is_numeric($curr_step_id) && $pos == $curr_step_id && $step['active']) {
return $curr_step_id;
} else if ($step['id'] == $curr_step_id && $step['active']) {
return $curr_step_id;
}
if ($step['active'] && $step['required'] && !$step['completed']) {
return $step['id'];
}
}
return $curr_step_id;
}
/**
* Returns the next step in the steps array or the first active required
* uncompleted step.
*
* @return string Step identifier of the next step.
* @access public
*/
public function getNextID()
{
// Loop through all steps.
foreach ($_SESSION[$this->seq]['steps'] as $pos=>$step) {
// If a step is found that is active, required, and not completed, we must do that one.
if ($step['active'] && $step['required'] && !$step['completed']) {
return $step['id'];
}
// Otherwise do the first step after the current one that is active.
if ($step['active'] && $pos > $this->getPosition()) {
return $step['id'];
}
}
return null;
}
/**
* To set a set as 'completed'.
* @return string Step identifier of the next step.
* @access public
*/
public function complete($step_id=null)
{
$pos = isset($step_id) ? $this->getPosition($step_id) : $this->getPosition();
$_SESSION[$this->seq]['steps'][$pos]['completed'] = true;
}
/**
* Prints the a link that returns to the form for a step.
*
* @param string $form_type 'form' or 'disp'.
* @param string $step_id ID of step.
* @return string Filename of template.
* @access public
*/
public function printEditLink($step_id=null)
{
$app =& App::getInstance();
$pos = isset($step_id) ? $this->getPosition($step_id) : $this->getPosition();
if ($_SESSION[$this->seq]['steps'][$pos]['editable']) {
printf('[%s]', $app->oHREF($_SERVER['PHP_SELF'] . '?step=' . $pos . '&boomerang=confirmation'), _("edit"));
}
}
/**
* Saves given $step_data (usually coming from $_POST) into $_SESSION
* array with the $step_id as the key.
*
* @param string $step_id ID of current step.
* @param mixed $step_data Data to place into session storage.
* @return string Step identifier of the next step.
* @access public
*/
public function setDataDefault($data_key, $data_val)
{
if (!isset($_SESSION[$this->seq]['data'][$data_key])) {
$_SESSION[$this->seq]['data'][$data_key] = $data_val;
$_SESSION[$this->seq]['defaults'][$data_key] = $data_val;
}
}
/**
* Returns the value saved in $_SESSION for a specific data key.
*
* @param mixed $data_key Key of data to return from session data.
* @access public
*/
public function getData($data_key=null)
{
if (!isset($data_key)) {
return $_SESSION[$this->seq]['data'];
}
if (isset($_SESSION[$this->seq]['data'][$data_key])) {
return $_SESSION[$this->seq]['data'][$data_key];
} else {
return null;
}
}
/**
* Deletes all data saved in $_SESSION.
*
* @param mixed $data_key Key of data to return from session data.
* @access public
*/
public function clearData($data_key=null)
{
if (isset($data_key)) {
// Clear a specific key.
$_SESSION[$this->seq]['data'][$key] = array();
} else {
// Clear the whole thing.
$_SESSION[$this->seq]['data'] = array();
}
}
/**
* Deletes all data that are older than auto_timeout. Set current time if not not expired or not set.
*/
protected function _auto_timeout()
{
$app =& App::getInstance();
if (isset($_SESSION[$this->seq]['last_access_time'])
&& $_SESSION[$this->seq]['last_access_time'] < time() - $this->idle_timeout) {
// Session has expired, flush all vars to start over.
$this->startOver();
$app->dieURL($this->start_url);
} else {
// Set timer.
$_SESSION[$this->seq]['last_access_time'] = time();
}
}
/**
* Saves given array (usually coming from $_POST) into $_SESSION
*
* @param mixed $step_data Array of data to merge with session data.
* @access public
*/
public function registerData($step_data)
{
$_SESSION[$this->seq]['data'] = array_merge($_SESSION[$this->seq]['data'], $step_data);
}
/**
* Reset all vars.
*
*/
public function startOver()
{
$this->current_step_id = 0;
$_SESSION[$this->seq]['steps'] = array();
$_SESSION[$this->seq]['data'] = array();
$_SESSION[$this->seq]['defaults'] = array();
$_SESSION[$this->seq]['last_access_time'] = time();
}
} // END CLASS