diff --git a/lib/workflow/workflowadminutil.inc.php b/lib/workflow/workflowadminutil.inc.php index 351eb52..092e09b 100644 --- a/lib/workflow/workflowadminutil.inc.php +++ b/lib/workflow/workflowadminutil.inc.php @@ -114,5 +114,6 @@ class KTWorkflowAdminUtil { } return $aRet; } - + + } diff --git a/lib/workflow/workflowutil.inc.php b/lib/workflow/workflowutil.inc.php index 1e3c213..42cfc3c 100644 --- a/lib/workflow/workflowutil.inc.php +++ b/lib/workflow/workflowutil.inc.php @@ -253,6 +253,8 @@ class KTWorkflowUtil { } // }}} + // FIXME DEPRECATED + // {{{ setEnabledActionsForState /** * Sets the actions that are enabled by this workflow state. @@ -295,6 +297,34 @@ class KTWorkflowUtil { } // }}} + function setDisabledActionsForState($oState, $aActions) { + $iStateId = KTUtil::getId($oState); + $sTable = KTUtil::getTableName('workflow_state_disabled_actions'); + + $aQuery = array( + "DELETE FROM $sTable WHERE state_id = ?", + array($iStateId), + ); + $res = DBUtil::runQuery($aQuery); + if (PEAR::isError($res)) { + return $res; + } + + if(!is_array($aActions)) return; + + $aOptions = array('noid' => true); + foreach ($aActions as $sAction) { + $res = DBUtil::autoInsert($sTable, array( + 'state_id' => $iStateId, + 'action_name' => $sAction, + ), $aOptions); + if (PEAR::isError($res)) { + return $res; + } + } + return; + } + // {{{ getEnabledActionsForState /** * Gets the actions that are enabled by this workflow state. @@ -320,6 +350,17 @@ class KTWorkflowUtil { } // }}} + function getDisabledActionsForState($oState) { + $iStateId = KTUtil::getId($oState); + $sTable = KTUtil::getTableName('workflow_state_disabled_actions'); + + $aQuery = array( + "SELECT action_name FROM $sTable WHERE state_id = ?", + array($iStateId), + ); + return DBUtil::getResultArrayKey($aQuery, 'action_name'); + } + // {{{ actionEnabledForDocument /** * Checks if a particular action is enabled to occur on a document @@ -343,7 +384,7 @@ class KTWorkflowUtil { return true; } $oState =& KTWorkflowState::getByDocument($oDocument); - if (!in_array($sName, KTWorkflowUtil::getEnabledActionsForState($oState))) { + if (in_array($sName, KTWorkflowUtil::getDisabledActionsForState($oState))) { return false; } return true; diff --git a/plugins/ktcore/admin/workflowsv2.php b/plugins/ktcore/admin/workflowsv2.php index a6516d3..6870839 100644 --- a/plugins/ktcore/admin/workflowsv2.php +++ b/plugins/ktcore/admin/workflowsv2.php @@ -1073,40 +1073,300 @@ class KTWorkflowAdminV2 extends KTAdminDispatcher { } $states = KTWorkflowState::getByWorkflow($this->oWorkflow); + $action_grid = array(); + foreach ($states as $oState) { + $state_actions = array(); + $disabled = KTWorkflowUtil::getDisabledActionsForState($oState); + + foreach ($disabled as $name) { + $state_actions[$name] = $name; + } + + $action_grid[$oState->getId()] = $state_actions; + } $oTemplate->setData(array( 'context' => $this, 'states' => $states, 'actions' => $actions, + 'grid' => $action_grid, )); return $oTemplate->render(); } function do_editactions() { - $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/actions_overview'); + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/actions_edit'); $this->oPage->setBreadcrumbDetails(_kt("Actions")); + $actions = KTUtil::keyArray(KTDocumentActionUtil::getAllDocumentActions(), 'getName'); + $blacklist = array('ktcore.actions.document.displaydetails'); + + foreach ($blacklist as $name) { + unset($actions[$name]); + } + + $states = KTWorkflowState::getByWorkflow($this->oWorkflow); + $action_grid = array(); + foreach ($states as $oState) { + $state_actions = array(); + $disabled = KTWorkflowUtil::getDisabledActionsForState($oState); + + foreach ($disabled as $name) { + $state_actions[$name] = $name; + } + + $action_grid[$oState->getId()] = $state_actions; + } $oTemplate->setData(array( 'context' => $this, - 'workflow_name' => $this->oWorkflow->getName(), + 'states' => $states, + 'actions' => $actions, + 'grid' => $action_grid, + 'args' => $this->meldPersistQuery("","saveactions", true), )); return $oTemplate->render(); } function do_saveactions() { - $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/actions_overview'); - $this->oPage->setBreadcrumbDetails(_kt("Actions")); + $disabled_actions = (array) $_REQUEST['fActions']; - $res = KTWorkflowUtil::setEnabledActionsForState($oState, $_REQUEST['fActions']); + + $states = KTWorkflowState::getByWorkflow($this->oWorkflow); + $actions = KTUtil::keyArray(KTDocumentActionUtil::getAllDocumentActions(), 'getName'); + + $this->startTransaction(); + + foreach ($states as $oState) { + $disable = array(); + $state_disabled = (array) $disabled_actions[$oState->getId()]; + if (!empty($state_disabled)) { + foreach ($actions as $name => $oAction) { + if ($state_disabled[$name]) { + $disable[] = $name; + } + } + } + + $res = KTWorkflowUtil::setDisabledActionsForState($oState, $disable); + } + + $this->successRedirectTo('actionsoverview', _kt('Disabled actions updated.')); + } + + function do_transitionsecurityoverview() { + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/transition_guards_overview'); + $this->oPage->setBreadcrumbDetails(_kt("Transition Guards")); + + $transitions = KTWorkflowTransition::getByWorkflow($this->oWorkflow); $oTemplate->setData(array( 'context' => $this, - 'workflow_name' => $this->oWorkflow->getName(), + 'transitions' => $transitions, )); return $oTemplate->render(); } + // helper + function describeTransitionGuards($oTransition) { + $restrictions = KTWorkflowUtil::getGuardTriggersForTransition($oTransition); + + if (empty($restrictions)) { + return _kt("No restrictions in place for this transition."); + } + + $restriction_text = array(); + foreach ($restrictions as $oGuard) { + $restriction_text[] = $oGuard->getConfigDescription(); + } + + return implode('. ', $restriction_text); + } + + function form_addtransitionguard() { + $oForm = new KTForm; + $oForm->setOptions(array( + 'identifier' => 'ktcore.admin.workflow.addguard', + 'label' => _kt("Add New Transition Restriction"), + 'action' => 'addguard', + 'cancel_action' => 'manageguards', + 'fail_action' => 'manageguards', + 'submit_label' => _kt("Add Restriction"), + 'context' => $this, + )); + + $oTriggerSingleton =& KTWorkflowTriggerRegistry::getSingleton(); + $aTriggerList = $oTriggerSingleton->listWorkflowTriggers(); + $vocab = array(); + foreach ($aTriggerList as $ns => $aTriggerInfo) { + $aInfo = $aTriggerInfo; // i am lazy. + //var_dump($aInfo); + $actions = array(); + if ($aInfo['guard']) { + $actions[] = _kt('Guard'); + } else { + continue; + } + if ($aInfo['action']) { + $actions[] = _kt('Action'); + } + $sActStr = implode(', ', $actions); + $vocab[$ns] = sprintf(_kt("%s (%s)"), $aInfo['name'], $sActStr); + } + + $oForm->setWidgets(array( + array('ktcore.widgets.selection', array( + 'label' => _kt("Restriction Type"), + 'name' => 'guard_name', + 'vocab' => $vocab, + 'simple_select' => false, + 'required' => true, + )), + )); + + $oForm->setValidators(array( + array('ktcore.validators.string', array( + 'test' => 'guard_name', + 'output' => 'guard_name', + )), + )); + return $oForm; + } + + function do_manageguards() { + $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/restrictions_edit'); + $this->oPage->setBreadcrumbDetails(_kt("Actions")); + + $restrictions = KTWorkflowUtil::getGuardTriggersForTransition($this->oTransition); + $add_form = $this->form_addtransitionguard(); + + $oTemplate->setData(array( + 'context' => $this, + 'add_form' => $add_form, + 'aGuardTriggers' => $restrictions, + )); + return $oTemplate->render(); + } + + function do_addguard() { + $oForm = $this->form_addtransitionguard(); + $res = $oForm->validate(); + $data = $res['results']; + $errors = $res['errors']; + + if (!empty($errors)) { + return $oForm->handleError(); + } + + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton(); + + $this->startTransaction(); + + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger(KTUtil::arrayGet($data, 'guard_name')); + if (PEAR::isError($oTrigger)) { + return $oForm->handleError(_kt('Unable to add trigger.')); + } + + $oTriggerConfig = KTWorkflowTriggerInstance::createFromArray(array( + 'transitionid' => KTUtil::getId($this->oTransition), + 'namespace' => KTUtil::arrayGet($data, 'guard_name'), + 'config' => array(), + )); + + if (PEAR::isError($oTriggerConfig)) { + return $oForm->handleError(_kt('Unable to add trigger.') . $oTriggerConfig->getMessage()); + } + + // now, if the trigger is editable... + $oTrigger->loadConfig($oTriggerConfig); + if ($oTrigger->bIsConfigurable) { + $this->successRedirectTo('editguardtrigger', _kt("New restriction added. This restriction requires configuration: please specify this below."), array('fTriggerInstanceId' => $oTriggerConfig->getId())); + } else { + $this->successRedirectTo('manageguards', _kt("New restriction added.")); + } + exit(0); + } + + + function do_editguardtrigger() { + $this->oPage->setBreadcrumbDetails(_kt('editing restriction')); + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']); + if (PEAR::isError($oTriggerInstance)) { + return $this->errorRedirectTo('manageguards', _kt('Unable to load trigger.')); + } + // grab the transition ns from the request. + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton(); + + $this->startTransaction(); + + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger($oTriggerInstance->getNamespace()); + if (PEAR::isError($oTrigger)) { + $this->errorRedirectTo('editTransition', _kt('Unable to add trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + $oTrigger->loadConfig($oTriggerInstance); + + return $oTrigger->displayConfiguration($this->meldPersistQuery(array('fTriggerInstanceId' => $oTriggerInstance->getId()), 'saveguardtrigger', true)); + } + + // }}} + + function do_saveguardtrigger() { + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']); + if (PEAR::isError($oTriggerInstance)) { + $this->errorRedirectTo('manageguards', _kt('Unable to load trigger.')); + exit(0); + } + + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton(); + + $this->startTransaction(); + + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger($oTriggerInstance->getNamespace()); + if (PEAR::isError($oTrigger)) { + $this->errorRedirectTo('manageguards', _kt('Unable to load trigger.')); + exit(0); + } + $oTrigger->loadConfig($oTriggerInstance); + + $res = $oTrigger->saveConfiguration(); + if (PEAR::isError($res)) { + $this->errorRedirectTo('manageguards', _kt('Unable to save trigger: ') . $res->getMessage()); + exit(0); + } + + $this->successRedirectTo('manageguards', _kt('Trigger saved.')); + exit(0); + } + + function do_deleteguardtrigger() { + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']); + if (PEAR::isError($oTriggerInstance)) { + return $this->errorRedirectTo('manageguards', _kt('Unable to load trigger.')); + } + + // grab the transition ns from the request. + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton(); + $this->startTransaction(); + + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger($oTriggerInstance->getNamespace()); + if (PEAR::isError($oTrigger)) { + $this->errorRedirectTo('manageguards', _kt('Unable to load trigger.')); + exit(0); + } + $oTrigger->loadConfig($oTriggerInstance); + + $res = $oTriggerInstance->delete(); + if (PEAR::isError($res)) { + $this->errorRedirectTo('editTransition', _kt('Unable to delete trigger: ') . $res->getMessage(), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + $this->successRedirectTo('manageguards', _kt('Trigger deleted.')); + exit(0); + } + + // ----------------- Effects --------------------- function do_effects() { $oTemplate = $this->oValidator->validateTemplate('ktcore/workflow/admin/effects_overview'); diff --git a/sql/mysql/upgrade/3.1.6.2/workflow_state_disabled_actions.sql b/sql/mysql/upgrade/3.1.6.2/workflow_state_disabled_actions.sql new file mode 100644 index 0000000..b5c57d7 --- /dev/null +++ b/sql/mysql/upgrade/3.1.6.2/workflow_state_disabled_actions.sql @@ -0,0 +1,12 @@ +CREATE TABLE `workflow_state_disabled_actions` ( + `state_id` int(11) NOT NULL default '0', + `action_name` char(255) NOT NULL default '0', + KEY `state_id` (`state_id`), + KEY `action_name` (`action_name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + + +CREATE TABLE `zseq_workflow_state_disabled_actions` ( + `id` int(10) unsigned NOT NULL auto_increment, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; diff --git a/templates/ktcore/workflow/admin/actions_edit.smarty b/templates/ktcore/workflow/admin/actions_edit.smarty new file mode 100644 index 0000000..2c16ab8 --- /dev/null +++ b/templates/ktcore/workflow/admin/actions_edit.smarty @@ -0,0 +1,42 @@ +

