<?php
// {{{ Header
/*
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| SIERRA : PHP Application Framework http://code.google.com/p/sierra-php |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| Copyright 2005 Jason Read |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.|
| See the License for the specific language governing permissions and |
| limitations under the License. |
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
*/
// }}}
// {{{ Imports
require_once('workflow/SRA_WorkflowDecision.php');
require_once('workflow/SRA_WorkflowTask.php');
// }}}
// {{{ Constants
/**
* identifies that the step due date should be relative to the current date
* @type int
*/
define('SRA_WORKFLOW_STEP_DUE_DATE_REL_CUR', 0);
/**
* identifies that the step due date should be relative to the wf start date
* @type int
*/
define('SRA_WORKFLOW_STEP_DUE_DATE_REL_WF_START', 1);
/**
* identifies that the step due date should be relative to the wf due date
* @type int
*/
define('SRA_WORKFLOW_STEP_DUE_DATE_REL_WF_DUE_DATE', 2);
// }}}
// {{{ SRA_WorkflowStep
/**
* defines a phase of work within the workflow. if a step is not a "finish"
* step, then it will also provide 1 or more outbound step references defining
* what should occur after this step is complete. workflow steps may be used to
* accomplish tasks (1 or more nested "tasks") or to implement conditional
* logic (using nested decisions) or both
*
* @author Jason Read <hide@address.com>
* @package sierra.workflow
*/
class SRA_WorkflowStep {
// {{{ Attributes
// public attributes
/**
* the unique identifier for this step
* @type string
*/
var $id;
/**
* the identifier of another workflow to connect to when this step is
* completed. NOTE: the connect-to workflow will be initialized using the same
* params as this step
* @type string
*/
var $connectTo;
/**
* any decisions associated with this step. each decision defines an alternate
* output path for the step
* @type SRA_WorkflowDecision[]
*/
var $decisions = array();
/**
* an explicit date or relative due-date expression. explicit dates should be
* entered in the format "YYYY-MM-DD HH:MM:SS" (time is optional). relative
* expressions utilize the same format except that any of the date values
* (YYYY, MM, DD, HH, MM, or SS) may be replaced with a relative modifier in
* the format "+n" where n is the increase from the current timestamp. For
* example, to specify the 1st of the following month, the dueDate would be:
* "+0-+1-01" - where +0 signifies the current year, and +1 signifies the
* following month. if the current month was december (12), the following
* month will be January and the year will be incremented automatically.
* Another example: to specify exactly one week from the current time,
* dueDate would be "+0-+0-+7" - where the first +0 signifies the current
* year, the second +0 signifies the current month, and +7 signifies 7 days
* from the current date. month and year rollovers resulting in the 1 week
* jump will be automatically applied (for example, if the action was created
* on 12/28). negative increments can be applied by enclosing the increment
* value "n" in parenthesis. for example, to specify 1 month minus 1 week from
* the current date, dueDate would be: "+0-+1-+(7)". the increment value "n"
* may also be an action or workflow parameter using the format "${param name}
* @type string
*/
var $dueDate;
/**
* if the step dueDate is relative, this attribute determines what it is
* relative to. the following options are available:
* 0: the date/time when the task was invoked (default)
* 1: the workflow start date
* 2: the workflow due date (should not be greater)
* the SRA_WORKFLOW_TASK_DUE_DATE_REL_* constants are defined for these
* options
* @type int
*/
var $dueDateRel;
/**
* when entityId is specified, and no entity has yet been instantiated and
* assigned to it, this attribute MUST be specified designating the entity
* type. this will be the name of the entity within one of the application's
* entity models. un-committed entities (when entity-pk is not specified) are
* stored in a workflow table and should not exceed 10mbps in size
* @type string
*/
var $entity;
/**
* if this step will add a new or use an existing entity within the workflow,
* this attribute will be the unique identifier for that entity. if this step
* is the first reference to that entityId, it will be automatically added to
* the workflow
* @type string
*/
var $entityId;
/**
* either the explicit primary key value or the name of a step/workflow
* parameter containing that value. this attribute should be specified when
* this step will add an entity instance to the workflow and that entity
* should be referenced from the database. if "entity" is specified, and this
* attribute is not, a new instance of that "entity" will be instantiated and
* added to the workflow. parameter-based primary key values should be entered
* in the format "${param name}"
* @type string
*/
var $entityPk;
/**
* whether or not this is a finish step. finish steps are those that cause the
* workflow to complete and terminate. finish steps should not specify a
* "next" attribute or any nested "decision" elements
* @type boolean
*/
var $finish;
/**
* the name of this step as determined by the config "resource"
* @type string
*/
var $name;
/**
* the default proceeding/output step once this one has been completed. the
* workflow will transition to that step unless a match is found in any of the
* nested "decision" elements for this step
* @type string
*/
var $next;
/**
* should notifications be sent for this step and its corresponding tasks?
* this value overrides the enclosing workflow 'notify' flag
* @type boolean
*/
var $notify;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifyBcc;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifyCc;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifyFrom;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifySubject;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifyTpl;
/**
* overrides the default workflow value for tasks in this step
* @type string
*/
var $notifyTplHtml;
/**
* used to define implementation/front-end specified values. they are not used
* within the lib/workflow system itself. they may be accessed within the
* SRA_Workflow/SRA_WorkflowStep::params associative array attribute
* @type array
*/
var $params = array();
/**
* the label resource for this task
* @type string
*/
var $resource;
/**
* if this step is the responsibility of a user other than the owner of the
* workflow instance, this attribute may specify a "role" or group of users
* that it should be assigned to. all users within that role will have equal
* permission to complete and add new tasks to this step. this attribute may
* also reference a step/workflow parameter using the format ${param name}
* @type string
*/
var $role;
/**
* the tasks assigned to this workflow step
* @type SRA_WorkflowTask[]
*/
var $tasks = array();
/**
* if this task is the responsibility of a user other than the owner of the
* workflow instance, this attribute may specify the identifier of that user.
* this user have permission to complete and add new tasks to this step. this
* attribute may also reference a workflow parameter using the format
* ${param name}
* @type mixed
*/
var $user;
// }}}
// {{{ Operations
// constructor(s)
// {{{ SRA_WorkflowStep
/**
* parses the step configuration data and sets the appropriate instance
* variables. if there is a problem with the configuration, the instance
* variable "err" will be assigned to an appropriate error object (the error
* will be logged)
* @param array $conf the configuration to parse
* @param SRA_Workflow $workflow the workflow this step pertains to
* @access public
*/
function SRA_WorkflowStep($conf, & $workflow) {
if (!isset($conf['attributes']['key'])) {
$err = 'id is not specified';
}
$this->id = $conf['attributes']['key'];
$this->connectTo = isset($conf['attributes']['connect-to']) ? $conf['attributes']['connect-to'] : NULL;
if ($this->connectTo && !SRA_Workflow::isValid(SRA_WorkflowManager::getWorkflowSetup($this->connectTo))) {
$err = 'connect-to "' . $this->connectTo . '" is not valid';
}
$this->dueDate = isset($conf['attributes']['due-date']) ? $conf['attributes']['due-date'] : NULL;
$this->dueDateRel = isset($conf['attributes']['due-date-rel']) ? $conf['attributes']['due-date-rel'] : SRA_WORKFLOW_STEP_DUE_DATE_REL_CUR;
if (isset($conf['attributes']['entity']) && SRA_Error::isError(SRA_DaoFactory::getDao($conf['attributes']['entity']))) {
$err = 'entity ' . $conf['attributes']['entity'] . ' is not valid';
}
$this->entity = isset($conf['attributes']['entity']) ? $conf['attributes']['entity'] : NULL;
if (!$err && $this->entity && !isset($conf['attributes']['entity-id'])) {
$err = 'no entity id specified for entity ' . $this->entity;
}
$this->entityId = isset($conf['attributes']['entity-id']) ? $conf['attributes']['entity-id'] : NULL;
$this->entityPk = isset($conf['attributes']['entity-pk']) ? $conf['attributes']['entity-pk'] : NULL;
$this->finish = isset($conf['attributes']['finish']) && $conf['attributes']['finish'] == '1' ? TRUE : FALSE;
if (!$err && $this->finish && (count($this->decisions) || isset($conf['attributes']['next']))) {
$err = 'finish steps cannot have a next reference or any nested decisions';
}
$this->next = isset($conf['attributes']['next']) ? $conf['attributes']['next'] : NULL;
if (!$err && !$this->next && !$this->finish) {
$err = 'non-finish steps must specify a "next" step reference';
}
else if (!$err && $this->next && !isset($workflow->steps[$this->next])) {
$err = $this->next . ' is not a valid step reference for this workflow';
}
$this->notify = isset($conf['attributes']['notify']) ? ($conf['attributes']['notify'] == '1' ? TRUE : FALSE) : $workflow->notify;
$this->notifyBcc = isset($conf['attributes']['notify-bcc']) ? $conf['attributes']['notify-bcc'] : NULL;
$this->notifyCc = isset($conf['attributes']['notify-cc']) ? $conf['attributes']['notify-cc'] : NULL;
$this->notifyFrom = isset($conf['attributes']['notify-from']) ? $conf['attributes']['notify-from'] : $workflow->notifyFrom;
$this->notifySubject = isset($conf['attributes']['notify-subject']) ? $conf['attributes']['notify-subject'] : $workflow->notifySubject;
if (!$err && $this->notifySubject && !($this->notifySubject = $workflow->resources->getString($this->notifySubject))) {
$err = 'notify subject "' . $this->notifySubject . '" is not valid';
}
$tpl =& SRA_Controller::getAppTemplate();
$this->notifyTpl = isset($conf['attributes']['notify-tpl']) ? $conf['attributes']['notify-tpl'] : NULL;
if (!$err && $this->notifyTpl && !$tpl->validateTemplate($this->notifyTpl)) {
$err = 'notify template "' . $this->notifyTpl . '" is not valid';
}
$this->notifyTplHtml = isset($conf['attributes']['notify-tpl-html']) ? $conf['attributes']['notify-tpl-html'] : NULL;
if (!$err && $this->notifyTplHtml && !$tpl->validateTemplate($this->notifyTplHtml)) {
$err = 'notify html template "' . $this->notifyTplHtml . '" is not valid';
}
if (!$err && (!isset($conf['attributes']['resource']) || !($this->name = $workflow->resources->getString($conf['attributes']['resource'])))) {
$err = 'resource "' . $conf['attributes']['resource'] . '" is not valid';
}
else {
$this->resource = $conf['attributes']['resource'];
}
if (!$err && count($conf['param'])) {
$keys = array_keys($conf['param']);
foreach ($keys as $key) {
$this->params[$key] = $conf['param'][$key]['attributes']['value'];
}
}
$this->role = isset($conf['attributes']['role']) ? $conf['attributes']['role'] : NULL;
if ($this->role && !$workflow->roleEntity) {
$err = 'role cannot be specified when there is no role entity assigned in the workflow';
}
$this->user = isset($conf['attributes']['user']) ? $conf['attributes']['user'] : NULL;
if (!$err && count($conf['decision'])) {
$keys = array_keys($conf['decision']);
foreach ($keys as $key) {
if (!SRA_WorkflowDecision::isValid($this->decisions[] = new SRA_WorkflowDecision($conf['decision'][$key], $workflow, $this))) {
$err = "decision ${key} produced error";
break;
}
}
}
if (!$err && isset($conf['task']) && count($conf['task'])) {
$keys = array_keys($conf['task']);
foreach ($keys as $key) {
if (!SRA_WorkflowTask::isValid($this->tasks[] = new SRA_WorkflowTask($conf['task'][$key], $workflow, $this))) {
$err = "task ${key} produced error";
break;
}
}
}
if ($err) { $this->err = SRA_Error::logError('SRA_WorkflowStep::SRA_WorkflowStep: ' . $this->id . ' Failed - ' . $err, __FILE__, __LINE__); }
}
// }}}
// Static methods
// {{{ isValid
/**
* Static method that returns true if the object parameter is a SRA_WorkflowStep object.
*
* @param Object $object The object to validate
* @access public
* @return boolean
*/
function isValid( & $object ) {
return (is_object($object) && (!isset($object->err) || !SRA_Error::isError($object->err)) && strtolower(get_class($object)) == 'sra_workflowstep');
}
// }}}
// private operations
}
// }}}
?>