Commit 62d36179f84b88d752ee7e2c8e0326fd0516fa23
1 parent
58c2d3cf
phase 1 of workflow admin improvements. still missing:
- action control - transition guards - "effects" (transition actions & notifications) git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@5890 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
26 changed files
with
2054 additions
and
17 deletions
lib/dispatcher.inc.php
| @@ -55,7 +55,7 @@ class KTDispatcher { | @@ -55,7 +55,7 @@ class KTDispatcher { | ||
| 55 | $this->oRedirector =& new KTDispatchStandardRedirector($this); | 55 | $this->oRedirector =& new KTDispatchStandardRedirector($this); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | - function redispatch($event_var, $action_prefix = null) { | 58 | + function redispatch($event_var, $action_prefix = null, $orig_dispatcher = null) { |
| 59 | $previous_event = KTUtil::arrayGet($_REQUEST, $this->event_var); | 59 | $previous_event = KTUtil::arrayGet($_REQUEST, $this->event_var); |
| 60 | if ($previous_event) { | 60 | if ($previous_event) { |
| 61 | $this->persistParams(array($this->event_var)); | 61 | $this->persistParams(array($this->event_var)); |
| @@ -64,7 +64,22 @@ class KTDispatcher { | @@ -64,7 +64,22 @@ class KTDispatcher { | ||
| 64 | if ($action_prefix) { | 64 | if ($action_prefix) { |
| 65 | $this->action_prefix = $action_prefix; | 65 | $this->action_prefix = $action_prefix; |
| 66 | } | 66 | } |
| 67 | - | 67 | + |
| 68 | + if (!is_null($orig_dispatcher)) { | ||
| 69 | + $this->persistParams($orig_dispatcher->aPersistParams); | ||
| 70 | + $core = array('aBreadcrumbs', | ||
| 71 | + 'bTransactionStarted', | ||
| 72 | + 'oUser', | ||
| 73 | + 'session', | ||
| 74 | + 'action_prefix', | ||
| 75 | + 'bJSONMode'); | ||
| 76 | + foreach($core as $k) { | ||
| 77 | + if(isset($orig_dispatcher->$k)) { | ||
| 78 | + $this->$k = $orig_dispatcher->$k; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + } | ||
| 82 | + | ||
| 68 | return $this->dispatch(); | 83 | return $this->dispatch(); |
| 69 | } | 84 | } |
| 70 | 85 | ||
| @@ -99,6 +114,10 @@ class KTDispatcher { | @@ -99,6 +114,10 @@ class KTDispatcher { | ||
| 99 | $this->startTransaction(); | 114 | $this->startTransaction(); |
| 100 | } | 115 | } |
| 101 | 116 | ||
| 117 | + if (method_exists($this, 'predispatch')) { | ||
| 118 | + $this->predispatch(); | ||
| 119 | + } | ||
| 120 | + | ||
| 102 | $ret = $this->$method(); | 121 | $ret = $this->$method(); |
| 103 | $this->handleOutput($ret); | 122 | $this->handleOutput($ret); |
| 104 | 123 | ||
| @@ -108,17 +127,18 @@ class KTDispatcher { | @@ -108,17 +127,18 @@ class KTDispatcher { | ||
| 108 | } | 127 | } |
| 109 | 128 | ||
| 110 | function subDispatch(&$oOrigDispatcher) { | 129 | function subDispatch(&$oOrigDispatcher) { |
| 111 | - foreach(array('aBreadcrumbs', | ||
| 112 | - 'bTransactionStarted', | ||
| 113 | - 'oUser', | ||
| 114 | - 'session', | ||
| 115 | - 'event_var', | ||
| 116 | - 'action_prefix', | ||
| 117 | - 'bJSONMode') as $k) { | ||
| 118 | - if(isset($oOrigDispatcher->$k)) { | ||
| 119 | - $this->$k = $oOrigDispatcher->$k; | ||
| 120 | - } | ||
| 121 | - } | 130 | + $core = array('aBreadcrumbs', |
| 131 | + 'bTransactionStarted', | ||
| 132 | + 'oUser', | ||
| 133 | + 'session', | ||
| 134 | + 'event_var', | ||
| 135 | + 'action_prefix', | ||
| 136 | + 'bJSONMode'); | ||
| 137 | + foreach($core as $k) { | ||
| 138 | + if(isset($oOrigDispatcher->$k)) { | ||
| 139 | + $this->$k = $oOrigDispatcher->$k; | ||
| 140 | + } | ||
| 141 | + } | ||
| 122 | 142 | ||
| 123 | return $this->dispatch(); | 143 | return $this->dispatch(); |
| 124 | } | 144 | } |
lib/util/ktutil.inc
| @@ -702,6 +702,16 @@ class KTUtil { | @@ -702,6 +702,16 @@ class KTUtil { | ||
| 702 | return $sRet; | 702 | return $sRet; |
| 703 | } | 703 | } |
| 704 | 704 | ||
| 705 | + function keyArray($aEntities, $sIdFunc = 'getId') { | ||
| 706 | + $aRet = array(); | ||
| 707 | + foreach ($aEntities as $oEnt) { | ||
| 708 | + $meth = array(&$oEnt, $sIdFunc); | ||
| 709 | + $id = call_user_func($meth); | ||
| 710 | + $aRet[$id] = $oEnt; | ||
| 711 | + } | ||
| 712 | + return $aRet; | ||
| 713 | + } | ||
| 714 | + | ||
| 705 | } | 715 | } |
| 706 | 716 | ||
| 707 | /** | 717 | /** |
lib/workflow/workflow.inc.php
| @@ -37,12 +37,14 @@ class KTWorkflow extends KTEntity { | @@ -37,12 +37,14 @@ class KTWorkflow extends KTEntity { | ||
| 37 | var $sName; | 37 | var $sName; |
| 38 | var $sHumanName; | 38 | var $sHumanName; |
| 39 | var $iStartStateId; | 39 | var $iStartStateId; |
| 40 | + var $bEnabled; | ||
| 40 | 41 | ||
| 41 | var $_aFieldToSelect = array( | 42 | var $_aFieldToSelect = array( |
| 42 | "iId" => "id", | 43 | "iId" => "id", |
| 43 | "sName" => "name", | 44 | "sName" => "name", |
| 44 | "sHumanName" => "human_name", | 45 | "sHumanName" => "human_name", |
| 45 | "iStartStateId" => "start_state_id", | 46 | "iStartStateId" => "start_state_id", |
| 47 | + 'bEnabled' => 'enabled', | ||
| 46 | ); | 48 | ); |
| 47 | 49 | ||
| 48 | var $_bUsePearError = true; | 50 | var $_bUsePearError = true; |
| @@ -51,10 +53,12 @@ class KTWorkflow extends KTEntity { | @@ -51,10 +53,12 @@ class KTWorkflow extends KTEntity { | ||
| 51 | function getName() { return $this->sName; } | 53 | function getName() { return $this->sName; } |
| 52 | function getHumanName() { return $this->sHumanName; } | 54 | function getHumanName() { return $this->sHumanName; } |
| 53 | function getStartStateId() { return $this->iStartStateId; } | 55 | function getStartStateId() { return $this->iStartStateId; } |
| 56 | + function getIsEnabled() { return ($this->bEnabled == true); } | ||
| 54 | function setID($iId) { $this->iId = $iId; } | 57 | function setID($iId) { $this->iId = $iId; } |
| 55 | function setName($sName) { $this->sName = $sName; } | 58 | function setName($sName) { $this->sName = $sName; } |
| 56 | function setHumanName($sHumanName) { $this->sHumanName = $sHumanName; } | 59 | function setHumanName($sHumanName) { $this->sHumanName = $sHumanName; } |
| 57 | function setStartStateId($iStartStateId) { $this->iStartStateId = $iStartStateId; } | 60 | function setStartStateId($iStartStateId) { $this->iStartStateId = $iStartStateId; } |
| 61 | + function setIsEnabled($mValue) { $this->bEnabled = ($mValue == true); } | ||
| 58 | 62 | ||
| 59 | function _table () { | 63 | function _table () { |
| 60 | return KTUtil::getTableName('workflows'); | 64 | return KTUtil::getTableName('workflows'); |
| @@ -79,10 +83,14 @@ class KTWorkflow extends KTEntity { | @@ -79,10 +83,14 @@ class KTWorkflow extends KTEntity { | ||
| 79 | function &getByName($sName) { | 83 | function &getByName($sName) { |
| 80 | return KTEntityUtil::getBy('KTWorkflow', 'name', $sName); | 84 | return KTEntityUtil::getBy('KTWorkflow', 'name', $sName); |
| 81 | } | 85 | } |
| 86 | + | ||
| 87 | + function getIsFunctional() { | ||
| 88 | + return (($this->getStartStateId() != false) && ($this->getIsEnabled())); | ||
| 89 | + } | ||
| 82 | 90 | ||
| 83 | // STATIC | 91 | // STATIC |
| 84 | function &getFunctional() { | 92 | function &getFunctional() { |
| 85 | - return KTEntityUtil::getList2('KTWorkflow', 'start_state_id IS NOT NULL'); | 93 | + return KTEntityUtil::getList2('KTWorkflow', 'start_state_id IS NOT NULL AND enabled = 1'); |
| 86 | } | 94 | } |
| 87 | 95 | ||
| 88 | function &getByDocument($oDocument) { | 96 | function &getByDocument($oDocument) { |
lib/workflow/workflowadminutil.inc.php
0 โ 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +// a very simple utility class to help keep the admin code clean, tidy | ||
| 4 | +// and re-usable, without having to do the same thing in 5 or 6 different | ||
| 5 | +// places. | ||
| 6 | +// | ||
| 7 | +// this also helps ease code-creep in workflowutil | ||
| 8 | + | ||
| 9 | +class KTWorkflowAdminUtil { | ||
| 10 | + // transition origins. | ||
| 11 | + function saveTransitionsFrom($oState, $aTransitionIds) { | ||
| 12 | + $sTable = KTUtil::getTableName('workflow_state_transitions'); | ||
| 13 | + $aQuery = array( | ||
| 14 | + "DELETE FROM $sTable WHERE state_id = ?", | ||
| 15 | + array($oState->getId()), | ||
| 16 | + ); | ||
| 17 | + $res = DBUtil::runQuery($aQuery); | ||
| 18 | + if (PEAR::isError($res)) { | ||
| 19 | + return $res; | ||
| 20 | + } | ||
| 21 | + $aOptions = array('noid' => true); | ||
| 22 | + if (empty($aTransitionIds)) { | ||
| 23 | + return; // don't fail if there are no transitions. | ||
| 24 | + } | ||
| 25 | + foreach ($aTransitionIds as $iTransitionId) { | ||
| 26 | + $res = DBUtil::autoInsert($sTable, array( | ||
| 27 | + 'state_id' => $oState->getId(), | ||
| 28 | + 'transition_id' => $iTransitionId, | ||
| 29 | + ), $aOptions); | ||
| 30 | + if (PEAR::isError($res)) { | ||
| 31 | + return $res; | ||
| 32 | + } | ||
| 33 | + } | ||
| 34 | + return; | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + function saveTransitionSources($oTransition, $aStateIds) { | ||
| 38 | + $sTable = KTUtil::getTableName('workflow_state_transitions'); | ||
| 39 | + $aQuery = array( | ||
| 40 | + "DELETE FROM $sTable WHERE transition_id = ?", | ||
| 41 | + array($oTransition->getId()), | ||
| 42 | + ); | ||
| 43 | + $res = DBUtil::runQuery($aQuery); | ||
| 44 | + if (PEAR::isError($res)) { | ||
| 45 | + return $res; | ||
| 46 | + } | ||
| 47 | + $aOptions = array('noid' => true); | ||
| 48 | + if (empty($aStateIds)) { | ||
| 49 | + return; // don't fail if there are no transitions. | ||
| 50 | + } | ||
| 51 | + foreach ($aStateIds as $iStateId) { | ||
| 52 | + $res = DBUtil::autoInsert($sTable, array( | ||
| 53 | + 'state_id' => $iStateId, | ||
| 54 | + 'transition_id' => $oTransition->getId(), | ||
| 55 | + ), $aOptions); | ||
| 56 | + if (PEAR::isError($res)) { | ||
| 57 | + return $res; | ||
| 58 | + } | ||
| 59 | + } | ||
| 60 | + return; | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | +// {{{ getTransitionsFrom | ||
| 64 | + /** | ||
| 65 | + * Gets which workflow transitions are available to be chosen from | ||
| 66 | + * this workflow state. | ||
| 67 | + * | ||
| 68 | + * Workflow transitions have only destination workflow states, and | ||
| 69 | + * it is up to the workflow state to decide which workflow | ||
| 70 | + * transitions it wants to allow to leave its state. | ||
| 71 | + * | ||
| 72 | + * This function optionally will return the database id numbers of | ||
| 73 | + * the workflow transitions using the 'ids' option. | ||
| 74 | + */ | ||
| 75 | + function getTransitionsFrom($oState, $aOptions = null) { | ||
| 76 | + $bIds = KTUtil::arrayGet($aOptions, 'ids'); | ||
| 77 | + $sTable = KTUtil::getTableName('workflow_state_transitions'); | ||
| 78 | + $aQuery = array( | ||
| 79 | + "SELECT transition_id FROM $sTable WHERE state_id = ?", | ||
| 80 | + array($oState->getId()), | ||
| 81 | + ); | ||
| 82 | + $aTransitionIds = DBUtil::getResultArrayKey($aQuery, 'transition_id'); | ||
| 83 | + if (PEAR::isError($aTransitionIds)) { | ||
| 84 | + return $aTransitionIds; | ||
| 85 | + } | ||
| 86 | + if ($bIds) { | ||
| 87 | + return $aTransitionIds; | ||
| 88 | + } | ||
| 89 | + $aRet = array(); | ||
| 90 | + foreach ($aTransitionIds as $iId) { | ||
| 91 | + $aRet[] =& KTWorkflowTransition::get($iId); | ||
| 92 | + } | ||
| 93 | + return $aRet; | ||
| 94 | + } | ||
| 95 | + // }}} | ||
| 96 | + | ||
| 97 | + function getSourceStates($oTransition, $aOptions = null) { | ||
| 98 | + $bIds = KTUtil::arrayGet($aOptions, 'ids'); | ||
| 99 | + $sTable = KTUtil::getTableName('workflow_state_transitions'); | ||
| 100 | + $aQuery = array( | ||
| 101 | + "SELECT state_id FROM $sTable WHERE transition_id = ?", | ||
| 102 | + array($oTransition->getId()), | ||
| 103 | + ); | ||
| 104 | + $aStateIds = DBUtil::getResultArrayKey($aQuery, 'state_id'); | ||
| 105 | + if (PEAR::isError($aStateIds)) { | ||
| 106 | + return $aStateIds; | ||
| 107 | + } | ||
| 108 | + if ($bIds) { | ||
| 109 | + return $aStateIds; | ||
| 110 | + } | ||
| 111 | + $aRet = array(); | ||
| 112 | + foreach ($aStateIds as $iId) { | ||
| 113 | + $aRet[] =& KTWorkflowState::get($iId); | ||
| 114 | + } | ||
| 115 | + return $aRet; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | +} |
lib/workflow/workflowstate.inc.php
| @@ -106,6 +106,23 @@ class KTWorkflowState extends KTEntity { | @@ -106,6 +106,23 @@ class KTWorkflowState extends KTEntity { | ||
| 106 | return KTWorkflowState::get($iStateId); | 106 | return KTWorkflowState::get($iStateId); |
| 107 | } | 107 | } |
| 108 | 108 | ||
| 109 | + // STATIC | ||
| 110 | + function nameExists($sName, $oWorkflow) { | ||
| 111 | + $iWorkflowId = KTUtil::getId($oWorkflow); | ||
| 112 | + $res = KTEntityUtil::getByDict( | ||
| 113 | + 'KTWorkflowState', array( | ||
| 114 | + 'name' => $sName, | ||
| 115 | + 'workflow_id' => $iWorkflowId | ||
| 116 | + ) | ||
| 117 | + ); | ||
| 118 | + // expect KTEntityNoObjects | ||
| 119 | + if (PEAR::isError($res)) { | ||
| 120 | + return false; | ||
| 121 | + } | ||
| 122 | + | ||
| 123 | + return true; | ||
| 124 | + } | ||
| 125 | + | ||
| 109 | } | 126 | } |
| 110 | 127 | ||
| 111 | ?> | 128 | ?> |
lib/workflow/workflowtransition.inc.php
| @@ -130,6 +130,24 @@ class KTWorkflowTransition extends KTEntity { | @@ -130,6 +130,24 @@ class KTWorkflowTransition extends KTEntity { | ||
| 130 | $oWorkflowState =& KTWorkflowState::get($this->getTargetStateId()); | 130 | $oWorkflowState =& KTWorkflowState::get($this->getTargetStateId()); |
| 131 | return sprintf("%s (to state %s)", $this->getName(), $oWorkflowState->getName()); | 131 | return sprintf("%s (to state %s)", $this->getName(), $oWorkflowState->getName()); |
| 132 | } | 132 | } |
| 133 | + | ||
| 134 | + // STATIC | ||
| 135 | + function nameExists($sName, $oWorkflow) { | ||
| 136 | + $iWorkflowId = KTUtil::getId($oWorkflow); | ||
| 137 | + $res = KTEntityUtil::getByDict( | ||
| 138 | + 'KTWorkflowTransition', array( | ||
| 139 | + 'name' => $sName, | ||
| 140 | + 'workflow_id' => $iWorkflowId | ||
| 141 | + ) | ||
| 142 | + ); | ||
| 143 | + // expect KTEntityNoObjects | ||
| 144 | + if (PEAR::isError($res)) { | ||
| 145 | + return false; | ||
| 146 | + } | ||
| 147 | + | ||
| 148 | + return true; | ||
| 149 | + } | ||
| 150 | + | ||
| 133 | } | 151 | } |
| 134 | 152 | ||
| 135 | ?> | 153 | ?> |
plugins/ktcore/KTCorePlugin.php
| @@ -218,9 +218,9 @@ class KTCorePlugin extends KTPlugin { | @@ -218,9 +218,9 @@ class KTCorePlugin extends KTPlugin { | ||
| 218 | $this->registerAdminPage("workflows", 'KTWorkflowDispatcher', 'documents', | 218 | $this->registerAdminPage("workflows", 'KTWorkflowDispatcher', 'documents', |
| 219 | _kt('Workflows'), _kt('Configure the process documents go through.'), | 219 | _kt('Workflows'), _kt('Configure the process documents go through.'), |
| 220 | 'admin/workflows.php', null); | 220 | 'admin/workflows.php', null); |
| 221 | - //$this->registerAdminPage("workflows_2", 'KTWorkflowDispatcher', 'documents', | ||
| 222 | - // _kt('Workflows v2'), _kt('Configure the process documents go through.'), | ||
| 223 | - // 'admin/workflowsNew.php', null); | 221 | + $this->registerAdminPage("workflows_2", 'KTWorkflowAdminV2', 'documents', |
| 222 | + _kt('Workflows v2'), _kt('Configure the process documents go through.'), | ||
| 223 | + 'admin/workflowsv2.php', null); | ||
| 224 | 224 | ||
| 225 | // storage | 225 | // storage |
| 226 | $this->registerAdminPage("checkout", 'KTCheckoutAdminDispatcher', 'storage', | 226 | $this->registerAdminPage("checkout", 'KTCheckoutAdminDispatcher', 'storage', |
plugins/ktcore/admin/workflow/newworkflow.inc.php
0 โ 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +// core | ||
| 4 | +require_once(KT_LIB_DIR . "/dispatcher.inc.php"); | ||
| 5 | + | ||
| 6 | +// workflow | ||
| 7 | +require_once(KT_LIB_DIR . '/workflow/workflow.inc.php'); | ||
| 8 | +require_once(KT_LIB_DIR . '/workflow/workflowstate.inc.php'); | ||
| 9 | +require_once(KT_LIB_DIR . '/workflow/workflowtransition.inc.php'); | ||
| 10 | +require_once(KT_LIB_DIR . '/workflow/workflowstatepermissionsassignment.inc.php'); | ||
| 11 | +require_once(KT_LIB_DIR . '/workflow/workflowutil.inc.php'); | ||
| 12 | +require_once(KT_LIB_DIR . '/workflow/workflowadminutil.inc.php'); | ||
| 13 | + | ||
| 14 | +// other | ||
| 15 | +require_once(KT_LIB_DIR . '/permissions/permission.inc.php'); | ||
| 16 | +require_once(KT_LIB_DIR . '/actions/documentaction.inc.php'); | ||
| 17 | +require_once(KT_LIB_DIR . '/widgets/portlet.inc.php'); | ||
| 18 | +require_once(KT_LIB_DIR . '/users/User.inc'); | ||
| 19 | +require_once(KT_LIB_DIR . '/groups/Group.inc'); | ||
| 20 | +require_once(KT_LIB_DIR . '/roles/Role.inc'); | ||
| 21 | +require_once(KT_LIB_DIR . '/widgets/portlet.inc.php'); | ||
| 22 | +require_once(KT_LIB_DIR . '/widgets/forms.inc.php'); | ||
| 23 | + | ||
| 24 | +class KTNewWorkflowWizard extends KTAdminDispatcher { | ||
| 25 | + function predispatch() { | ||
| 26 | + $this->persistParams(array('fWizardKey')); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + function &form_step1() { | ||
| 30 | + $oForm = new KTForm; | ||
| 31 | + | ||
| 32 | + $oForm->setOptions(array( | ||
| 33 | + 'action' => 'process_step1', | ||
| 34 | + 'cancel_url' => KTUtil::addQueryStringSelf(''), // NBM: is there a cleaner way to reference the parent? | ||
| 35 | + 'fail_action' => 'main', | ||
| 36 | + 'label' => _kt('Workflow Details'), | ||
| 37 | + 'submit_label' => _kt('Next'), | ||
| 38 | + 'description' => _kt('This first step requires that you provide basic details about the workflow: its name, etc.'), | ||
| 39 | + 'context' => $this, | ||
| 40 | + )); | ||
| 41 | + $oForm->setWidgets(array( | ||
| 42 | + array('ktcore.widgets.string',array( | ||
| 43 | + 'label' => _kt('Workflow Name'), | ||
| 44 | + 'description' => _kt('Each workflow must have a unique name.'), | ||
| 45 | + 'required' => true, | ||
| 46 | + 'name' => 'workflow_name', | ||
| 47 | + )), | ||
| 48 | + array('ktcore.widgets.text',array( | ||
| 49 | + 'label' => _kt('States'), | ||
| 50 | + 'description' => _kt('As documents progress through their lifecycle, they pass through a number of <strong>states</strong>. These states describe a step in the process the document must follow. Examples of states include "reviewed","submitted" or "pending". Note that the first state you list is the one in which documents will start the workflow - this can be changed later on. Please enter a list of states, one per line. State names must be unique.'), | ||
| 51 | + 'required' => true, | ||
| 52 | + 'name' => 'states', | ||
| 53 | + 'rows' => 15, | ||
| 54 | + )), | ||
| 55 | + array('ktcore.widgets.text',array( | ||
| 56 | + 'label' => _kt('Transitions'), | ||
| 57 | + 'description' => _kt('In order to move between states, users will cause "transitions" to occur. These transitions represent processes followed, e.g. "review document", "distribute invoice" or "publish". Please enter a list of transitions, one per line. Transition names must be unique. You\'ll assign transitions to states in the next step.'), | ||
| 58 | + 'required' => false, | ||
| 59 | + 'name' => 'transitions', | ||
| 60 | + )), | ||
| 61 | + )); | ||
| 62 | + | ||
| 63 | + $oForm->setValidators(array( | ||
| 64 | + array('ktcore.validators.string', array( | ||
| 65 | + 'test' => 'workflow_name', | ||
| 66 | + 'output' => 'workflow_name', | ||
| 67 | + )), | ||
| 68 | + array('ktcore.validators.string', array( | ||
| 69 | + 'test' => 'states', | ||
| 70 | + 'output' => 'states', | ||
| 71 | + 'max_length' => 9999, | ||
| 72 | + )), | ||
| 73 | + array('ktcore.validators.string', array( | ||
| 74 | + 'test' => 'transitions', | ||
| 75 | + 'output' => 'transitions', | ||
| 76 | + 'max_length' => 9999, | ||
| 77 | + )), | ||
| 78 | + )); | ||
| 79 | + | ||
| 80 | + return $oForm; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + function do_main() { | ||
| 84 | + $oTemplate =& $this->oValidator->validateTemplate('ktcore/workflow/admin/new_wizard_step1'); | ||
| 85 | + | ||
| 86 | + $oForm =& $this->form_step1(); | ||
| 87 | + | ||
| 88 | + $oTemplate->setData(array( | ||
| 89 | + 'context' => $this, | ||
| 90 | + 'form' => $oForm, | ||
| 91 | + )); | ||
| 92 | + return $oTemplate->render(); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + function do_process_step1() { | ||
| 96 | + $oForm =& $this->form_step1(); | ||
| 97 | + $res = $oForm->validate(); | ||
| 98 | + $data = $res['results']; | ||
| 99 | + // perform additional validation. | ||
| 100 | + $extra_errors = array(); | ||
| 101 | + | ||
| 102 | + $oWorkflow = KTWorkflow::getByName($data['workflow_name']); | ||
| 103 | + if (!PEAR::isError($oWorkflow)) { | ||
| 104 | + $extra_errors['workflow_name'][] = _kt("A workflow with that name already exists. Please choose a different name for this workflow."); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + $initial_states = (array) explode("\n", $data['states']); // must be there, we validated it. | ||
| 108 | + $failed = array(); | ||
| 109 | + $states = array(); | ||
| 110 | + $is_first = true; | ||
| 111 | + $initial_state = ''; | ||
| 112 | + foreach ($initial_states as $sInitialStateName) { | ||
| 113 | + $state_name = trim($sInitialStateName); | ||
| 114 | + if (empty($state_name)) { | ||
| 115 | + continue; | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + if ($states[$state_name]) { | ||
| 119 | + $failed[] = $state_name; | ||
| 120 | + continue; | ||
| 121 | + } | ||
| 122 | + if ($is_first) { | ||
| 123 | + $is_first = false; | ||
| 124 | + $initial_state = $state_name; | ||
| 125 | + } | ||
| 126 | + $states[$state_name] = $state_name; | ||
| 127 | + } | ||
| 128 | + if (empty($states)) { | ||
| 129 | + $extra_errors['states'][] = _kt('You must provide at least one state name.'); | ||
| 130 | + } | ||
| 131 | + if (!empty($failed)) { | ||
| 132 | + $extra_errors['states'] = sprintf(_kt("You cannot have duplicate state names: %s"), implode(', ', $failed)); | ||
| 133 | + } | ||
| 134 | + $data['states'] = $states; | ||
| 135 | + $data['initial_state'] = $initial_state; | ||
| 136 | + | ||
| 137 | + $initial_transitions = (array) explode("\n", $data['transitions']); // must be there, we validated it. | ||
| 138 | + $failed = array(); | ||
| 139 | + $transitions = array(); | ||
| 140 | + foreach ($initial_transitions as $sInitialTransitionName) { | ||
| 141 | + $transition_name = trim($sInitialTransitionName); | ||
| 142 | + if (empty($transition_name)) { | ||
| 143 | + continue; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + if ($transitions[$transition_name]) { | ||
| 147 | + $failed[] = $transition_name; | ||
| 148 | + continue; | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + $transitions[$transition_name] = $transition_name; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + if (!empty($failed)) { | ||
| 155 | + $extra_errors['transitions'] = sprintf(_kt("You cannot have duplicate transition names: %s"), implode(', ', $failed)); | ||
| 156 | + } | ||
| 157 | + $data['transitions'] = $transitions; | ||
| 158 | + | ||
| 159 | + // handle errors. | ||
| 160 | + if (!empty($res['errors']) || !empty($extra_errors)) { | ||
| 161 | + $oForm->handleError(null, $extra_errors); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + // store the data for a while. | ||
| 165 | + $fWizardKey = KTUtil::randomString(); | ||
| 166 | + $wiz_data = (array) $_SESSION['_wiz_data']; | ||
| 167 | + $wiz_data[$fWizardKey] = $data; | ||
| 168 | + $_SESSION['_wiz_data'] =& $wiz_data; | ||
| 169 | + | ||
| 170 | + if (empty($data['transitions'])) { | ||
| 171 | + return $this->finalise(); // finish and go. | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + $this->successRedirectTo("step2",_kt("Initial data stored."), sprintf('fWizardKey=%s', $fWizardKey)); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + function do_step2() { | ||
| 178 | + $fWizardKey = KTUtil::arrayGet($_REQUEST, 'fWizardKey'); | ||
| 179 | + $wiz_data = (array) $_SESSION['_wiz_data'][$fWizardKey]; | ||
| 180 | + | ||
| 181 | + if (empty($wiz_data)) { | ||
| 182 | + $this->errorRedirectToMain(_kt("Unable to find previous value. Please try again.")); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/new_wizard_step2'); | ||
| 186 | + | ||
| 187 | + $transitions = (array) $wiz_data['transitions']; | ||
| 188 | + $args = $this->meldPersistQuery("", "process_step2", true); | ||
| 189 | + | ||
| 190 | + $oTemplate->setData(array( | ||
| 191 | + 'context' => $this, | ||
| 192 | + 'args' => $args, | ||
| 193 | + 'transitions' => $wiz_data['transitions'], | ||
| 194 | + 'states' => $wiz_data['states'], | ||
| 195 | + )); | ||
| 196 | + return $oTemplate->render(); | ||
| 197 | + } | ||
| 198 | + | ||
| 199 | + function do_process_step2() { | ||
| 200 | + $fWizardKey = KTUtil::arrayGet($_REQUEST, 'fWizardKey'); | ||
| 201 | + $wiz_data = $_SESSION['_wiz_data'][$fWizardKey]; | ||
| 202 | + if (empty($wiz_data)) { | ||
| 203 | + $this->errorRedirectToMain(_kt("Unable to locate stored data. Please try again.")); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + // we can't use the form "stuff" here since we don't have a grid widget yet | ||
| 207 | + // and hopefully never will. | ||
| 208 | + | ||
| 209 | + $fToData = (array) KTUtil::arrayGet($_REQUEST, 'fTo'); | ||
| 210 | + $fFromData = (array) KTUtil::arrayGet($_REQUEST, 'fFrom'); | ||
| 211 | + | ||
| 212 | + // these are data[transition][state] = true | ||
| 213 | + | ||
| 214 | + $fTo = array(); | ||
| 215 | + $initial_state = $wiz_data['initial_state']; | ||
| 216 | + foreach ($wiz_data['transitions'] as $transition) { | ||
| 217 | + $candidate = $fToData[$transition]; | ||
| 218 | + if (empty($wiz_data['states'][$candidate])) { | ||
| 219 | + $candidate = $initial_state; | ||
| 220 | + } | ||
| 221 | + $fTo[$transition] = $candidate; | ||
| 222 | + } | ||
| 223 | + | ||
| 224 | + $fFrom = array(); | ||
| 225 | + foreach ($wiz_data['transitions'] as $transition) { | ||
| 226 | + $d = (array) KTUtil::arrayGet($fFromData, $transition); | ||
| 227 | + $final = array(); | ||
| 228 | + foreach ($d as $state => $discard) { | ||
| 229 | + if (!empty($wiz_data['states'][$state])) { | ||
| 230 | + $final[] = $state; | ||
| 231 | + } | ||
| 232 | + } | ||
| 233 | + $fFrom[$transition] = $final; | ||
| 234 | + } | ||
| 235 | + | ||
| 236 | + $wiz_data['from'] = $fFrom; | ||
| 237 | + $wiz_data['to'] = $fTo; | ||
| 238 | + | ||
| 239 | + $_SESSION['_wiz_data'][$fWizardKey] = $wiz_data; | ||
| 240 | + | ||
| 241 | + return $this->finalise(); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + function finalise() { | ||
| 245 | + $fWizardKey = KTUtil::arrayGet($_REQUEST, 'fWizardKey'); | ||
| 246 | + $wiz_data = $_SESSION['_wiz_data'][$fWizardKey]; | ||
| 247 | + | ||
| 248 | + // gather all our data. we're sure this is all good and healthy. | ||
| 249 | + | ||
| 250 | + $states = $wiz_data['states']; | ||
| 251 | + $transitions = $wiz_data['transitions']; | ||
| 252 | + $from = $wiz_data['from']; | ||
| 253 | + $to = $wiz_data['to']; | ||
| 254 | + $initial_state = $wiz_data['initial_state']; | ||
| 255 | + $workflow_name = $wiz_data['workflow_name']; | ||
| 256 | + | ||
| 257 | + $this->startTransaction(); | ||
| 258 | + // create the initial workflow | ||
| 259 | + $oWorkflow = KTWorkflow::createFromArray(array( | ||
| 260 | + 'name' => $workflow_name, | ||
| 261 | + 'humanname' => $workflow_name, | ||
| 262 | + 'enabled' => true, | ||
| 263 | + )); | ||
| 264 | + if (PEAR::isError($oWorkflow)) { | ||
| 265 | + $this->errorRedirectToMain(sprintf(_kt("Failed to create workflow: %s"), $oWorkflow->getMessage())); | ||
| 266 | + } | ||
| 267 | + $iWorkflowId = $oWorkflow->getId(); | ||
| 268 | + // create the states. | ||
| 269 | + $aStates = array(); | ||
| 270 | + foreach ($states as $state_name) { | ||
| 271 | + $oState = KTWorkflowState::createFromArray(array( | ||
| 272 | + 'workflowid' => $iWorkflowId, | ||
| 273 | + 'name' => $state_name, | ||
| 274 | + 'humanname' => $state_name, | ||
| 275 | + )); | ||
| 276 | + if (PEAR::isError($oState)) { | ||
| 277 | + $this->errorRedirectToMain(sprintf(_kt("Failed to create state: %s"), $oState->getMessage())); | ||
| 278 | + } | ||
| 279 | + $aStates[$state_name] = $oState; | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + // update the initial state on workflow | ||
| 283 | + $oInitialState = $aStates[$initial_state]; | ||
| 284 | + $oWorkflow->setStartStateId($oInitialState->getId()); | ||
| 285 | + $res = $oWorkflow->update(); | ||
| 286 | + if (PEAR::isError($res)) { | ||
| 287 | + $this->errorRedirectToMain(sprintf(_kt("Failed to update workflow: %s"), $res->getMessage())); | ||
| 288 | + } | ||
| 289 | + | ||
| 290 | + // next, we create and hook up the transitions. | ||
| 291 | + $aTransitions = array(); | ||
| 292 | + foreach ($transitions as $transition) { | ||
| 293 | + $dest_name = $to[$transition]; | ||
| 294 | + $oDestState = $aStates[$dest_name]; | ||
| 295 | + $oTransition = KTWorkflowTransition::createFromArray(array( | ||
| 296 | + "WorkflowId" => $iWorkflowId, | ||
| 297 | + "Name" => $transition, | ||
| 298 | + "HumanName" => $transition, | ||
| 299 | + "TargetStateId" => $oDestState->getId(), | ||
| 300 | + // FIXME we need to deprecate these, eventually | ||
| 301 | + "GuardPermissionId" => null, | ||
| 302 | + "GuardGroupId" => null, | ||
| 303 | + "GuardRoleId" => null, | ||
| 304 | + "GuardConditionId" => null, | ||
| 305 | + )); | ||
| 306 | + if (PEAR::isError($oTransition)) { | ||
| 307 | + $this->errorRedirectToMain(sprintf(_kt("Failed to create transition: %s"), $oTransition->getMessage())); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + // hook up source states. | ||
| 311 | + $state_ids = array(); | ||
| 312 | + $sources = (array) $from[$transition]; | ||
| 313 | + foreach ($sources as $state_name) { | ||
| 314 | + | ||
| 315 | + // must exist. | ||
| 316 | + $oState = $aStates[$state_name]; | ||
| 317 | + $state_ids[] = $oState->getId(); | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + $res = KTWorkflowAdminUtil::saveTransitionSources($oTransition, $state_ids); | ||
| 321 | + if (PEAR::isError($res)) { | ||
| 322 | + $this->errorRedirectToMain(sprintf(_kt("Failed to set transition origins: %s"), $res->getMessage())); | ||
| 323 | + } | ||
| 324 | + } | ||
| 325 | + | ||
| 326 | + $this->commitTransaction(); | ||
| 327 | + | ||
| 328 | + // finally, we want to redirect the user to the parent dispatcher somehow. | ||
| 329 | + // FIXME nbm: how do you recommend we do this? | ||
| 330 | + | ||
| 331 | + $base = $_SERVER['PHP_SELF']; | ||
| 332 | + $qs = sprintf("action=view&fWorkflowId=%d",$oWorkflow->getId()); | ||
| 333 | + $url = KTUtil::addQueryString($base, $qs); | ||
| 334 | + $this->addInfoMessage(_kt("Your new workflow has been created. You may want to configure security and notifications from the menu on the left.")); | ||
| 335 | + redirect($url); | ||
| 336 | + } | ||
| 337 | +} | ||
| 338 | + | ||
| 339 | +?> |
plugins/ktcore/admin/workflowsv2.php
0 โ 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +// core | ||
| 4 | +require_once(KT_LIB_DIR . "/dispatcher.inc.php"); | ||
| 5 | + | ||
| 6 | +// workflow | ||
| 7 | +require_once(KT_LIB_DIR . '/workflow/workflow.inc.php'); | ||
| 8 | +require_once(KT_LIB_DIR . '/workflow/workflowstate.inc.php'); | ||
| 9 | +require_once(KT_LIB_DIR . '/workflow/workflowtransition.inc.php'); | ||
| 10 | +require_once(KT_LIB_DIR . '/workflow/workflowstatepermissionsassignment.inc.php'); | ||
| 11 | +require_once(KT_LIB_DIR . '/workflow/workflowutil.inc.php'); | ||
| 12 | +require_once(KT_LIB_DIR . '/workflow/workflowadminutil.inc.php'); | ||
| 13 | + | ||
| 14 | +// other | ||
| 15 | +require_once(KT_LIB_DIR . '/permissions/permission.inc.php'); | ||
| 16 | +require_once(KT_LIB_DIR . '/actions/documentaction.inc.php'); | ||
| 17 | +require_once(KT_LIB_DIR . '/widgets/portlet.inc.php'); | ||
| 18 | +require_once(KT_LIB_DIR . '/users/User.inc'); | ||
| 19 | +require_once(KT_LIB_DIR . '/groups/Group.inc'); | ||
| 20 | +require_once(KT_LIB_DIR . '/roles/Role.inc'); | ||
| 21 | +require_once(KT_LIB_DIR . '/widgets/portlet.inc.php'); | ||
| 22 | +require_once(KT_LIB_DIR . '/widgets/forms.inc.php'); | ||
| 23 | + | ||
| 24 | +class WorkflowNavigationPortlet extends KTPortlet { | ||
| 25 | + var $oWorkflow; | ||
| 26 | + var $sHelpPage = 'ktcore/admin/workflow.html'; | ||
| 27 | + var $bActive = true; | ||
| 28 | + | ||
| 29 | + function WorkflowNavigationPortlet($sTitle, $oWorkflow = null) { | ||
| 30 | + $this->oWorkflow = $oWorkflow; | ||
| 31 | + parent::KTPortlet($sTitle); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + function render() { | ||
| 35 | + if (is_null($this->oWorkflow)) { return _kt('No Workflow Selected.'); } | ||
| 36 | + | ||
| 37 | + $aAdminPages = array(); | ||
| 38 | + $aAdminPages[] = array('name' => _kt('Overview'), 'query' => 'action=view&fWorkflowId=' . $this->oWorkflow->getId()); | ||
| 39 | + $aAdminPages[] = array('name' => _kt('States and Transitions'), 'query' => 'action=basic&fWorkflowId=' . $this->oWorkflow->getId()); | ||
| 40 | + $aAdminPages[] = array('name' => _kt('Security'), 'query' => 'action=security&fWorkflowId=' . $this->oWorkflow->getId()); | ||
| 41 | + $aAdminPages[] = array('name' => _kt('Workflow Effects'), 'query' => 'action=effects&fWorkflowId=' . $this->oWorkflow->getId()); | ||
| 42 | + $aAdminPages[] = array('name' => _kt('Select different workflow'), 'query' => 'action=main&fWorkflowId=' . $this->oWorkflow->getId()); | ||
| 43 | + | ||
| 44 | + $oTemplating =& KTTemplating::getSingleton(); | ||
| 45 | + $oTemplate = $oTemplating->loadTemplate("ktcore/workflow/admin_portlet"); | ||
| 46 | + $aTemplateData = array( | ||
| 47 | + "context" => $this, | ||
| 48 | + "aAdminPages" => $aAdminPages, | ||
| 49 | + ); | ||
| 50 | + | ||
| 51 | + return $oTemplate->render($aTemplateData); | ||
| 52 | + } | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +class KTWorkflowAdminV2 extends KTAdminDispatcher { | ||
| 56 | + var $oWorkflow; | ||
| 57 | + var $oState; | ||
| 58 | + var $oTransition; | ||
| 59 | + | ||
| 60 | + function check() { | ||
| 61 | + $res = parent::check(); | ||
| 62 | + $this->persistParams(array('fWorkflowId', 'fStateId', 'fTransitionId')); | ||
| 63 | + | ||
| 64 | + $iWorkflowId = KTUtil::arrayGet($_REQUEST, 'fWorkflowId'); | ||
| 65 | + $iStateId = KTUtil::arrayGet($_REQUEST, 'fStateId'); | ||
| 66 | + $iTransitionId = KTUtil::arrayGet($_REQUEST, 'fTransitionId'); | ||
| 67 | + | ||
| 68 | + if (!is_null($iWorkflowId)) { | ||
| 69 | + $oWorkflow =& KTWorkflow::get($iWorkflowId); | ||
| 70 | + if (!PEAR::isError($oWorkflow)) { | ||
| 71 | + $this->oWorkflow =& $oWorkflow; | ||
| 72 | + } | ||
| 73 | + } | ||
| 74 | + | ||
| 75 | + if (!is_null($iStateId)) { | ||
| 76 | + $oState =& KTWorkflowState::get($iStateId); | ||
| 77 | + if (!PEAR::isError($oState)) { | ||
| 78 | + $this->oState =& $oState; | ||
| 79 | + } | ||
| 80 | + } | ||
| 81 | + | ||
| 82 | + if (!is_null($iTransitionId)) { | ||
| 83 | + $oTransition =& KTWorkflowTransition::get($iTransitionId); | ||
| 84 | + if (!PEAR::isError($oTransition)) { | ||
| 85 | + $this->oTransition =& $oTransition; | ||
| 86 | + } | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + $this->aBreadcrumbs[] = array( | ||
| 90 | + 'url' => $_SERVER['PHP_SELF'], | ||
| 91 | + 'name' => _kt('Workflows'), | ||
| 92 | + ); | ||
| 93 | + | ||
| 94 | + if (!is_null($this->oWorkflow)) { | ||
| 95 | + $this->oPage->addPortlet(new WorkflowNavigationPortlet(_kt("Workflow Administration"), $this->oWorkflow)); | ||
| 96 | + | ||
| 97 | + $this->aBreadcrumbs[] = array( | ||
| 98 | + 'url' => KTUtil::addQueryStringSelf(sprintf('action=view&fWorkflowId=%d', $iWorkflowId)), | ||
| 99 | + 'name' => $this->oWorkflow->getName(), | ||
| 100 | + ); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + return $res; | ||
| 104 | + } | ||
| 105 | + | ||
| 106 | + function do_main() { | ||
| 107 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/list'); | ||
| 108 | + | ||
| 109 | + $aWorkflows = KTWorkflow::getList(); | ||
| 110 | + | ||
| 111 | + $oTemplate->setData(array( | ||
| 112 | + 'context' => $this, | ||
| 113 | + 'workflows' => $aWorkflows, | ||
| 114 | + )); | ||
| 115 | + return $oTemplate->render(); | ||
| 116 | + } | ||
| 117 | + | ||
| 118 | + function do_newWorkflow() { | ||
| 119 | + // subdispatch this to the NewWorkflowWizard. | ||
| 120 | + require_once(dirname(__FILE__) . '/workflow/newworkflow.inc.php'); | ||
| 121 | + | ||
| 122 | + $oSubDispatcher =& new KTNewWorkflowWizard; | ||
| 123 | + $oSubDispatcher->redispatch('wizard', null, $this); | ||
| 124 | + exit(0); | ||
| 125 | + } | ||
| 126 | + | ||
| 127 | + // -------------------- Overview ----------------- | ||
| 128 | + // basic view page. | ||
| 129 | + | ||
| 130 | + function do_view() { | ||
| 131 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/view'); | ||
| 132 | + | ||
| 133 | + $this->oPage->setBreadcrumbDetails(_kt("Overview")); | ||
| 134 | + | ||
| 135 | + if (!$this->oWorkflow->getIsEnabled()) { | ||
| 136 | + $this->addInfoMessage(sprintf(_kt("This workflow is currently marked as disabled. No new documents can be assigned to this workflow until it is enabled. To change this, please <a href=\"%s\">edit</a> the workflow's base properties."), KTUtil::addQueryString($_SERVER['PHP_SELF'], $this->meldPersistQuery("","editcore")))); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + if ($this->oWorkflow->getStartStateId() == false) { | ||
| 140 | + $this->addErrorMessage(sprintf(_kt("No start state is specified for this workflow. No new documents can be assigned to this workflow until one is assigned. To change this, please <a href=\"%s\">edit</a> the workflow's base properties."), KTUtil::addQueryString($_SERVER['PHP_SELF'], $this->meldPersistQuery("","editcore")))); | ||
| 141 | + } | ||
| 142 | + | ||
| 143 | + // for the basic view | ||
| 144 | + $start_state_id = $this->oWorkflow->getStartStateId(); | ||
| 145 | + $oState = KTWorkflowState::get($start_state_id); | ||
| 146 | + | ||
| 147 | + if (PEAR::isError($oState)) { | ||
| 148 | + $state_name = _kt('No starting state.'); | ||
| 149 | + } else { | ||
| 150 | + $state_name = $oState->getName(); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + | ||
| 154 | + | ||
| 155 | + $oTemplate->setData(array( | ||
| 156 | + 'context' => $this, | ||
| 157 | + 'workflow_name' => $this->oWorkflow->getName(), | ||
| 158 | + 'state_name' => $state_name, | ||
| 159 | + 'workflow' => $this->oWorkflow, | ||
| 160 | + )); | ||
| 161 | + return $oTemplate->render(); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + function form_coreedit() { | ||
| 165 | + $oForm = new KTForm; | ||
| 166 | + | ||
| 167 | + $oForm->setOptions(array( | ||
| 168 | + 'context' => $this, | ||
| 169 | + 'action' => 'setcore', | ||
| 170 | + 'fail_action' => 'editcore', | ||
| 171 | + 'cancel_action' => 'view', | ||
| 172 | + 'label' => _kt('Edit Workflow Details'), | ||
| 173 | + 'submit_label' => _kt('Update Workflow Details'), | ||
| 174 | + )); | ||
| 175 | + | ||
| 176 | + $oForm->setWidgets(array( | ||
| 177 | + array('ktcore.widgets.string',array( | ||
| 178 | + 'label' => _kt("Workflow Name"), | ||
| 179 | + 'description' => _kt("Each workflow must have a unique name."), | ||
| 180 | + 'name' => 'workflow_name', | ||
| 181 | + 'required' => true, | ||
| 182 | + 'value' => $this->oWorkflow->getName(), | ||
| 183 | + )), | ||
| 184 | + array('ktcore.widgets.entityselection', array( | ||
| 185 | + 'label' => _kt("Starting State"), | ||
| 186 | + 'description' => _kt('When a document has this workflow applied to it, which state should it initially have.'), | ||
| 187 | + 'name' => 'start_state', | ||
| 188 | + 'label_method' => 'getHumanName', | ||
| 189 | + 'vocab' => KTWorkflowState::getByWorkflow($this->oWorkflow), | ||
| 190 | + 'value' => $this->oWorkflow->getStartStateId(), | ||
| 191 | + 'required' => true, | ||
| 192 | + )), | ||
| 193 | + array('ktcore.widgets.boolean', array( | ||
| 194 | + 'label' => _kt('Enabled'), | ||
| 195 | + 'description' => _kt('If a workflow is disabled, no new documents may be placed in it. Documents which were previously in the workflow continue to be able to change state, however.'), | ||
| 196 | + 'name' => 'enabled', | ||
| 197 | + 'value' => $this->oWorkflow->getIsEnabled(), | ||
| 198 | + )), | ||
| 199 | + )); | ||
| 200 | + | ||
| 201 | + $oForm->setValidators(array( | ||
| 202 | + array('ktcore.validators.string', array( | ||
| 203 | + 'test' => 'workflow_name', | ||
| 204 | + 'output' => 'workflow_name', | ||
| 205 | + )), | ||
| 206 | + array('ktcore.validators.entity', array( | ||
| 207 | + 'test' => 'start_state', | ||
| 208 | + 'class' => 'KTWorkflowState', | ||
| 209 | + 'output' => 'start_state', | ||
| 210 | + )), | ||
| 211 | + array('ktcore.validators.boolean', array( | ||
| 212 | + 'test' => 'enabled', | ||
| 213 | + 'output' => 'enabled', | ||
| 214 | + )) | ||
| 215 | + )); | ||
| 216 | + | ||
| 217 | + return $oForm; | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + function do_editcore() { | ||
| 221 | + | ||
| 222 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/edit_core'); | ||
| 223 | + $this->oPage->setBreadcrumbDetails(_kt("Edit Details")); | ||
| 224 | + | ||
| 225 | + $oForm = $this->form_coreedit(); | ||
| 226 | + | ||
| 227 | + $oTemplate->setData(array( | ||
| 228 | + 'context' => $this, | ||
| 229 | + 'workflow_name' => $this->oWorkflow->getName(), | ||
| 230 | + 'edit_form' => $oForm, | ||
| 231 | + )); | ||
| 232 | + return $oTemplate->render(); | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + function do_setcore() { | ||
| 236 | + $oForm = $this->form_coreedit(); | ||
| 237 | + $res = $oForm->validate(); | ||
| 238 | + $data = $res['results']; | ||
| 239 | + $errors = $res['errors']; | ||
| 240 | + if (!empty($errors)) { | ||
| 241 | + $oForm->handleError(); | ||
| 242 | + } | ||
| 243 | + | ||
| 244 | + $this->startTransaction(); | ||
| 245 | + $this->oWorkflow->setName($data['workflow_name']); | ||
| 246 | + $this->oWorkflow->setHumanName($data['workflow_name']); | ||
| 247 | + $this->oWorkflow->setStartStateId($data['start_state']->getId()); | ||
| 248 | + $this->oWorkflow->setIsEnabled($data['enabled']); | ||
| 249 | + $res = $this->oWorkflow->update(); | ||
| 250 | + if (PEAR::isError($res)) { | ||
| 251 | + $oForm->handleError(sprintf(_kt("Failed to update workflow: %s"), $res->getMessage())); | ||
| 252 | + } | ||
| 253 | + | ||
| 254 | + $this->successRedirectTo("view",_kt("Workflow updated.")); | ||
| 255 | + } | ||
| 256 | + | ||
| 257 | + // ----------------- Basic - States & Transition --------------------- | ||
| 258 | + function do_basic() { | ||
| 259 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/basic_overview'); | ||
| 260 | + $this->aBreadcrumbs[] = array( | ||
| 261 | + 'url' => KTUtil::addQueryStringSelf($this->meldPersistQuery("", "basic")), | ||
| 262 | + 'name' => _kt("States and Transitions"), | ||
| 263 | + ); | ||
| 264 | + $this->oPage->setBreadcrumbDetails(_kt("Overview")); | ||
| 265 | + | ||
| 266 | + $aStates = KTWorkflowState::getByWorkflow($this->oWorkflow); | ||
| 267 | + $aTransitions = KTWorkflowTransition::getByWorkflow($this->oWorkflow); | ||
| 268 | + | ||
| 269 | + $oTemplate->setData(array( | ||
| 270 | + 'context' => $this, | ||
| 271 | + 'workflow_name' => $this->oWorkflow->getName(), | ||
| 272 | + 'states' => $aStates, | ||
| 273 | + 'transitions' => $aTransitions, | ||
| 274 | + )); | ||
| 275 | + return $oTemplate->render(); | ||
| 276 | + } | ||
| 277 | + | ||
| 278 | + function form_transitionconnections() { | ||
| 279 | + $oForm = new KTForm; | ||
| 280 | + $oForm->setOptions(array( | ||
| 281 | + 'label' => _kt('Configure Workflow Process'), | ||
| 282 | + 'description' => _kt('The process a document follows is controlled by the way that the transitions between states are setup. A document starts the workflow in the initial state, and then follows transitions between states. Which users can perform these transitions can be configured in the "Security" section.'), | ||
| 283 | + 'submit_label' => _kt('Update Process'), | ||
| 284 | + 'cancel_action' => 'basic', | ||
| 285 | + 'action' => 'setconnections', | ||
| 286 | + 'fail_action' => 'transitionconnections', // consistency - this is not really used. | ||
| 287 | + 'context' => $this, | ||
| 288 | + )); | ||
| 289 | + | ||
| 290 | + return $oForm; | ||
| 291 | + } | ||
| 292 | + | ||
| 293 | + function do_transitionconnections() { | ||
| 294 | + // we don't use a traditional form here, since the grid is too complex | ||
| 295 | + // and its essentially one-shot. | ||
| 296 | + // | ||
| 297 | + // | ||
| 298 | + $oForm = $this->form_transitionconnections(); | ||
| 299 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/configure_process'); | ||
| 300 | + | ||
| 301 | + // we want to re-use this for *subsets*. | ||
| 302 | + $transition_ids = KTUtil::arrayGet($_REQUEST, 'transition_ids'); | ||
| 303 | + $bRestrict = is_array($transition_ids); | ||
| 304 | + | ||
| 305 | + $transitions = KTWorkflowTransition::getByWorkflow($this->oWorkflow); | ||
| 306 | + $availability = array(); | ||
| 307 | + foreach ($transitions as $oTransition) { | ||
| 308 | + if ($bRestrict) { | ||
| 309 | + if ($transition_ids[$oTransition->getId()]) { | ||
| 310 | + $final_transitions[] = $oTransition; | ||
| 311 | + } else { | ||
| 312 | + continue; | ||
| 313 | + } | ||
| 314 | + } | ||
| 315 | + | ||
| 316 | + $sources = KTWorkflowAdminUtil::getSourceStates($oTransition, array('ids' => true)); | ||
| 317 | + $aSources = array(); | ||
| 318 | + foreach ($sources as $source) { $aSources[$source] = $source; } | ||
| 319 | + $availability[$oTransition->getId()] = $aSources; | ||
| 320 | + } | ||
| 321 | + | ||
| 322 | + | ||
| 323 | + if ($bRestrict) { | ||
| 324 | + $transitions = $final_transitions; | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + $oTemplate->setData(array( | ||
| 328 | + 'context' => $this, | ||
| 329 | + 'form' => $oForm, | ||
| 330 | + 'states' => KTWorkflowState::getByWorkflow($this->oWorkflow), | ||
| 331 | + 'transitions' => $transitions, | ||
| 332 | + 'availability' => $availability, | ||
| 333 | + )); | ||
| 334 | + | ||
| 335 | + return $oTemplate->render(); | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + function do_setconnections() { | ||
| 339 | + // we *must* ensure that transitions are not set to originate from their | ||
| 340 | + // destination. | ||
| 341 | + // | ||
| 342 | + // we can ignore it here, because its dealt with in workflowadminutil | ||
| 343 | + | ||
| 344 | + $to = (array) KTUtil::arrayGet($_REQUEST, 'fTo'); | ||
| 345 | + $from = (array) KTUtil::arrayGet($_REQUEST, 'fFrom'); | ||
| 346 | + | ||
| 347 | + // we do not trust any of this data. | ||
| 348 | + $states = KTWorkflowState::getByWorkflow($this->oWorkflow); | ||
| 349 | + $states = KTUtil::keyArray($states); | ||
| 350 | + $transitions = KTWorkflowTransition::getByWorkflow($this->oWorkflow); | ||
| 351 | + | ||
| 352 | + $this->startTransaction(); | ||
| 353 | + | ||
| 354 | + foreach ($transitions as $oTransition) { | ||
| 355 | + $dest_id = $to[$oTransition->getId()]; | ||
| 356 | + $oDestState = $states[$dest_id]; | ||
| 357 | + | ||
| 358 | + if (!is_null($oDestState)) { | ||
| 359 | + $oTransition->setTargetStateId($dest_id); | ||
| 360 | + $res = $oTransition->update(); | ||
| 361 | + if (PEAR::isError($res)) { | ||
| 362 | + $this->errorRedirectTo('basic', sprintf(_kt("Unexpected error updating transition: %s"), $res->getMessage())); | ||
| 363 | + } | ||
| 364 | + } | ||
| 365 | + | ||
| 366 | + // hook up source states. | ||
| 367 | + $source_state_ids = array(); | ||
| 368 | + $sources = (array) $from[$oTransition->getId()]; | ||
| 369 | + | ||
| 370 | + foreach ($sources as $state_id => $discard) { | ||
| 371 | + // test existence | ||
| 372 | + $oState = $states[$state_id]; | ||
| 373 | + if (!is_null($oState) && ($dest_id != $state_id)) { | ||
| 374 | + $source_state_ids[] = $oState->getId(); | ||
| 375 | + } | ||
| 376 | + } | ||
| 377 | + | ||
| 378 | + $res = KTWorkflowAdminUtil::saveTransitionSources($oTransition, $source_state_ids); | ||
| 379 | + if (PEAR::isError($res)) { | ||
| 380 | + $this->errorRedirectTo('basic', sprintf(_kt("Failed to set transition origins: %s"), $res->getMessage())); | ||
| 381 | + } | ||
| 382 | + } | ||
| 383 | + | ||
| 384 | + $this->successRedirectTo('basic', _kt("Workflow process updated.")); | ||
| 385 | + } | ||
| 386 | + | ||
| 387 | + function form_addstates() { | ||
| 388 | + $oForm = new KTForm; | ||
| 389 | + $oForm->setOptions(array( | ||
| 390 | + 'context' => $this, | ||
| 391 | + 'label' => _kt("Add New States"), | ||
| 392 | + 'submit_label' => _kt("Add States"), | ||
| 393 | + 'action' => 'createstates', | ||
| 394 | + 'cancel_action' => 'basic', | ||
| 395 | + 'fail_action' => 'addstates', | ||
| 396 | + )); | ||
| 397 | + | ||
| 398 | + $oForm->setWidgets(array( | ||
| 399 | + array('ktcore.widgets.text',array( | ||
| 400 | + 'label' => _kt('New States'), | ||
| 401 | + 'description' => _kt('As documents progress through their lifecycle, they pass through a number of <strong>states</strong>. These states describe a step in the process the document must follow. Examples of states include "reviewed","submitted" or "pending". Note that the first state you list is the one in which documents will start the workflow - this can be changed later on. Please enter a list of states, one per line. State names must be unique, and this includes states already in this workflow.'), | ||
| 402 | + 'required' => true, | ||
| 403 | + 'name' => 'states', | ||
| 404 | + 'rows' => 15, | ||
| 405 | + )), | ||
| 406 | + )); | ||
| 407 | + $oForm->setValidators(array( | ||
| 408 | + array('ktcore.validators.string', array( | ||
| 409 | + 'test' => 'states', | ||
| 410 | + 'output' => 'states', | ||
| 411 | + 'max_length' => 9999, | ||
| 412 | + )), | ||
| 413 | + )); | ||
| 414 | + | ||
| 415 | + return $oForm; | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + function do_addstates() { | ||
| 419 | + $oForm = $this->form_addstates(); | ||
| 420 | + | ||
| 421 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/add_states'); | ||
| 422 | + $oTemplate->setData(array( | ||
| 423 | + 'context' => $this, | ||
| 424 | + 'form' => $oForm | ||
| 425 | + )); | ||
| 426 | + return $oTemplate->render(); | ||
| 427 | + } | ||
| 428 | + | ||
| 429 | + function do_createstates() { | ||
| 430 | + $oForm = $this->form_addstates(); | ||
| 431 | + $res = $oForm->validate(); | ||
| 432 | + $data = $res['results']; | ||
| 433 | + $errors = $res['errors']; | ||
| 434 | + $extra_errors = array(); | ||
| 435 | + | ||
| 436 | + // we want to check for duplicates, empties, etc. | ||
| 437 | + | ||
| 438 | + $initial_states = (array) explode("\n", $data['states']); | ||
| 439 | + $failed = array(); | ||
| 440 | + $old_states = array(); | ||
| 441 | + $states = array(); | ||
| 442 | + foreach ($initial_states as $sName) { | ||
| 443 | + $state_name = trim($sName); | ||
| 444 | + if (empty($state_name)) { | ||
| 445 | + continue; | ||
| 446 | + } | ||
| 447 | + | ||
| 448 | + if ($states[$state_name]) { | ||
| 449 | + $failed[] = $state_name; | ||
| 450 | + continue; | ||
| 451 | + } | ||
| 452 | + | ||
| 453 | + // check for pre-existing states. | ||
| 454 | + $exists = KTWorkflowState::nameExists($sName, $this->oWorkflow); | ||
| 455 | + if ($exists) { | ||
| 456 | + $old_states[] = $sName; | ||
| 457 | + } | ||
| 458 | + | ||
| 459 | + $states[$state_name] = $state_name; | ||
| 460 | + } | ||
| 461 | + if (empty($states)) { | ||
| 462 | + $extra_errors['states'][] = _kt('You must provide at least one state name.'); | ||
| 463 | + } | ||
| 464 | + if (!empty($failed)) { | ||
| 465 | + $extra_errors['states'][] = sprintf(_kt("You cannot have duplicate state names: %s"), implode(', ', $failed)); | ||
| 466 | + } | ||
| 467 | + if (!empty($old_states)) { | ||
| 468 | + $extra_errors['states'][] = sprintf(_kt("You cannot use state names that are in use: %s"), implode(', ', $old_states)); | ||
| 469 | + } | ||
| 470 | + | ||
| 471 | + // handle any errors. | ||
| 472 | + if (!empty($errors) || !empty($extra_errors)) { | ||
| 473 | + $oForm->handleError(null, $extra_errors); | ||
| 474 | + } | ||
| 475 | + | ||
| 476 | + $this->startTransaction(); | ||
| 477 | + // now act | ||
| 478 | + foreach ($states as $state_name) { | ||
| 479 | + $oState = KTWorkflowState::createFromArray(array( | ||
| 480 | + 'workflowid' => $this->oWorkflow->getId(), | ||
| 481 | + 'name' => $state_name, | ||
| 482 | + 'humanname' => $state_name, | ||
| 483 | + )); | ||
| 484 | + if (PEAR::isError($oState)) { | ||
| 485 | + $oForm->handleError(sprintf(_kt("Unexpected failure creating state: %s"), $oState->getMessage())); | ||
| 486 | + } | ||
| 487 | + } | ||
| 488 | + | ||
| 489 | + $this->successRedirectTo('basic', _kt("New States Created.")); | ||
| 490 | + } | ||
| 491 | + | ||
| 492 | + | ||
| 493 | + function form_addtransitions() { | ||
| 494 | + $oForm = new KTForm; | ||
| 495 | + $oForm->setOptions(array( | ||
| 496 | + 'context' => $this, | ||
| 497 | + 'label' => _kt("Add New Transitions"), | ||
| 498 | + 'submit_label' => _kt("Add Transitions"), | ||
| 499 | + 'action' => 'createtransitions', | ||
| 500 | + 'cancel_action' => 'basic', | ||
| 501 | + 'fail_action' => 'addtransitions', | ||
| 502 | + )); | ||
| 503 | + | ||
| 504 | + $oForm->setWidgets(array( | ||
| 505 | + array('ktcore.widgets.text',array( | ||
| 506 | + 'label' => _kt('Transitions'), | ||
| 507 | + 'description' => _kt('In order to move between states, users will cause "transitions" to occur. These transitions represent processes followed, e.g. "review document", "distribute invoice" or "publish". Please enter a list of transitions, one per line. Transition names must be unique. You\'ll assign transitions to states in the next step.'), | ||
| 508 | + 'required' => false, | ||
| 509 | + 'name' => 'transitions', | ||
| 510 | + )), | ||
| 511 | + )); | ||
| 512 | + $oForm->setValidators(array( | ||
| 513 | + array('ktcore.validators.string', array( | ||
| 514 | + 'test' => 'transitions', | ||
| 515 | + 'output' => 'transitions', | ||
| 516 | + 'max_length' => 9999, | ||
| 517 | + )), | ||
| 518 | + )); | ||
| 519 | + | ||
| 520 | + return $oForm; | ||
| 521 | + } | ||
| 522 | + | ||
| 523 | + function do_addtransitions() { | ||
| 524 | + $oForm = $this->form_addtransitions(); | ||
| 525 | + | ||
| 526 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/add_transitions'); | ||
| 527 | + $oTemplate->setData(array( | ||
| 528 | + 'context' => $this, | ||
| 529 | + 'form' => $oForm | ||
| 530 | + )); | ||
| 531 | + return $oTemplate->render(); | ||
| 532 | + } | ||
| 533 | + | ||
| 534 | + function do_createtransitions() { | ||
| 535 | + $oForm = $this->form_addtransitions(); | ||
| 536 | + $res = $oForm->validate(); | ||
| 537 | + $data = $res['results']; | ||
| 538 | + $errors = $res['errors']; | ||
| 539 | + $extra_errors = array(); | ||
| 540 | + | ||
| 541 | + // we want to check for duplicates, empties, etc. | ||
| 542 | + | ||
| 543 | + $initial_transitions = (array) explode("\n", $data['transitions']); | ||
| 544 | + $failed = array(); | ||
| 545 | + $old_transitions = array(); | ||
| 546 | + $transitions = array(); | ||
| 547 | + foreach ($initial_transitions as $sName) { | ||
| 548 | + $transition_name = trim($sName); | ||
| 549 | + if (empty($transition_name)) { | ||
| 550 | + continue; | ||
| 551 | + } | ||
| 552 | + | ||
| 553 | + if ($transitions[$transition_name]) { | ||
| 554 | + $failed[] = $transition_name; | ||
| 555 | + continue; | ||
| 556 | + } | ||
| 557 | + | ||
| 558 | + // check for pre-existing states. | ||
| 559 | + $exists = KTWorkflowTransition::nameExists($sName, $this->oWorkflow); | ||
| 560 | + if ($exists) { | ||
| 561 | + $old_transitions[] = $sName; | ||
| 562 | + } | ||
| 563 | + | ||
| 564 | + $transitions[$transition_name] = $transition_name; | ||
| 565 | + } | ||
| 566 | + if (empty($transitions)) { | ||
| 567 | + $extra_errors['transitions'][] = _kt('You must provide at least one transition name.'); | ||
| 568 | + } | ||
| 569 | + if (!empty($failed)) { | ||
| 570 | + $extra_errors['transitions'][] = sprintf(_kt("You cannot have duplicate transition names: %s"), implode(', ', $failed)); | ||
| 571 | + } | ||
| 572 | + if (!empty($old_states)) { | ||
| 573 | + $extra_errors['transitions'][] = sprintf(_kt("You cannot use transition names that are in use: %s"), implode(', ', $old_transitions)); | ||
| 574 | + } | ||
| 575 | + | ||
| 576 | + // handle any errors. | ||
| 577 | + if (!empty($errors) || !empty($extra_errors)) { | ||
| 578 | + $oForm->handleError(null, $extra_errors); | ||
| 579 | + } | ||
| 580 | + | ||
| 581 | + $this->startTransaction(); | ||
| 582 | + $transition_ids = array(); | ||
| 583 | + $oState = KTWorkflowState::get($this->oWorkflow->getStartStateId()); | ||
| 584 | + foreach ($transitions as $transition_name) { | ||
| 585 | + $oTransition = KTWorkflowTransition::createFromArray(array( | ||
| 586 | + "WorkflowId" => $this->oWorkflow->getId(), | ||
| 587 | + "Name" => $transition_name, | ||
| 588 | + "HumanName" => $transition_name, | ||
| 589 | + "TargetStateId" => $oState->getId(), | ||
| 590 | + "GuardPermissionId" => null, | ||
| 591 | + "GuardGroupId" => null, | ||
| 592 | + "GuardRoleId" => null, | ||
| 593 | + "GuardConditionId" => null, | ||
| 594 | + )); | ||
| 595 | + if (PEAR::isError($oTransition)) { | ||
| 596 | + $oForm->handleError(sprintf(_kt("Unexpected failure creating transition: %s"), $oTransition->getMessage())); | ||
| 597 | + } | ||
| 598 | + $transition_ids[] = $oTransition->getId(); | ||
| 599 | + } | ||
| 600 | + | ||
| 601 | + $transition_ids_query = array(); | ||
| 602 | + foreach ($transition_ids as $id) { | ||
| 603 | + $transition_ids_query[] = sprintf('transition_ids[%s]=%s',$id, $id); | ||
| 604 | + } | ||
| 605 | + $transition_ids_query = implode('&', $transition_ids_query); | ||
| 606 | + | ||
| 607 | + $this->successRedirectTo('transitionconnections', _kt("New States Created."), $transition_ids_query); | ||
| 608 | + } | ||
| 609 | + | ||
| 610 | + function form_editstate($oState) { | ||
| 611 | + $oForm = new KTForm; | ||
| 612 | + | ||
| 613 | + $oForm->setOptions(array( | ||
| 614 | + 'context' => $this, | ||
| 615 | + 'label' => _kt('Edit State'), | ||
| 616 | + 'submit_label' => _kt('Update State'), | ||
| 617 | + 'action' => 'savestate', | ||
| 618 | + 'fail_action' => 'editstate', | ||
| 619 | + 'cancel_action' => 'basic', | ||
| 620 | + )); | ||
| 621 | + | ||
| 622 | + $oForm->setWidgets(array( | ||
| 623 | + array('ktcore.widgets.string', array( | ||
| 624 | + 'name' => 'name', | ||
| 625 | + 'label' => _kt('State Name'), | ||
| 626 | + 'description' => _kt('As documents progress through their lifecycle, they pass through a number of <strong>states</strong>. These states describe a step in the process the document must follow. Examples of states include "reviewed","submitted" or "pending". Note that the first state you list is the one in which documents will start the workflow - this can be changed later on. Please enter a list of states, one per line. State names must be unique, and this includes states already in this workflow.'), | ||
| 627 | + 'required' => true, | ||
| 628 | + 'value' => $oState->getName(), | ||
| 629 | + )), | ||
| 630 | + )); | ||
| 631 | + | ||
| 632 | + $oForm->setValidators(array( | ||
| 633 | + array('ktcore.validators.string', array( | ||
| 634 | + 'test' => 'name', | ||
| 635 | + 'output' => 'name', | ||
| 636 | + )), | ||
| 637 | + )); | ||
| 638 | + | ||
| 639 | + return $oForm; | ||
| 640 | + } | ||
| 641 | + | ||
| 642 | + function do_editstate() { | ||
| 643 | + // remember that we check for state, | ||
| 644 | + // and its null if none or an error was passed. | ||
| 645 | + if (is_null($this->oState)) { | ||
| 646 | + $this->errorRedirectTo('basic', _kt("No state specified.")); | ||
| 647 | + } | ||
| 648 | + | ||
| 649 | + $oTemplate =& $this->oValidator->validateTemplate('ktcore/workflow/admin/edit_state'); | ||
| 650 | + $this->oPage->setBreadcrumbDetails(_kt('Manage State Details')); | ||
| 651 | + | ||
| 652 | + $oForm = $this->form_editstate($this->oState); | ||
| 653 | + | ||
| 654 | + $oTemplate->setData(array( | ||
| 655 | + 'context' => $this, | ||
| 656 | + 'edit_form' => $oForm, | ||
| 657 | + )); | ||
| 658 | + | ||
| 659 | + return $oTemplate->render(); | ||
| 660 | + } | ||
| 661 | + | ||
| 662 | + function do_savestate() { | ||
| 663 | + $oForm = $this->form_editstate(); | ||
| 664 | + $res = $oForm->validate(); | ||
| 665 | + $data = $res['results']; | ||
| 666 | + $errors = $res['errors']; | ||
| 667 | + $extra_errors = array(); | ||
| 668 | + | ||
| 669 | + // check if any *other* states have this name. | ||
| 670 | + if ($data['name'] == $this->oState->getName()) { | ||
| 671 | + $this->successRedirectTo('editstate',_kt("No change in name.")); | ||
| 672 | + } | ||
| 673 | + | ||
| 674 | + // otherwise we're looking for something different if there's a conflict. | ||
| 675 | + | ||
| 676 | + if (KTWorkflowState::nameExists($data['name'], $this->oWorkflow)) { | ||
| 677 | + $extra_errors['name'][] = _kt('There is already a state with that name in this workflow.'); | ||
| 678 | + } | ||
| 679 | + | ||
| 680 | + if (!empty($errors) || !empty($extra_errors)) { | ||
| 681 | + $oForm->handleError(null, $extra_errors); | ||
| 682 | + } | ||
| 683 | + | ||
| 684 | + $this->startTransaction(); | ||
| 685 | + | ||
| 686 | + $this->oState->setName($data['name']); | ||
| 687 | + $this->oState->setHumanName($data['name']); | ||
| 688 | + $res = $this->oState->update(); | ||
| 689 | + | ||
| 690 | + if (PEAR::isError($res)) { | ||
| 691 | + $oForm->handleError(sprintf(_kt("Unable to update state: %s"), $res->getMessage())); | ||
| 692 | + } | ||
| 693 | + | ||
| 694 | + $this->successRedirectTo('editstate', _kt("State updated.")); | ||
| 695 | + } | ||
| 696 | + | ||
| 697 | + function form_edittransition($oTransition) { | ||
| 698 | + $oForm = new KTForm; | ||
| 699 | + | ||
| 700 | + $oForm->setOptions(array( | ||
| 701 | + 'context' => $this, | ||
| 702 | + 'label' => _kt('Edit Transition'), | ||
| 703 | + 'submit_label' => _kt('Update Transition'), | ||
| 704 | + 'action' => 'savetransition', | ||
| 705 | + 'fail_action' => 'edittransition', | ||
| 706 | + 'cancel_action' => 'basic', | ||
| 707 | + )); | ||
| 708 | + | ||
| 709 | + $oForm->setWidgets(array( | ||
| 710 | + array('ktcore.widgets.string', array( | ||
| 711 | + 'name' => 'name', | ||
| 712 | + 'label' => _kt('Transition Name'), | ||
| 713 | + 'description' => _kt('In order to move between states, users will cause "transitions" to occur. These transitions represent processes followed, e.g. "review document", "distribute invoice" or "publish". Transition names must be unique within the workflow (e.g. within this workflow, you can only have one transition called "publish")'), | ||
| 714 | + 'required' => true, | ||
| 715 | + 'value' => $oTransition->getName(), | ||
| 716 | + )), | ||
| 717 | + )); | ||
| 718 | + | ||
| 719 | + $oForm->setValidators(array( | ||
| 720 | + array('ktcore.validators.string', array( | ||
| 721 | + 'test' => 'name', | ||
| 722 | + 'output' => 'name', | ||
| 723 | + )), | ||
| 724 | + )); | ||
| 725 | + | ||
| 726 | + return $oForm; | ||
| 727 | + } | ||
| 728 | + | ||
| 729 | + function do_edittransition() { | ||
| 730 | + // remember that we check for state, | ||
| 731 | + // and its null if none or an error was passed. | ||
| 732 | + if (is_null($this->oTransition)) { | ||
| 733 | + $this->errorRedirectTo('basic', _kt("No transition specified.")); | ||
| 734 | + } | ||
| 735 | + | ||
| 736 | + $oTemplate =& $this->oValidator->validateTemplate('ktcore/workflow/admin/edit_transition'); | ||
| 737 | + $this->oPage->setBreadcrumbDetails(_kt('Manage Transition')); | ||
| 738 | + | ||
| 739 | + $oForm = $this->form_edittransition($this->oTransition); | ||
| 740 | + | ||
| 741 | + $oTemplate->setData(array( | ||
| 742 | + 'context' => $this, | ||
| 743 | + 'edit_form' => $oForm, | ||
| 744 | + )); | ||
| 745 | + | ||
| 746 | + return $oTemplate->render(); | ||
| 747 | + } | ||
| 748 | + | ||
| 749 | + function do_savetransition() { | ||
| 750 | + $oForm = $this->form_edittransition(); | ||
| 751 | + $res = $oForm->validate(); | ||
| 752 | + $data = $res['results']; | ||
| 753 | + $errors = $res['errors']; | ||
| 754 | + $extra_errors = array(); | ||
| 755 | + | ||
| 756 | + // check if any *other* states have this name. | ||
| 757 | + if ($data['name'] == $this->oTransition->getName()) { | ||
| 758 | + $this->successRedirectTo('edittransition',_kt("No change in name.")); | ||
| 759 | + } | ||
| 760 | + | ||
| 761 | + // otherwise we're looking for something different if there's a conflict. | ||
| 762 | + | ||
| 763 | + if (KTWorkflowTransitions::nameExists($data['name'], $this->oWorkflow)) { | ||
| 764 | + $extra_errors['name'][] = _kt('There is already a transition with that name in this workflow.'); | ||
| 765 | + } | ||
| 766 | + | ||
| 767 | + if (!empty($errors) || !empty($extra_errors)) { | ||
| 768 | + $oForm->handleError(null, $extra_errors); | ||
| 769 | + } | ||
| 770 | + | ||
| 771 | + $this->startTransaction(); | ||
| 772 | + | ||
| 773 | + $this->oTransition->setName($data['name']); | ||
| 774 | + $this->oTransition->setHumanName($data['name']); | ||
| 775 | + $res = $this->oTransition->update(); | ||
| 776 | + | ||
| 777 | + if (PEAR::isError($res)) { | ||
| 778 | + $oForm->handleError(sprintf(_kt("Unable to update transition: %s"), $res->getMessage())); | ||
| 779 | + } | ||
| 780 | + | ||
| 781 | + $this->successRedirectTo('transition', _kt("Transition updated.")); | ||
| 782 | + } | ||
| 783 | + | ||
| 784 | + // ----------------- Security --------------------- | ||
| 785 | + function do_security() { | ||
| 786 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/security_overview'); | ||
| 787 | + $this->oPage->setBreadcrumbDetails(_kt("Security")); | ||
| 788 | + | ||
| 789 | + | ||
| 790 | + $oTemplate->setData(array( | ||
| 791 | + 'context' => $this, | ||
| 792 | + 'workflow_name' => $this->oWorkflow->getName(), | ||
| 793 | + )); | ||
| 794 | + return $oTemplate->render(); | ||
| 795 | + } | ||
| 796 | + | ||
| 797 | + | ||
| 798 | + // == PERMISSIONS | ||
| 799 | + function do_permissionsoverview() { | ||
| 800 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/permissions_overview'); | ||
| 801 | + | ||
| 802 | + // we want to give a complete overview. | ||
| 803 | + // this involves a grid of: | ||
| 804 | + // permission permissions | ||
| 805 | + // state x - | ||
| 806 | + // state - x | ||
| 807 | + | ||
| 808 | + $aStates = KTWorkflowState::getByWorkflow($this->oWorkflow); | ||
| 809 | + $aUsefulPermissions = KTPermission::getDocumentRelevantList(); | ||
| 810 | + $aPermissionGrid = array(); | ||
| 811 | + $aControllers = array(); | ||
| 812 | + foreach ($aStates as $oState) { | ||
| 813 | + $perms = array(); | ||
| 814 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($oState); | ||
| 815 | + $aControllers[$oState->getId()] = (!empty($aStatePermAssigns)); | ||
| 816 | + | ||
| 817 | + foreach ($aStatePermAssigns as $oPermAssign) { | ||
| 818 | + $perms[$oPermAssign->getPermissionId()] = $oPermAssign; // we only care about non-null in *this* map. | ||
| 819 | + } | ||
| 820 | + | ||
| 821 | + $aPermissionGrid[$oState->getId()] = $perms; | ||
| 822 | + } | ||
| 823 | + | ||
| 824 | + $oTemplate->setData(array( | ||
| 825 | + 'context' => $this, | ||
| 826 | + 'controllers' => $aControllers, | ||
| 827 | + 'perm_grid' => $aPermissionGrid, | ||
| 828 | + 'perms' => $aUsefulPermissions, | ||
| 829 | + 'states' => $aStates, | ||
| 830 | + )); | ||
| 831 | + return $oTemplate->render(); | ||
| 832 | + } | ||
| 833 | + | ||
| 834 | + function form_managepermissions() { | ||
| 835 | + $oForm = new KTForm; | ||
| 836 | + $oForm->setOptions(array( | ||
| 837 | + 'label' => _kt("Controlled Permissions"), | ||
| 838 | + 'submit_label' => _kt("Set controlled permissions"), | ||
| 839 | + 'action' => 'setcontrolledpermissions', | ||
| 840 | + 'fail_action' => 'managepermissions', | ||
| 841 | + 'cancel_action' => 'permissionsoverview', | ||
| 842 | + 'context' => $this, | ||
| 843 | + )); | ||
| 844 | + | ||
| 845 | + return $oForm; | ||
| 846 | + } | ||
| 847 | + | ||
| 848 | + // == PERMISSIONS | ||
| 849 | + function do_managepermissions() { | ||
| 850 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/managepermissions'); | ||
| 851 | + | ||
| 852 | + $oForm = $this->form_managepermissions(); | ||
| 853 | + | ||
| 854 | + $aUsefulPermissions = KTPermission::getDocumentRelevantList(); | ||
| 855 | + $aPermissionGrid = array(); | ||
| 856 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($this->oState); | ||
| 857 | + | ||
| 858 | + foreach ($aStatePermAssigns as $oPermAssign) { | ||
| 859 | + $aPermissionGrid[$oPermAssign->getPermissionId()] = $oPermAssign; | ||
| 860 | + } | ||
| 861 | + | ||
| 862 | + $oTemplate->setData(array( | ||
| 863 | + 'context' => $this, | ||
| 864 | + 'perm_grid' => $aPermissionGrid, | ||
| 865 | + 'perms' => $aUsefulPermissions, | ||
| 866 | + 'form' => $oForm, | ||
| 867 | + )); | ||
| 868 | + return $oTemplate->render(); | ||
| 869 | + } | ||
| 870 | + | ||
| 871 | + function do_setcontrolledpermissions() { | ||
| 872 | + $active = (array) KTUtil::arrayGet($_REQUEST, 'fControlled'); | ||
| 873 | + | ||
| 874 | + $aUsefulPerms = KTPermission::getDocumentRelevantList(); | ||
| 875 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($this->oState); | ||
| 876 | + $aStatePermAssigns = KTUtil::keyArray($aStatePermAssigns, 'getPermissionId'); | ||
| 877 | + $assigns = array(); | ||
| 878 | + | ||
| 879 | + $this->startTransaction(); | ||
| 880 | + // delete those who don't know want | ||
| 881 | + // create those we don't have. | ||
| 882 | + | ||
| 883 | + foreach ($aStatePermAssigns as $perm_id => $assign) { | ||
| 884 | + if (!$active[$perm_id]) { | ||
| 885 | + $assign->delete(); | ||
| 886 | + } | ||
| 887 | + } | ||
| 888 | + $emptydescriptor = KTPermissionUtil::getOrCreateDescriptor(array()); | ||
| 889 | + if (PEAR::isError($emptydescriptor)) { | ||
| 890 | + $this->errorRedirectTo("managepermissions", sprintf(_kt("Failed to create assignment: %s"), $emptydescriptor->getMessage())); | ||
| 891 | + } | ||
| 892 | + foreach ($active as $perm_id => $discard) { | ||
| 893 | + if (!$aStatePermAssigns[$perm_id]) { | ||
| 894 | + $assign = KTWorkflowStatePermissionAssignment::createFromArray(array( | ||
| 895 | + "iStateId" => $this->oState->getId(), | ||
| 896 | + "iPermissionId" => $perm_id, | ||
| 897 | + "iDescriptorId" => $emptydescriptor->getId(), | ||
| 898 | + )); | ||
| 899 | + if (PEAR::isError($assign)) { | ||
| 900 | + $this->errorRedirectTo("managepermissions", sprintf(_kt("Failed to create assignment: %s"), $assign->getMessage())); | ||
| 901 | + } | ||
| 902 | + } | ||
| 903 | + } | ||
| 904 | + | ||
| 905 | + $this->successRedirectTo("managepermissions", _kt("Controlled permission updated.")); | ||
| 906 | + } | ||
| 907 | + | ||
| 908 | + // == PERMISSIONS | ||
| 909 | + function do_allocatepermissions() { | ||
| 910 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/allocate_permissions'); | ||
| 911 | + | ||
| 912 | + $oForm = $this->form_managepermissions(); | ||
| 913 | + | ||
| 914 | + $aUsefulPermissions = KTPermission::getDocumentRelevantList(); | ||
| 915 | + $aPermissionGrid = array(); | ||
| 916 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($this->oState); | ||
| 917 | + | ||
| 918 | + foreach ($aStatePermAssigns as $oPermAssign) { | ||
| 919 | + $aPermissionGrid[$oPermAssign->getPermissionId()] = $oPermAssign; | ||
| 920 | + } | ||
| 921 | + | ||
| 922 | + $aPermissionsToJSON = array(); | ||
| 923 | + foreach($aUsefulPermissions as $oP) { | ||
| 924 | + $perm_id = $oP->getId(); | ||
| 925 | + if ($aPermissionGrid[$perm_id]) { | ||
| 926 | + $aPermissionsToJSON[] = array('id'=>$oP->getId(), 'name'=>$oP->getHumanName()); | ||
| 927 | + } | ||
| 928 | + } | ||
| 929 | + | ||
| 930 | + $oJSON = new Services_JSON; | ||
| 931 | + $sJSONPermissions = $oJSON->encode($aPermissionsToJSON); | ||
| 932 | + | ||
| 933 | + $oTemplate->setData(array( | ||
| 934 | + 'context' => $this, | ||
| 935 | + 'perm_grid' => $aPermissionGrid, | ||
| 936 | + 'perms' => $aUsefulPermissions, | ||
| 937 | + 'form' => $oForm, | ||
| 938 | + 'jsonpermissions' => $sJSONPermissions, | ||
| 939 | + 'args' => $this->meldPersistQuery("","setpermissionallocations",true), | ||
| 940 | + )); | ||
| 941 | + return $oTemplate->render(); | ||
| 942 | + } | ||
| 943 | + | ||
| 944 | + // JSON helper. from permissions. | ||
| 945 | + | ||
| 946 | + function &_getPermissionsMap() { | ||
| 947 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($this->oState); | ||
| 948 | + $aPermissionsMap = array('role'=>array(), 'group'=>array()); | ||
| 949 | + | ||
| 950 | + foreach ($aStatePermAssigns as $oPermAssign) { | ||
| 951 | + $oDescriptor = KTPermissionDescriptor::get($oPermAssign->getDescriptorId()); | ||
| 952 | + $iPermissionId = $oPermAssign->getPermissionId(); | ||
| 953 | + | ||
| 954 | + // groups | ||
| 955 | + $aGroupIds = $oDescriptor->getGroups(); | ||
| 956 | + foreach ($aGroupIds as $iId) { | ||
| 957 | + $aPermissionsMap['group'][$iId][$iPermissionId] = true; | ||
| 958 | + } | ||
| 959 | + | ||
| 960 | + // roles | ||
| 961 | + $aRoleIds = $oDescriptor->getRoles(); | ||
| 962 | + foreach ($aRoleIds as $iId) { | ||
| 963 | + $aPermissionsMap['role'][$iId][$iPermissionId] = true; | ||
| 964 | + } | ||
| 965 | + } | ||
| 966 | + return $aPermissionsMap; | ||
| 967 | + } | ||
| 968 | + | ||
| 969 | + function json_getEntities($optFilter = null) { | ||
| 970 | + $sFilter = KTUtil::arrayGet($_REQUEST, 'filter', false); | ||
| 971 | + if($sFilter == false && $optFilter != null) { | ||
| 972 | + $sFilter = $optFilter; | ||
| 973 | + } | ||
| 974 | + | ||
| 975 | + $bSelected = KTUtil::arrayGet($_REQUEST, 'selected', false); | ||
| 976 | + | ||
| 977 | + $aEntityList = array('off'=>'-- Please filter --'); | ||
| 978 | + | ||
| 979 | + // get permissions map | ||
| 980 | + $aPermissionsMap =& $this->_getPermissionsMap(); | ||
| 981 | + | ||
| 982 | + if($bSelected || $sFilter && trim($sFilter)) { | ||
| 983 | + if(!$bSelected) { | ||
| 984 | + $aEntityList = array(); | ||
| 985 | + } | ||
| 986 | + | ||
| 987 | + $aGroups = Group::getList(sprintf('name like "%%%s%%"', $sFilter)); | ||
| 988 | + foreach($aGroups as $oGroup) { | ||
| 989 | + $aPerm = @array_keys($aPermissionsMap['group'][$oGroup->getId()]); | ||
| 990 | + if(!is_array($aPerm)) { | ||
| 991 | + $aPerm = array(); | ||
| 992 | + } | ||
| 993 | + | ||
| 994 | + if($bSelected) { | ||
| 995 | + if(count($aPerm)) | ||
| 996 | + $aEntityList['g'.$oGroup->getId()] = array('type' => 'group', | ||
| 997 | + 'display' => 'Group: ' . $oGroup->getName(), | ||
| 998 | + 'name' => $oGroup->getName(), | ||
| 999 | + 'permissions' => $aPerm, | ||
| 1000 | + 'id' => $oGroup->getId(), | ||
| 1001 | + 'selected' => true); | ||
| 1002 | + } else { | ||
| 1003 | + $aEntityList['g'.$oGroup->getId()] = array('type' => 'group', | ||
| 1004 | + 'display' => 'Group: ' . $oGroup->getName(), | ||
| 1005 | + 'name' => $oGroup->getName(), | ||
| 1006 | + 'permissions' => $aPerm, | ||
| 1007 | + 'id' => $oGroup->getId()); | ||
| 1008 | + } | ||
| 1009 | + } | ||
| 1010 | + | ||
| 1011 | + $aRoles = Role::getList(sprintf('name like "%%%s%%"', $sFilter)); | ||
| 1012 | + foreach($aRoles as $oRole) { | ||
| 1013 | + $aPerm = @array_keys($aPermissionsMap['role'][$oRole->getId()]); | ||
| 1014 | + if(!is_array($aPerm)) { | ||
| 1015 | + $aPerm = array(); | ||
| 1016 | + } | ||
| 1017 | + | ||
| 1018 | + if($bSelected) { | ||
| 1019 | + if(count($aPerm)) | ||
| 1020 | + $aEntityList['r'.$oRole->getId()] = array('type' => 'role', | ||
| 1021 | + 'display' => 'Role: ' . $oRole->getName(), | ||
| 1022 | + 'name' => $oRole->getName(), | ||
| 1023 | + 'permissions' => $aPerm, | ||
| 1024 | + 'id' => $oRole->getId(), | ||
| 1025 | + 'selected' => true); | ||
| 1026 | + } else { | ||
| 1027 | + $aEntityList['r'.$oRole->getId()] = array('type' => 'role', | ||
| 1028 | + 'display' => 'Role: ' . $oRole->getName(), | ||
| 1029 | + 'name' => $oRole->getName(), | ||
| 1030 | + 'permissions' => $aPerm, | ||
| 1031 | + 'id' => $oRole->getId()); | ||
| 1032 | + } | ||
| 1033 | + } | ||
| 1034 | + } | ||
| 1035 | + return $aEntityList; | ||
| 1036 | + } | ||
| 1037 | + | ||
| 1038 | + | ||
| 1039 | + function do_setpermissionallocations() { | ||
| 1040 | + $aPermissionAllowed = (array) KTUtil::arrayGet($_REQUEST, 'foo'); // thanks BD. | ||
| 1041 | + | ||
| 1042 | + $this->startTransaction(); | ||
| 1043 | + | ||
| 1044 | + $aStatePermAssigns = KTWorkflowStatePermissionAssignment::getByState($this->oState); | ||
| 1045 | + | ||
| 1046 | + // we now walk the alloc'd perms, and go. | ||
| 1047 | + foreach ($aStatePermAssigns as $oPermAssign) { | ||
| 1048 | + $aAllowed = (array) $aPermissionAllowed[$oPermAssign->getPermissionId()]; // is already role, group, etc. | ||
| 1049 | + $oDescriptor = KTPermissionUtil::getOrCreateDescriptor($aAllowed); | ||
| 1050 | + if (PEAR::isError($oDescriptor)) { $this->errorRedirectTo('allocatepermissions', _kt('Failed to allocate as specified.')); } | ||
| 1051 | + | ||
| 1052 | + $oPermAssign->setDescriptorId($oDescriptor->getId()); | ||
| 1053 | + $res = $oPermAssign->update(); | ||
| 1054 | + if (PEAR::isError($res)) { $this->errorRedirectTo('allocatepermissions', _kt('Failed to allocate as specified.')); } | ||
| 1055 | + } | ||
| 1056 | + | ||
| 1057 | + KTPermissionUtil::updatePermissionLookupForState($oState); | ||
| 1058 | + | ||
| 1059 | + $this->successRedirectTo('managepermissions', _kt('Permissions Allocated.')); | ||
| 1060 | + } | ||
| 1061 | + | ||
| 1062 | + | ||
| 1063 | + // ----------------- Effects --------------------- | ||
| 1064 | + function do_effects() { | ||
| 1065 | + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/effects_overview'); | ||
| 1066 | + $this->oPage->setBreadcrumbDetails(_kt("Workflow Effects")); | ||
| 1067 | + | ||
| 1068 | + | ||
| 1069 | + $oTemplate->setData(array( | ||
| 1070 | + 'context' => $this, | ||
| 1071 | + 'workflow_name' => $this->oWorkflow->getName(), | ||
| 1072 | + )); | ||
| 1073 | + return $oTemplate->render(); | ||
| 1074 | + } | ||
| 1075 | +} | ||
| 1076 | + | ||
| 1077 | +?> |
sql/mysql/upgrade/3.1.6/workflow-sanity.sql
0 โ 100644
templates/ktcore/workflow/admin/add_states.smarty
0 โ 100644
templates/ktcore/workflow/admin/add_transitions.smarty
0 โ 100644
templates/ktcore/workflow/admin/allocate_permissions.smarty
0 โ 100644
| 1 | + | ||
| 2 | +{$context->oPage->requireJSResource("resources/js/jsonlookup.js")} | ||
| 3 | +{$context->oPage->requireJSResource("resources/js/permissions.js")} | ||
| 4 | + | ||
| 5 | +{capture assign=sJavascript}initializePermissions('entities', '{addQS context=$context}action=json&json_action=getEntities{/addQS}', {$jsonpermissions});{/capture} | ||
| 6 | +{$context->oPage->requireJSStandalone($sJavascript)} | ||
| 7 | + | ||
| 8 | +<form action="{$smarty.server.PHP_SELF}" method="POST"> | ||
| 9 | +<div class="field"> | ||
| 10 | + | ||
| 11 | + <p class="descriptiveText">{i18n}Select roles and groups for whom you wish to change permission assignement from the box on the left, and move them over to the box on the right using the button with right-pointing arrows. You can then allocate or remove permissions from these entities and save by pressing the 'Update Permission Assignments' button'.{/i18n}</p> | ||
| 12 | + | ||
| 13 | +<table> | ||
| 14 | + | ||
| 15 | +<thead> | ||
| 16 | + <tr> | ||
| 17 | + <td style="width:45%"><label for="select_{$name}_avail">Available Entities</label></td> | ||
| 18 | + <td style="width:10%"> </td> | ||
| 19 | + <td style="width:45%"><label for="select_{$name}_assigned">Assigned Entities</label></td> | ||
| 20 | + </tr> | ||
| 21 | +</thead> | ||
| 22 | + | ||
| 23 | +<tbody> | ||
| 24 | + <tr> | ||
| 25 | + <td style="vertical-align: top"> | ||
| 26 | + | ||
| 27 | + <select name="entities" id="select_entities_avail" multiple="true"size="5"> | ||
| 28 | + </select> | ||
| 29 | + | ||
| 30 | + <div><label for="filter_entities_avail">Filter</label><input type="text" id="filter_entities_avail" /><br/><a href="#" id="entities_show_all">Show All</a></div> | ||
| 31 | + </td> | ||
| 32 | + | ||
| 33 | + <td> | ||
| 34 | + | ||
| 35 | + <input type="button" id="entities_add" value="»" /> | ||
| 36 | + <br /><br/> | ||
| 37 | + <input type="button" id="entities_remove" value="«" /> | ||
| 38 | + | ||
| 39 | + </td> | ||
| 40 | + | ||
| 41 | + <td style="vertical-align: top"> | ||
| 42 | + | ||
| 43 | + <select name="entities" id="select_entities_assigned" multiple="true"size="5"> | ||
| 44 | + </select> | ||
| 45 | + <div><label for="filter_entities_assigned">Filter</label><input type="text" id="filter_entities_assigned" /></div> | ||
| 46 | + </td> | ||
| 47 | + </tr> | ||
| 48 | +</tbody> | ||
| 49 | +</table> | ||
| 50 | + | ||
| 51 | + <input name="entities_items_added" id="entities_items_added" type="hidden" /> | ||
| 52 | + <input name="entities_items_removed" id="entities_items_removed" type="hidden" /> | ||
| 53 | + | ||
| 54 | + <input type="hidden" name="kt_core_fieldsets_expect[entities]" value ="1" /> | ||
| 55 | +</div> | ||
| 56 | + | ||
| 57 | + | ||
| 58 | +<div id="permissions_table_container"></div> | ||
| 59 | + | ||
| 60 | +{foreach from=$args key=k item=v} | ||
| 61 | + <input type="hidden" name="{$k}" value="{$v}" /> | ||
| 62 | +{/foreach} | ||
| 63 | +<div id="submitButtons" class="form_actions"> | ||
| 64 | + <input type="submit" value="{i18n}Update Workflow Permissions{/i18n}" /> | ||
| 65 | + <a class="form_cancel" href="{addQS}action=managepermissions{/addQS}">{i18n}Cancel{/i18n}</a> | ||
| 66 | +</div> | ||
| 67 | +</form> | ||
| 68 | + | ||
| 69 | + |
templates/ktcore/workflow/admin/basic_overview.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_name=$workflow_name}States and Transitions: #name#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">The core of a workflow is the <strong>process</strong> | ||
| 4 | +that documents in that workflow follow. These processes are made up of <strong>states</strong> | ||
| 5 | +(which documents are in, e.g. "reviewed" or "published") and <strong>transitions</strong> | ||
| 6 | +which documents follow (e.g. "submit for review" or "publish").</p> | ||
| 7 | + | ||
| 8 | +<p><a class="ktAction ktEdit ktActionDescribed" href="{addQS context=$context}action=transitionconnections{/addQS}">Configure Workflow Process</a> | ||
| 9 | +<a href="{addQS context=$context}action=transitionconnections{/addQS}">Configure Workflow Process</a> <span class="descriptiveText">(e.g. which transitions lead to which states)</span></p> | ||
| 10 | + | ||
| 11 | +<div style="width: 40%; float: left; "> | ||
| 12 | + | ||
| 13 | +<h3>States</h3> | ||
| 14 | + <a class="ktAction ktAdd ktActionDescribed" href="{addQS context=$context}action=addstates{/addQS}">Add New States</a> | ||
| 15 | + <a href="{addQS context=$context}action=addstates{/addQS}">Add New States</a><br /><br /> | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + <table class="kt_collection" cellspacing="0"> | ||
| 19 | + <thead> | ||
| 20 | + <tr> | ||
| 21 | + <th>State Name</th> | ||
| 22 | + <th>Edit</th> | ||
| 23 | + <th>Delete</th> | ||
| 24 | + </tr> | ||
| 25 | + </thead> | ||
| 26 | + <tbody> | ||
| 27 | + {foreach from=$states item=oState} | ||
| 28 | + <tr> | ||
| 29 | + <td> | ||
| 30 | + {$oState->getName()} | ||
| 31 | + </td> | ||
| 32 | + <td> | ||
| 33 | + <a class="ktAction ktEdit" href="{addQS context=$context}action=editstate&fStateId={$oState->getId()}{/addQS}">Edit State</a> | ||
| 34 | + </td> | ||
| 35 | + <td> | ||
| 36 | + <a class="ktAction ktDelete" href="{addQS context=$context}action=deletestate&fStateId={$oState->getId()}{/addQS}">Delete State</a> | ||
| 37 | + </td> | ||
| 38 | + </tr> | ||
| 39 | + {/foreach} | ||
| 40 | + </tbody> | ||
| 41 | + </table> | ||
| 42 | +</div> | ||
| 43 | +<div style="float: left; width: 40%; margin-left: 1em;"> | ||
| 44 | +<h3>Transitions</h3> | ||
| 45 | + | ||
| 46 | + <a class="ktAction ktAdd ktActionDescribed" href="{addQS context=$context}action=addtransitions{/addQS}">Add New Transitions</a> | ||
| 47 | + <a href="{addQS context=$context}action=addtransitions{/addQS}">Add New Transitions</a><br /><br /> | ||
| 48 | + | ||
| 49 | + | ||
| 50 | + | ||
| 51 | + <table class="kt_collection" cellspacing="0"> | ||
| 52 | + <thead> | ||
| 53 | + <tr> | ||
| 54 | + <th>Transition Name</th> | ||
| 55 | + <th>Edit</th> | ||
| 56 | + <th>Delete</th> | ||
| 57 | + </tr> | ||
| 58 | + </thead> | ||
| 59 | + <tbody> | ||
| 60 | + {foreach from=$transitions item=oTransition} | ||
| 61 | + <tr> | ||
| 62 | + <td> | ||
| 63 | + {$oTransition->getName()} | ||
| 64 | + </td> | ||
| 65 | + <td> | ||
| 66 | + <a class="ktAction ktEdit" href="{addQS context=$context}action=edittransition&fTransitionId={$oTransition->getId()}{/addQS}">Edit</a> | ||
| 67 | + </td> | ||
| 68 | + <td> | ||
| 69 | + <a class="ktAction ktDelete" href="{addQS context=$context}action=deletetransition&fTransitionId={$oTransition->getId()}{/addQS}">Delete</a> | ||
| 70 | + </td> | ||
| 71 | + </tr> | ||
| 72 | + {/foreach} | ||
| 73 | + </tbody> | ||
| 74 | + </table> | ||
| 75 | +</div> |
templates/ktcore/workflow/admin/configure_process.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_name=$context->oWorkflow->getName()}States and Transitions: #name#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +{capture assign=widgets} | ||
| 4 | + | ||
| 5 | + | ||
| 6 | + <table class="kt_collection" cellspacing="0"> | ||
| 7 | + <thead> | ||
| 8 | + <tr> | ||
| 9 | + <th>{i18n}Transition{/i18n}</th> | ||
| 10 | + <th>{i18n}Leads to state{/i18n}</th> | ||
| 11 | + {foreach from=$states item=oState} | ||
| 12 | + <th>{$oState->getName()}</th> | ||
| 13 | + {/foreach} | ||
| 14 | + </tr> | ||
| 15 | + </thead> | ||
| 16 | + <tbody> | ||
| 17 | + {foreach from=$transitions item=oTransition} | ||
| 18 | + <tr class="row {cycle values=odd,even}"> | ||
| 19 | + <td>{$oTransition->getName()}</td> | ||
| 20 | + <td><select name="fTo[{$oTransition->getId()}]"> | ||
| 21 | + {foreach from=$states item=oState} | ||
| 22 | + <option value="{$oState->getId()}" | ||
| 23 | + {if ($oState->getId() == $oTransition->getTargetStateId())}selected="true"{/if} | ||
| 24 | + >{$oState->getName()}</option> | ||
| 25 | + {/foreach} | ||
| 26 | + </select></td> | ||
| 27 | + {foreach from=$states item=oState} | ||
| 28 | + {assign value=$oTransition->getId() var=trans_id} | ||
| 29 | + {assign value=$oState->getId() var=state_id} | ||
| 30 | + <td><input type="checkbox" name="fFrom[{$trans_id}][{$state_id}]" | ||
| 31 | + {if ($availability.$trans_id.$state_id)}checked="true"{/if}/></td> | ||
| 32 | + {/foreach} | ||
| 33 | + </tr> | ||
| 34 | + {/foreach} | ||
| 35 | + </tbody> | ||
| 36 | + </table> | ||
| 37 | + | ||
| 38 | +{/capture} | ||
| 39 | +{assign value=$form->renderButtons() var=buttons} | ||
| 40 | + | ||
| 41 | +{$form->renderContaining($widgets, $buttons)} |
templates/ktcore/workflow/admin/edit_core.smarty
0 โ 100644
templates/ktcore/workflow/admin/edit_state.smarty
0 โ 100644
| 1 | +<h2>Manage State</h2> | ||
| 2 | + | ||
| 3 | +{$edit_form->render()} | ||
| 4 | + | ||
| 5 | +{* The real meat is down here. We define and list a set of actions and links to them. *} | ||
| 6 | + | ||
| 7 | +<h3>State Effects</h3> | ||
| 8 | + | ||
| 9 | +<p class="descriptiveText">{i18n}One of the reasons that workflow is so key to | ||
| 10 | +the way KnowledgeTree is used is that states can have a variety of effects on | ||
| 11 | +the way other systems work. For example: workflow states can override the permissions | ||
| 12 | +on a document, and reaching a state can cause notifications to be sent out.{/i18n}</p> | ||
| 13 | + | ||
| 14 | +<dl> | ||
| 15 | + <dt><strong><a href="">{i18n}Security at this state{/i18n}</a></strong></dt> | ||
| 16 | + <dd class="descriptiveText">{i18n}When a document is in a workflow state, that | ||
| 17 | + state can override some or all of the permissions that would "normally" be | ||
| 18 | + assigned to the document (e.g. via the folder it is in). It can also restrict | ||
| 19 | + which document actions are available.{/i18n}</dd> | ||
| 20 | + | ||
| 21 | + <dt><a href=""><strong>{i18n}Notifications{/i18n}</a></strong></dt> | ||
| 22 | + <dd class="descriptiveText">{i18n}In order to progress through a workflow, a document | ||
| 23 | + will usually require collaboration between a number of different users. | ||
| 24 | + One way to help this process is to inform certain groups or roles about | ||
| 25 | + the document's current state.{/i18n}</dd> | ||
| 26 | +</dl> |
templates/ktcore/workflow/admin/edit_transition.smarty
0 โ 100644
| 1 | +<h2>Manage Transition</h2> | ||
| 2 | + | ||
| 3 | +{$edit_form->render()} | ||
| 4 | + | ||
| 5 | +{* The real meat is down here. We define and list a set of actions and links to them. *} | ||
| 6 | + | ||
| 7 | +<dl> | ||
| 8 | + <dt><strong><a href="">{i18n}Transition Requirements{/i18n}</a></strong></dt> | ||
| 9 | + <dd class="descriptiveText">{i18n}You can control when and by whom transitions can | ||
| 10 | + be performed by setting up various guards. These can include permissions, roles, | ||
| 11 | + groups or a variety of other restriction conditions.{/i18n}</dd> | ||
| 12 | + | ||
| 13 | +</dl> |
templates/ktcore/workflow/admin/effects_overview.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_name=$workflow_name}Workflow Effects Overview: #name#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">As a document moves through a workflow, it can cause | ||
| 4 | +varies other actions to occur. For example, you can attach a "Move" action to a transition, | ||
| 5 | +which will cause any document moving through that workflow to be moved to a particular folder. | ||
| 6 | +Or you can specify that when a document reaches the "Pending Review" state, users | ||
| 7 | +with the role "Reviewer" on that document are informed.</p> |
templates/ktcore/workflow/admin/list.smarty
0 โ 100644
| 1 | +<h2>{i18n}Workflow Admin{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">{i18n}Workflow is a description of a document's lifecycle. It is made up of workflow states, which describe where in the lifecycle the document is, and workflow transitions, which describe the next steps within the lifecycle of the document.{/i18n}</p> | ||
| 4 | + | ||
| 5 | +<a class="ktAction ktAdd ktActionDescribed" href="{addQS context=$context}action=newWorkflow{/addQS}">{i18n}Create New Workflow{/i18n}</a><a href="{addQS context=$context}action=newWorkflow{/addQS}">{i18n}Create New Workflow{/i18n}</a> | ||
| 6 | + | ||
| 7 | +{if !empty($workflows)} | ||
| 8 | + | ||
| 9 | +<h3>{i18n}Existing workflows{/i18n}</h3> | ||
| 10 | +<p class="descriptiveText">{i18n}Select a workflow to modify. To enable a disabled workflow, edit it and set a proper starting state.{/i18n}</p> | ||
| 11 | + | ||
| 12 | +<table class="kt_collection narrow" cellspacing="0"> | ||
| 13 | + | ||
| 14 | + <thead> | ||
| 15 | + <tr> | ||
| 16 | + <th>{i18n}Name{/i18n}</th> | ||
| 17 | + <th>{i18n}Status{/i18n}</th> | ||
| 18 | + <th>{i18n}Edit{/i18n}</th> | ||
| 19 | + </tr> | ||
| 20 | + </thead> | ||
| 21 | + | ||
| 22 | + <tbody> | ||
| 23 | +{foreach from=$workflows item=oWorkflow} | ||
| 24 | + | ||
| 25 | + <tr> | ||
| 26 | + <td>{$oWorkflow->getName()}</td> | ||
| 27 | + <td class="centered">{if $oWorkflow->getIsFunctional()} <span class="ktAction ktAllowed">{i18n}Enabled{/i18n}</span> {else} <span class="ktAction ktDenied">{i18n}Disabled{/i18n}</span> {/if}</td> | ||
| 28 | + <td><a class="ktAction ktEdit" href="{addQS}action=view&fWorkflowId={$oWorkflow->getId()}{/addQS}">{i18n}Edit{/i18n}</a></td> | ||
| 29 | + </tr> | ||
| 30 | +{/foreach} | ||
| 31 | + </tbody> | ||
| 32 | +</table> | ||
| 33 | + | ||
| 34 | +{/if} |
templates/ktcore/workflow/admin/managepermissions.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_statename=$context->oState->getName()}Manage Permissions: #statename#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +{if empty($perm_grid)} | ||
| 4 | +<div class="ktInfo"><p>{i18n}No permissions are controlled by this state. | ||
| 5 | +Indicate below which permissions are controlled to allocate them.{/i18n}</p></div> | ||
| 6 | +{else} | ||
| 7 | + | ||
| 8 | +<p class="descriptiveText">{i18n}Once you've selected the permissions you want to control | ||
| 9 | +for this workflow state, you should allocate these to the appropriate groups | ||
| 10 | +and roles.{/i18n}</p> | ||
| 11 | + | ||
| 12 | +<a class="ktAction ktEdit ktActionDescribed" href="{addQS context=$context}action=allocatepermissions{/addQS}">Allocate permissions</a> | ||
| 13 | +<a href="{addQS context=$context}action=allocatepermissions{/addQS}">Allocate permissions</a> | ||
| 14 | + | ||
| 15 | +{/if} | ||
| 16 | + | ||
| 17 | +<h3>Specify permissions</h3> | ||
| 18 | + | ||
| 19 | +{capture assign=permgrid} | ||
| 20 | + | ||
| 21 | +<p class="descriptiveText">{i18n}Select the permissions you want controlled by this state.{/i18n}</p> | ||
| 22 | + | ||
| 23 | +<table class="kt_collection" cellspacing="0"> | ||
| 24 | + <thead> | ||
| 25 | + <tr> | ||
| 26 | + {foreach from=$perms item=oPerm} | ||
| 27 | + <th class="centered">{i18n}{$oPerm->getHumanName()}{/i18n}</th> | ||
| 28 | + {/foreach} | ||
| 29 | + </tr> | ||
| 30 | + </thead> | ||
| 31 | + <tbody> | ||
| 32 | + <tr> | ||
| 33 | + {foreach from=$perms item=oPerm} | ||
| 34 | + {assign value=$oPerm->getId() var=perm_id} | ||
| 35 | + <td class="centered"><input type="checkbox" name="fControlled[{$perm_id}]" {if ($perm_grid[$perm_id])}checked="true"{/if}/></td> | ||
| 36 | + {/foreach} | ||
| 37 | + </tr> | ||
| 38 | + </tbody> | ||
| 39 | +</table> | ||
| 40 | +{/capture} | ||
| 41 | +{$form->renderContaining($permgrid, $form->renderButtons())} |
templates/ktcore/workflow/admin/new_wizard_step1.smarty
0 โ 100644
templates/ktcore/workflow/admin/new_wizard_step2.smarty
0 โ 100644
| 1 | +<h2>{i18n}Step 2: Connect transitions to states{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">{i18n}In order to move between states, the transitions | ||
| 4 | +you specified earlier must be configured to move from a set of states to a "destination" | ||
| 5 | +states. Use the table below to configure this behaviour.{/i18n}</p> | ||
| 6 | + | ||
| 7 | +<form method="POST" action="{$smarty.server.PHP_SELF}"> | ||
| 8 | + {foreach from=$args key=k item=v} | ||
| 9 | + <input type="hidden" name="{$k}" value="{$v}" /> | ||
| 10 | + {/foreach} | ||
| 11 | + | ||
| 12 | + <table class="kt_collection" cellspacing="0"> | ||
| 13 | + <thead> | ||
| 14 | + <tr> | ||
| 15 | + <th>{i18n}Transition{/i18n}</th> | ||
| 16 | + <th>{i18n}Leads to state{/i18n}</th> | ||
| 17 | + {foreach from=$states item=state} | ||
| 18 | + <th>{$state}</th> | ||
| 19 | + {/foreach} | ||
| 20 | + </tr> | ||
| 21 | + </thead> | ||
| 22 | + <tbody> | ||
| 23 | + {foreach from=$transitions item=transition} | ||
| 24 | + <tr class="row {cycle values=odd,even}"> | ||
| 25 | + <td>{$transition}</td> | ||
| 26 | + <td><select name="fTo[{$transition}]"> | ||
| 27 | + {foreach from=$states item=state} | ||
| 28 | + <option value="{$state}">{$state}</option> | ||
| 29 | + {/foreach} | ||
| 30 | + </select></td> | ||
| 31 | + {foreach from=$states item=state} | ||
| 32 | + <td><input type="checkbox" name="fFrom[{$transition}][{$state}]"/></td> | ||
| 33 | + {/foreach} | ||
| 34 | + </tr> | ||
| 35 | + {/foreach} | ||
| 36 | + </tbody> | ||
| 37 | + </table> | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + <div class="form_actions"> | ||
| 41 | + <input type="submit" value="{i18n}Create Workflow{/i18n}" /> | ||
| 42 | + <a href="{$smarty.server.PHP_SELF}">Cancel</a> | ||
| 43 | + </div> | ||
| 44 | +</form> |
templates/ktcore/workflow/admin/permissions_overview.smarty
0 โ 100644
| 1 | +<h2>{i18n}Permissions Overview{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">A particular workflow state can override some, all, | ||
| 4 | +or none of the permissions that would normally apply to a document. In this | ||
| 5 | +way you can (for example) let the folder's permissions decide who can see | ||
| 6 | +the document (with <strong>Read</strong> permissions), while having the workflow | ||
| 7 | +restrict access to the "edit" permission.</p> | ||
| 8 | + | ||
| 9 | +<p class="descriptiveText important">States which control permissions have a tick in | ||
| 10 | +the "Control" column. Permissions which are not controlled by a state (e.g. which | ||
| 11 | +are controlled by the folder a document is in) are marked with a dash (—). | ||
| 12 | +Controlled permissions are marked with a tick. <strong>Click on the state name to | ||
| 13 | +specify how it controls permissions.</strong></p> | ||
| 14 | + | ||
| 15 | +<table class="kt_collection" cellspacing="0"> | ||
| 16 | + <thead> | ||
| 17 | + <tr> | ||
| 18 | + <th>States</th> | ||
| 19 | + <th class="centered">Control</th> | ||
| 20 | + {foreach from=$perms item=oPerm} | ||
| 21 | + <th class="centered">{i18n}{$oPerm->getHumanName()}{/i18n}</th> | ||
| 22 | + {/foreach} | ||
| 23 | + </tr> | ||
| 24 | + </thead> | ||
| 25 | + <tbody> | ||
| 26 | + {foreach from=$states item=oState} | ||
| 27 | + {assign value=$oState->getId() var=state_id} | ||
| 28 | + <tr> | ||
| 29 | + <td><a href="{addQS context=$context}action=managepermissions&fStateId={$state_id}{/addQS}">{$oState->getName()}</a></td> | ||
| 30 | + <td class="centered">{if ($controllers.$state_id)} <span class="ktAction ktAllowed">yes</span> {else} <span class="ktAction ktDenied">no</span> {/if}</td> | ||
| 31 | + {foreach from=$perms item=oPerm} | ||
| 32 | + {assign value=$oPerm->getId() var=perm_id} | ||
| 33 | + <td class="centered">{if ($perm_grid.$state_id.$perm_id)} <span class="ktAction ktAllowed">managed</span> {else} — {/if}</td> | ||
| 34 | + {/foreach} | ||
| 35 | + </tr> | ||
| 36 | + {/foreach} | ||
| 37 | + </tbody> | ||
| 38 | +</table> |
templates/ktcore/workflow/admin/security_overview.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_name=$workflow_name}Security Overview: #name#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">KnowledgeTree has a powerful security model, in which users | ||
| 4 | +can only see documents they have permissions to see. Workflow is the finest-grained | ||
| 5 | +way to allocate permissions to a document, since it can override the permissions | ||
| 6 | +assigned at a folder level.</p> | ||
| 7 | + | ||
| 8 | +<p class="descriptiveText">There are 3 different ways in which workflows interact | ||
| 9 | +with the system's security:</p> | ||
| 10 | + | ||
| 11 | +<ol> | ||
| 12 | + <li><a href="{addQS context=$context}action=permissionsoverview{/addQS}">Document Permissions</a> (by state)</li> | ||
| 13 | + <li><a href="{addQS context=$context}action=actionsoverview{/addQS}">Action Restrictions</a> (by state)</li> | ||
| 14 | + <li><a href="{addQS context=$context}action=transitionrestrictionoverview{/addQS}">Transition Restrictions</a></li> | ||
| 15 | + | ||
| 16 | +</ol> | ||
| 17 | + | ||
| 18 | +<p class="descriptiveText">Each of these can be managed from this area.</p> |
templates/ktcore/workflow/admin/view.smarty
0 โ 100644
| 1 | +<h2>{i18n arg_name=$workflow_name}Workflow: #name#{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">One of the most powerful features of KnowledgeTree is the workflow | ||
| 4 | +system. This allows you to direct the lifecycle of a document from start to finish. The | ||
| 5 | +"Workflow Administration" menu on the left allows you to access and update information | ||
| 6 | +about states, transitions, security and notifications as they apply to this workflow.</p> | ||
| 7 | + | ||
| 8 | +<p><a class="ktAction ktEdit ktActionDescribed" href="{addQS context=$context}action=editcore{/addQS}">Edit Workflow Details</a> | ||
| 9 | +<a href="{addQS context=$context}action=editcore{/addQS}">Edit Workflow Details</a> <span class="descriptiveText">(e.g. workflow name, starting state)</span></p> |