Assign blocked actions

+ +

Actions which are checked on this page will +not be available to users.

+ +
+
Assign Blocked Actions + +{foreach from=$args item=v key=k} + +{/foreach} + + + + + {foreach from=$actions item=oAction} + + {/foreach} + + + + {foreach from=$states item=oState} + {assign value=$oState->getId() var=state_id} + + + {foreach from=$actions item=oAction} + {assign value=$oAction->getName() var=action_id} + + {/foreach} + + {/foreach} + +
State{$oAction->getDisplayName()}
{$oState->getName()} + +
+ +
+ +
+ +
+
diff --git a/templates/ktcore/workflow/admin/actions_overview.smarty b/templates/ktcore/workflow/admin/actions_overview.smarty index f5f4e0b..f3363d1 100644 --- a/templates/ktcore/workflow/admin/actions_overview.smarty +++ b/templates/ktcore/workflow/admin/actions_overview.smarty @@ -4,6 +4,9 @@ possible to block certain actions at any given point. Actions which are not blocked are still controlled by the usual permissions.

+

Edit Actions +Edit Actions

+ @@ -19,7 +22,11 @@ which are not blocked are still controlled by the usual permissions.

{foreach from=$actions item=oAction} {assign value=$oAction->getName() var=action_id} - + {if $grid.$state_id.$action_id} + + {else} + + {/if} {/foreach} {/foreach} diff --git a/templates/ktcore/workflow/admin/restrictions_edit.smarty b/templates/ktcore/workflow/admin/restrictions_edit.smarty new file mode 100644 index 0000000..19a9a8e --- /dev/null +++ b/templates/ktcore/workflow/admin/restrictions_edit.smarty @@ -0,0 +1,34 @@ +

