diff --git a/lib/plugins/plugin.inc.php b/lib/plugins/plugin.inc.php index 3167ec0..0f95342 100644 --- a/lib/plugins/plugin.inc.php +++ b/lib/plugins/plugin.inc.php @@ -51,6 +51,7 @@ class KTPlugin { var $_ai18nLang = array(); var $_aLanguage = array(); var $_aHelpLanguage = array(); + var $_aWFTriggers = array(); function KTPlugin($sFilename = null) { $this->sFilename = $sFilename; @@ -80,6 +81,11 @@ class KTPlugin { $sWebPath = sprintf("%s/%s", $this->sNamespace, $sWebPath); $this->_aPages[$sWebPath] = array($sWebPath, $sPageClassName, $sFilename, $this->sNamespace); } + + function registerWorkflowTrigger($sNamespace, $sTriggerClassName, $sFilename = null) { + $sFilename = $this->_fixFilename($sFilename); + $this->_aWFTriggers[$sNamespace] = array($sNamespace, $sTriggerClassName, $sFilename); + } function getPagePath($sPath) { $sExt = ".php"; @@ -191,6 +197,7 @@ class KTPlugin { require_once(KT_LIB_DIR . "/dashboard/dashletregistry.inc.php"); require_once(KT_LIB_DIR . "/i18n/i18nregistry.inc.php"); require_once(KT_LIB_DIR . "/help/help.inc.php"); + require_once(KT_LIB_DIR . "/workflow/workflowutil.inc.php"); $oPRegistry =& KTPortletRegistry::getSingleton(); $oTRegistry =& KTTriggerRegistry::getSingleton(); @@ -201,6 +208,7 @@ class KTPlugin { $oDashletRegistry =& KTDashletRegistry::getSingleton(); $oi18nRegistry =& KTi18nRegistry::getSingleton(); $oKTHelpRegistry =& KTHelpRegistry::getSingleton(); + $oWFTriggerRegistry =& KTWorkflowTriggerRegistry::getSingleton(); foreach ($this->_aPortlets as $k => $v) { call_user_func_array(array(&$oPRegistry, 'registerPortlet'), $v); @@ -249,6 +257,10 @@ class KTPlugin { foreach ($this->_aHelpLanguage as $k => $v) { call_user_func_array(array(&$oKTHelpRegistry, 'registerHelp'), $v); } + + foreach ($this->_aWFTriggers as $k => $v) { + call_user_func_array(array(&$oWFTriggerRegistry, 'registerWorkflowTrigger'), $v); + } } function setup() { diff --git a/lib/workflow/workflowtrigger.inc.php b/lib/workflow/workflowtrigger.inc.php index 8e3020c..a669719 100644 --- a/lib/workflow/workflowtrigger.inc.php +++ b/lib/workflow/workflowtrigger.inc.php @@ -50,6 +50,7 @@ class KTWorkflowTrigger { function loadConfig($oTriggerInstance) { $this->oTriggerInstance = $oTriggerInstance; + $this->aConfig = $oTriggerInstance->getConfig(); } function isLoaded() { return (!is_null($this->oTriggerInstance)); } @@ -64,6 +65,10 @@ class KTWorkflowTrigger { ); } + function getName() { return $this->sFriendlyName; } + function getNamespace() { return $this->sNamespace; } + function getConfigId() { return $this->oTriggerInstance->getId(); } + // return true for transition allowed on doc, false for transition not allowed on doc. function allowTransition($oDocument, $oUser) { return true; // abstract base class @@ -85,6 +90,22 @@ class KTWorkflowTrigger { return true; } + // display the configuration page for this plugin + // will be called -after- loadConfig, so it can prepopulate the options. + function displayConfiguration($args) { + return _kt('No configuration has been implemented for this plugin.'); + } + + // dispatched - again, after loadConfig, so it can set the config. + // throw an error to redispatch displayConfiguration, or return true to cause a db commit (probably). + function saveConfiguration() { + return true; + } + + // give a brief, friendly description of what we are and what we do. + function getConfigDescription() { + return ''; + } } ?> diff --git a/lib/workflow/workflowtriggerinstance.inc.php b/lib/workflow/workflowtriggerinstance.inc.php index 41752dd..e1fdf79 100644 --- a/lib/workflow/workflowtriggerinstance.inc.php +++ b/lib/workflow/workflowtriggerinstance.inc.php @@ -59,6 +59,7 @@ class KTWorkflowTriggerInstance extends KTEntity { function &get($iId) { return KTEntityUtil::get('KTWorkflowTriggerInstance', $iId); } function &createFromArray($aOptions) { $aOptions['configarraytext'] = serialize($aOptions['config']); + unset($aOptions['config']); return KTEntityUtil::createFromArray('KTWorkflowTriggerInstance', $aOptions); } function &getList($sWhereClause = null) { return KTEntityUtil::getList2('KTWorkflowTriggerInstance', $sWhereClause); } diff --git a/lib/workflow/workflowutil.inc.php b/lib/workflow/workflowutil.inc.php index 38ef8b1..ad2872d 100644 --- a/lib/workflow/workflowutil.inc.php +++ b/lib/workflow/workflowutil.inc.php @@ -745,6 +745,10 @@ class KTWorkflowUtil { class KTWorkflowTriggerRegistry { var $triggers; + function KTWorkflowTriggerRegistry() { + $this->triggers = array(); + } + // {{{ getSingleton function &getSingleton () { if (!KTUtil::arrayGet($GLOBALS['_KT_PLUGIN'], 'oKTWorkflowTriggerRegistry')) { @@ -764,11 +768,9 @@ class KTWorkflowTriggerRegistry { return PEAR::raiseError(sprintf(_kt("Unable to find workflow trigger: %s"), $sNamespace)); } - if (file_exists($aInfo['path'])) { - require_once($aInfo['path']); - } - - return new $sClassname; + require_once($aInfo['path']); + + return new $aInfo['class']; } // get a keyed list of workflow triggers diff --git a/plugins/ktcore/KTCorePlugin.php b/plugins/ktcore/KTCorePlugin.php index a27d8e9..ec39eb0 100644 --- a/plugins/ktcore/KTCorePlugin.php +++ b/plugins/ktcore/KTCorePlugin.php @@ -29,6 +29,7 @@ require_once(KT_LIB_DIR . '/plugins/pluginregistry.inc.php'); require_once(KT_LIB_DIR . '/plugins/plugin.inc.php'); + class KTCorePlugin extends KTPlugin { var $bAlwaysInclude = true; var $sNamespace = "ktcore.plugin"; @@ -179,6 +180,9 @@ class KTCorePlugin extends KTPlugin { 'admin/manageCleanup.php', null); // plugins + + // workflow triggers + $this->registerWorkflowTrigger('ktcore.workflowtriggers.permissionguard', 'PermissionGuardTrigger', 'KTWorkflowTriggers.inc.php'); } } diff --git a/plugins/ktcore/KTDocumentActions.php b/plugins/ktcore/KTDocumentActions.php index eba38b0..e58dd45 100644 --- a/plugins/ktcore/KTDocumentActions.php +++ b/plugins/ktcore/KTDocumentActions.php @@ -996,14 +996,14 @@ class KTDocumentWorkflowAction extends KTDocumentAction { $oUser =& User::get($_SESSION['userID']); $res = KTWorkflowUtil::performTransitionOnDocument($oTransition, $oDocument, $oUser, $sComments); - if(!Permission::userHasDocumentReadPermission($oDocument)) { - $this->commitTransaction(); + if(!Permission::userHasDocumentReadPermission($oDocument)) { + $this->commitTransaction(); $_SESSION['KTInfoMessage'][] = _kt('Transition performed') . '. ' . _kt('You no longer have permission to view this document'); - controllerRedirect('browse', sprintf('fFolderId=%d', $oDocument->getFolderId())); - } else { - $this->successRedirectToMain(_kt('Transition performed'), - array('fDocumentId' => $oDocument->getId())); - } + controllerRedirect('browse', sprintf('fFolderId=%d', $oDocument->getFolderId())); + } else { + $this->successRedirectToMain(_kt('Transition performed'), + array('fDocumentId' => $oDocument->getId())); + } } } // }}} diff --git a/plugins/ktcore/admin/workflows.php b/plugins/ktcore/admin/workflows.php index 01385b5..46eb8ae 100755 --- a/plugins/ktcore/admin/workflows.php +++ b/plugins/ktcore/admin/workflows.php @@ -1224,41 +1224,35 @@ class KTWorkflowDispatcher extends KTAdminDispatcher { } $aOptions['vocab'] = $vocab; $edit_fields[] = new KTLookupWidget(_kt('Destination State'), _kt('Once this transition is complete, which state should the document be in?'), 'fTargetStateId', $oTransition->getTargetStateId(), $this->oPage, true, null, null, $aOptions); - $aOptions = array(); - $vocab = array(); - $vocab[0] = _kt('None'); - foreach($aPermissions as $permission) { - $vocab[$permission->getId()] = $permission->getHumanName(); - } - $aOptions['vocab'] = $vocab; - $edit_fields[] = new KTLookupWidget(_kt('Guard Permission.'), _kt('Which permission must the user have in order to follow this transition?'), 'fPermissionId', $oTransition->getGuardPermissionId(), $this->oPage, true, null, null, $aOptions); - $aOptions = array(); + + // triggers + $add_trigger_fields = array(); $vocab = array(); - $vocab[0] = _kt('None'); - foreach($aGroups as $group) { - $vocab[$group->getId()] = $group->getName(); - } + $vocab[0] = _kt('-- Please select a trigger --'); + $oTriggerSingleton =& KTWorkflowTriggerRegistry::getSingleton(); + $aTriggerList = $oTriggerSingleton->listWorkflowTriggers(); // only want registered triggers - no other kind exists. + foreach ($aTriggerList as $ns => $aTriggerInfo) { + $aInfo = $aTriggerInfo; // i am lazy. + //var_dump($aInfo); + $actions = array(); + if ($aInfo['guard']) { + $actions[] = _kt('Guard'); + } + if ($aInfo['action']) { + $actions[] = _kt('Action'); + } + $sActStr = implode(', ', $actions); + $vocab[$ns] = sprintf(_kt("%s (%s)"), $aInfo['name'], $sActStr); + } + $aOptions['vocab'] = $vocab; - $edit_fields[] = new KTLookupWidget(_kt('Guard Group.'), _kt('Which group must the user belong to in order to follow this transition?'), 'fGroupId', $oTransition->getGuardGroupId(), $this->oPage, false, null, null, $aOptions); + $add_trigger_fields[] = new KTLookupWidget(_kt('Trigger'), _kt('Select the trigger to add to this transition. Each trigger indicates whether it controls who can see this transition, what occurs when the transition is performed, or both.'), 'fTriggerId', '0', $this->oPage, true, null, null, $aOptions); $aOptions = array(); - $vocab = array(); - $vocab[0] = _kt('None'); - foreach($aRoles as $role) { - $vocab[$role->getId()] = $role->getName(); - } - $aOptions['vocab'] = $vocab; - $edit_fields[] = new KTLookupWidget(_kt('Guard Role.'), _kt('Which role must the user have in order to follow this transition?'), 'fRoleId', $oTransition->getGuardRoleId(), $this->oPage, false, null, null, $aOptions); - if (!empty($aConditions)) { - $aOptions = array(); - $vocab = array(); - $vocab[0] = _kt('None'); - foreach($aConditions as $condition) { - $vocab[$condition->getId()] = $condition->getName(); - } - $aOptions['vocab'] = $vocab; - $edit_fields[] = new KTLookupWidget(_kt('Guard Condition.'), _kt('Which condition (stored search) must be satisfied before the transition can take place?'), 'fConditionId', $oTransition->getGuardConditionId(), $this->oPage, false, null, null, $aOptions); - } + + // attached triggers. + $aGuardTriggers = KTWorkflowUtil::getGuardTriggersForTransition($oTransition); + $aActionTriggers = KTWorkflowUtil::getActionTriggersForTransition($oTransition); $this->aBreadcrumbs[] = array( 'url' => $_SERVER['PHP_SELF'], @@ -1273,9 +1267,11 @@ class KTWorkflowDispatcher extends KTAdminDispatcher { 'aGroups' => $aGroups, 'aRoles' => $aRoles, 'aConditions' => $aConditions, + 'aGuardTriggers' => $aGuardTriggers, + 'aActionTriggers' => $aActionTriggers, // fields - + 'add_trigger_fields' => $add_trigger_fields, 'edit_fields' => $edit_fields, )); return $oTemplate; @@ -1343,6 +1339,115 @@ class KTWorkflowDispatcher extends KTAdminDispatcher { } // }}} + function do_addTrigger() { + $aRequest = $this->oValidator->validateDict($_REQUEST, array( + 'fWorkflowId' => array('type' => 'workflow'), + 'fTransitionId' => array('type' => 'workflowtransition'), + )); + $oWorkflow =& $this->oValidator->validateWorkflow($_REQUEST['fWorkflowId']); + $oTransition =& $this->oValidator->validateWorkflowTransition($_REQUEST['fTransitionId']); + + // grab the transition ns from the request. + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton(); + + $this->startTransaction(); + + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger(KTUtil::arrayGet($_REQUEST, 'fTriggerId')); + if (PEAR::isError($oTrigger)) { + $this->errorRedirectTo('editTransition', _kt('Unable to add trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + $oTriggerConfig = KTWorkflowTriggerInstance::createFromArray(array( + 'transitionid' => KTUtil::getId($oTransition), + 'namespace' => KTUtil::arrayGet($_REQUEST, 'fTriggerId'), + 'config' => array(), + )); + + if (PEAR::isError($oTriggerConfig)) { + $this->errorRedirectTo('editTransition', _kt('Unable to add trigger.' . $oTriggerConfig->getMessage()), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + $this->successRedirectTo('editTransition', _kt('Trigger added.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + function do_editTrigger() { + $this->oPage->setBreadcrumbDetails(_kt('editing trigger')); + $aRequest = $this->oValidator->validateDict($_REQUEST, array( + 'fWorkflowId' => array('type' => 'workflow'), + 'fTransitionId' => array('type' => 'workflowtransition'), + )); + $oWorkflow =& $this->oValidator->validateWorkflow($_REQUEST['fWorkflowId']); + $oTransition =& $this->oValidator->validateWorkflowTransition($_REQUEST['fTransitionId']); + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']); + if (PEAR::isError($oTriggerInstance)) { + $this->errorRedirectTo('editTransition', _kt('Unable to load trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + // 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); + + + // simplify our 'config' stuff. + $args = array(); + $args['fWorkflowId'] = $_REQUEST['fWorkflowId']; + $args['fTriggerInstanceId'] = $_REQUEST['fTriggerInstanceId']; + $args['fTransitionId'] = $_REQUEST['fTransitionId']; + $args['action'] = 'saveTrigger'; + + return $oTrigger->displayConfiguration($args); + } + + // }}} + + function do_saveTrigger() { + $this->oPage->setBreadcrumbDetails(_kt('editing trigger')); + $aRequest = $this->oValidator->validateDict($_REQUEST, array( + 'fWorkflowId' => array('type' => 'workflow'), + 'fTransitionId' => array('type' => 'workflowtransition'), + )); + $oWorkflow =& $this->oValidator->validateWorkflow($_REQUEST['fWorkflowId']); + $oTransition =& $this->oValidator->validateWorkflowTransition($_REQUEST['fTransitionId']); + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']); + if (PEAR::isError($oTriggerInstance)) { + $this->errorRedirectTo('editTransition', _kt('Unable to load trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + // 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 load trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + $oTrigger->loadConfig($oTriggerInstance); + + $res = $oTrigger->saveConfiguration(); + if (PEAR::isError($res)) { + $this->errorRedirectTo('editTransition', _kt('Unable to save trigger: ') . $res->getMessage(), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + + $this->successRedirectTo('editTransition', _kt('Trigger saved.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId()); + exit(0); + } + // }}} } diff --git a/templates/ktcore/workflow/editTransition.smarty b/templates/ktcore/workflow/editTransition.smarty index 46dd3f8..c2e365a 100644 --- a/templates/ktcore/workflow/editTransition.smarty +++ b/templates/ktcore/workflow/editTransition.smarty @@ -25,3 +25,65 @@ requirement.{/i18n}

+ +
+ {i18n}Transition Triggers{/i18n} + +

{i18n}Transition triggers allow you to have special actions automatically + occur when a transition is performed, and to control who can perform the transition. Some triggers + perform both of these functions, especially if performing the action requires that + certain conditions are in place before the action will occur.{/i18n}

+ +
+ + + + + + {foreach item=oWidget from=$add_trigger_fields} + {$oWidget->render()} + {/foreach} + +
+ +
+
+ +

{i18n}Guards{/i18n}

+ +

{i18n}Items which control whether a given user can perform this transition + on a specific document. All of these must allow the user to perform the transition.{/i18n}

+ {if empty($aGuardTriggers)} +

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

+ {else} + + + + + + + + + + + + {foreach from=$aGuardTriggers item=oTrigger} + + + + + + + {/foreach} + +
{i18n}Trigger{/i18n}{i18n}Configuration{/i18n}{i18n}Edit{/i18n}{i18n}Delete{/i18n}
{$oTrigger->getName()}{$oTrigger->getConfigDescription()}editx
+ + {/if} +

Actions

+ +

{i18n}Actions which are performed when the document follows the transition.{/i18n}

+ {if empty($aActionTriggers)} +

{i18n}No actions are performed when this transition occurs.{/i18n}

+ {else} + {/if} +
diff --git a/templates/ktcore/workflowtriggers/permissions.smarty b/templates/ktcore/workflowtriggers/permissions.smarty new file mode 100644 index 0000000..6259952 --- /dev/null +++ b/templates/ktcore/workflowtriggers/permissions.smarty @@ -0,0 +1,26 @@ +

{i18n}Guard permissions for Transition{/i18n}

+ +
+
+ {i18n}Guard Permissions{/i18n} + +{* misc, boring args *} +{foreach from=$args item=val key=name} + +{/foreach} + +

{i18n}Specify which permissions the user will require in order to perform this transition. Note that +the user will be required to have all these permissions.{/i18n}

+ +{* FIXME keep old selection *} + +{foreach from=$perms item=oPerm} + {i18n}{$oPerm->getHumanName()}{/i18n}
+{/foreach} + +
+ + {* FIXME how do I cancel again? *} +
+
+
\ No newline at end of file