{i18n}Transition Restrictions{/i18n}

+ +

{i18n}All of these must allow the user to perform the transition.{/i18n}

+ +{$add_form->render()} + +
+ +{if empty($aGuardTriggers)} +

{i18n}Anybody (with the ability to see the document) can perform this transition.{/i18n}

+{else} + +
State {$oState->getName()}Denied
+ + + + + + + + + + {foreach from=$aGuardTriggers item=oTrigger} + + + + + + + {/foreach} + +
{i18n}Restriction{/i18n}{i18n}Edit{/i18n}{i18n}Delete{/i18n}{i18n}Configuration{/i18n}
{$oTrigger->getName()}{if $oTrigger->bIsConfigurable}edit{else}—{/if}delete{$oTrigger->getConfigDescription()}
+ + {/if} diff --git a/templates/ktcore/workflow/admin/security_overview.smarty b/templates/ktcore/workflow/admin/security_overview.smarty index 38a134f..3997748 100644 --- a/templates/ktcore/workflow/admin/security_overview.smarty +++ b/templates/ktcore/workflow/admin/security_overview.smarty @@ -11,7 +11,7 @@ with the system's security:

  1. Document Permissions (by state)
  2. Action Restrictions (by state)
  3. -
  4. Transition Restrictions
  5. +
  6. Transition Restrictions
diff --git a/templates/ktcore/workflow/admin/transition_guards_overview.smarty b/templates/ktcore/workflow/admin/transition_guards_overview.smarty new file mode 100644 index 0000000..19c176c --- /dev/null +++ b/templates/ktcore/workflow/admin/transition_guards_overview.smarty @@ -0,0 +1,29 @@ +

Transition Restrictions Overview

+ +

In order to ensure that the workflow is followed +correctly, it is often necessary to restrict the situations in which a +transition can be followed. This can include things like the permissions the +user has on the document, the user's groups or roles, or whether the document +is checked-out or not.

+ +

Please note that the plugins that are installed +will affect the available options

+ + + + + + + + + + {foreach from=$transitions item=oTransition} + {assign value=$oTransition->getId() var=transition_id} + + + + + + {/foreach} + +
TransitionEditExisting Restrictions
{$oTransition->getName()}{$context->describeTransitionGuards($oTransition)}