Commit 90ec040fe0e797d99e9cb99ffc39adc61c1f6d50

Authored by bshuttle
1 parent d9e50c39

merge in more workflow trigger code from local branch


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@5630 c91229c3-7414-0410-bfa2-8a42b809f60b
plugins/ktcore/KTCorePlugin.php
... ... @@ -92,6 +92,13 @@ class KTCorePlugin extends KTPlugin {
92 92 'KTAdminSectionNavigation', 'ktcore.portlets.adminnavigation',
93 93 'KTPortlets.php');
94 94  
  95 + // workflow triggers
  96 + $this->registerWorkflowTrigger('ktcore.workflowtriggers.permissionguard', 'PermissionGuardTrigger', 'KTWorkflowTriggers.inc.php');
  97 + $this->registerWorkflowTrigger('ktcore.workflowtriggers.roleguard', 'RoleGuardTrigger', 'KTWorkflowTriggers.inc.php');
  98 + $this->registerWorkflowTrigger('ktcore.workflowtriggers.groupguard', 'GroupGuardTrigger', 'KTWorkflowTriggers.inc.php');
  99 + $this->registerWorkflowTrigger('ktcore.workflowtriggers.conditionguard', 'ConditionGuardTrigger', 'KTWorkflowTriggers.inc.php');
  100 +
  101 +
95 102 $this->setupAdmin();
96 103 }
97 104  
... ... @@ -180,9 +187,6 @@ class KTCorePlugin extends KTPlugin {
180 187 'admin/manageCleanup.php', null);
181 188 // plugins
182 189  
183   -
184   - // workflow triggers
185   - $this->registerWorkflowTrigger('ktcore.workflowtriggers.permissionguard', 'PermissionGuardTrigger', 'KTWorkflowTriggers.inc.php');
186 190 }
187 191 }
188 192  
... ...
plugins/ktcore/KTWorkflowTriggers.inc.php
... ... @@ -128,4 +128,289 @@ class PermissionGuardTrigger extends KTWorkflowTrigger {
128 128 }
129 129 }
130 130  
  131 +
  132 +class RoleGuardTrigger extends KTWorkflowTrigger {
  133 + var $sNamespace = 'ktcore.workflowtriggers.roleguard';
  134 + var $sFriendlyName;
  135 + var $sDescription;
  136 + var $oTriggerInstance;
  137 + var $aConfig = array();
  138 +
  139 + // generic requirements - both can be true
  140 + var $bIsGuard = true;
  141 + var $bIsAction = false;
  142 +
  143 + function RoleGuardTrigger() {
  144 + $this->sFriendlyName = _kt("Role Restrictions");
  145 + $this->sDescription = _kt("Prevents users who do not have the specified role from using this transition.");
  146 + }
  147 +
  148 + // override the allow transition hook.
  149 + function allowTransition($oDocument, $oUser) {
  150 + if (!$this->isLoaded()) {
  151 + return true;
  152 + }
  153 +
  154 + $iRoleId = $this->aConfig['role_id'];
  155 + $oRole = Role::get($this->aConfig['role_id']);
  156 + if (PEAR::isError($oRole)) {
  157 + return true; // fail safe for cases where the role is deleted.
  158 + }
  159 +
  160 + if ($iRoleId) {
  161 + $bHaveRole = false;
  162 + // handle the magic roles
  163 + if ($iRoleId == -3) {
  164 + // everyone: just accept
  165 + $bHaveRole = true;
  166 + } else if (($iRoleId == -4) && !$oUser->isAnonymous()) {
  167 + // authenticated
  168 + $bHaveRole = true;
  169 + } else {
  170 + $oRoleAllocation = DocumentRoleAllocation::getAllocationsForDocumentAndRole($oDocument->getId(), $iRoleId);
  171 + if ($oRoleAllocation == null) { // no role allocation on the doc - check the folder.
  172 + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($oDocument->getParentID(), $iRoleId);
  173 + }
  174 + // if that's -also- null
  175 + if ($oRoleAllocation == null) { // no role allocation, no fulfillment.
  176 + $bHaveRole = false;
  177 + } else if ($oRoleAllocation->hasMember($oUser)) {
  178 + $bHaveRole = false;
  179 + }
  180 + }
  181 +
  182 + if (!$bHaveRole) {
  183 + continue;
  184 + }
  185 + }
  186 +
  187 + return true;
  188 + }
  189 +
  190 + function displayConfiguration($args) {
  191 + // permissions
  192 + $aKeyedRoles = array();
  193 + $aRoles = Role::getList();
  194 + foreach ($aRoles as $oRole) { $aKeyedRoles[$oRole->getId()] = $oRole->getName(); }
  195 +
  196 + $oTemplating =& KTTemplating::getSingleton();
  197 + $oTemplate = $oTemplating->loadTemplate("ktcore/workflowtriggers/roles");
  198 + $aTemplateData = array(
  199 + "context" => $this,
  200 + "roles" => $aKeyedRoles,
  201 + "current_role" => KTUtil::arrayGet($this->aConfig, 'role_id'),
  202 + 'args' => $args,
  203 + );
  204 + return $oTemplate->render($aTemplateData);
  205 + }
  206 +
  207 + function saveConfiguration() {
  208 + $role_id = KTUtil::arrayGet($_REQUEST, 'role_id', null);
  209 + $oRole = Role::get($role_id);
  210 + if (PEAR::isError($oRole)) {
  211 + // silenty ignore
  212 + $role_id = null;
  213 + // $_SESSION['ktErrorMessages'][] = _kt('Unable to use the role you specified.');
  214 + }
  215 +
  216 + $config = array();
  217 + $config['role_id'] = $role_id;
  218 +
  219 + $this->oTriggerInstance->setConfig($config);
  220 + $res = $this->oTriggerInstance->update();
  221 +
  222 + return $res;
  223 + }
  224 +
  225 + function getConfigDescription() {
  226 + if (!$this->isLoaded()) {
  227 + return _kt('This trigger has no configuration.');
  228 + }
  229 + // the actual permissions are stored in the array.
  230 + $perms = array();
  231 + if (empty($this->aConfig) || is_null($this->aConfig['role_id'])) {
  232 + return _kt('No role is required to perform this transition');
  233 + }
  234 + $oRole = Role::get($this->aConfig['role_id']);
  235 + if (PEAR::isError($oRole)) {
  236 + return _kt('The role required for this trigger has been deleted, so anyone can perform this action.');
  237 + } else {
  238 + return sprintf(_kt("The user will require the <strong>%s</strong> role."), htmlentities($oRole->getName(), ENT_NOQUOTES, 'UTF-8'));
  239 + }
  240 + }
  241 +}
  242 +
  243 +
  244 +class GroupGuardTrigger extends KTWorkflowTrigger {
  245 + var $sNamespace = 'ktcore.workflowtriggers.groupguard';
  246 + var $sFriendlyName;
  247 + var $sDescription;
  248 + var $oTriggerInstance;
  249 + var $aConfig = array();
  250 +
  251 + // generic requirements - both can be true
  252 + var $bIsGuard = true;
  253 + var $bIsAction = false;
  254 +
  255 + function GroupGuardTrigger() {
  256 + $this->sFriendlyName = _kt("Group Restrictions");
  257 + $this->sDescription = _kt("Prevents users who are not members of the specified group from using this transition.");
  258 + }
  259 +
  260 + // override the allow transition hook.
  261 + function allowTransition($oDocument, $oUser) {
  262 + if (!$this->isLoaded()) {
  263 + return true;
  264 + }
  265 +
  266 + $iGroupId = $this->aConfig['group_id'];
  267 + $oGroup = Group::get($this->aConfig['group_id']);
  268 + if (PEAR::isError($oGroup)) {
  269 + return true; // fail safe for cases where the role is deleted.
  270 + }
  271 + $res = KTGroupUtil::getMembershipReason($oUser, $oGroup);
  272 + if (PEAR::isError($res) || is_empty($res)) { // broken setup, or no reason
  273 + return false;
  274 + } else {
  275 + return true;
  276 + }
  277 + }
  278 +
  279 + function displayConfiguration($args) {
  280 + // permissions
  281 + $aKeyedGroups = array();
  282 + $aGroups = Group::getList();
  283 + foreach ($aGroups as $oGroup) { $aKeyedGroups[$oGroup->getId()] = $oGroup->getName(); }
  284 +
  285 + $oTemplating =& KTTemplating::getSingleton();
  286 + $oTemplate = $oTemplating->loadTemplate("ktcore/workflowtriggers/group");
  287 + $aTemplateData = array(
  288 + "context" => $this,
  289 + "groups" => $aKeyedGroups,
  290 + "current_group" => KTUtil::arrayGet($this->aConfig, 'group_id'),
  291 + 'args' => $args,
  292 + );
  293 + return $oTemplate->render($aTemplateData);
  294 + }
  295 +
  296 + function saveConfiguration() {
  297 + $group_id = KTUtil::arrayGet($_REQUEST, 'group_id', null);
  298 + $oGroup = Group::get($group_id);
  299 + if (PEAR::isError($oGroup)) {
  300 + // silenty ignore
  301 + $group_id = null;
  302 + // $_SESSION['ktErrorMessages'][] = _kt('Unable to use the group you specified.');
  303 + }
  304 +
  305 + $config = array();
  306 + $config['group_id'] = $group_id;
  307 +
  308 + $this->oTriggerInstance->setConfig($config);
  309 + $res = $this->oTriggerInstance->update();
  310 +
  311 + return $res;
  312 + }
  313 +
  314 + function getConfigDescription() {
  315 + if (!$this->isLoaded()) {
  316 + return _kt('This trigger has no configuration.');
  317 + }
  318 + // the actual permissions are stored in the array.
  319 + $perms = array();
  320 + if (empty($this->aConfig) || is_null($this->aConfig['group_id'])) {
  321 + return _kt('No group is required to perform this transition');
  322 + }
  323 + $oGroup = Group::get($this->aConfig['group_id']);
  324 + if (PEAR::isError($oGroup)) {
  325 + return _kt('The group required for this trigger has been deleted, so anyone can perform this action.');
  326 + } else {
  327 + return sprintf(_kt("The user must be a member of the group \"<strong>%s</strong>\"."), htmlentities($oGroup->getName(), ENT_NOQUOTES, 'UTF-8'));
  328 + }
  329 + }
  330 +}
  331 +
  332 +
  333 +class ConditionGuardTrigger extends KTWorkflowTrigger {
  334 + var $sNamespace = 'ktcore.workflowtriggers.conditionguard';
  335 + var $sFriendlyName;
  336 + var $sDescription;
  337 + var $oTriggerInstance;
  338 + var $aConfig = array();
  339 +
  340 + // generic requirements - both can be true
  341 + var $bIsGuard = true;
  342 + var $bIsAction = false;
  343 +
  344 + function ConditionGuardTrigger() {
  345 + $this->sFriendlyName = _kt("Conditional Restrictions");
  346 + $this->sDescription = _kt("Prevents this transition from occuring if the condition specified is not met for the document.");
  347 + }
  348 +
  349 + // override the allow transition hook.
  350 + function allowTransition($oDocument, $oUser) {
  351 + if (!$this->isLoaded()) {
  352 + return true;
  353 + }
  354 +
  355 + $iConditionId = $this->aConfig['condition_id'];
  356 + $oCondition = KTSavedSearch::get($this->aConfig['condition_id']);
  357 + if (PEAR::isError($oCondition)) {
  358 + return true; // fail safe for cases where the role is deleted.
  359 + }
  360 + return KTSearchUtil::testConditionOnDocument($iConditionId, $oDocument);
  361 + }
  362 +
  363 + function displayConfiguration($args) {
  364 + // permissions
  365 + $aKeyedConditions = array();
  366 + $aConditions = KTSavedSearch::getConditions();
  367 + foreach ($aConditions as $oCondition) { $aKeyedConditions[$oCondition->getId()] = $oCondition->getName(); }
  368 +
  369 + $oTemplating =& KTTemplating::getSingleton();
  370 + $oTemplate = $oTemplating->loadTemplate("ktcore/workflowtriggers/condition");
  371 + $aTemplateData = array(
  372 + "context" => $this,
  373 + "conditions" => $aKeyedConditions,
  374 + "current_group" => KTUtil::arrayGet($this->aConfig, 'group_id'),
  375 + 'args' => $args,
  376 + );
  377 + return $oTemplate->render($aTemplateData);
  378 + }
  379 +
  380 + function saveConfiguration() {
  381 + $condition_id = KTUtil::arrayGet($_REQUEST, 'condition_id', null);
  382 + $oCondition = KTSavedSearch::get($condition_id);
  383 + if (PEAR::isError($oCondition)) {
  384 + // silenty ignore
  385 + $condition_id = null;
  386 + // $_SESSION['ktErrorMessages'][] = _kt('Unable to use the group you specified.');
  387 + }
  388 +
  389 + $config = array();
  390 + $config['condition_id'] = $condition_id;
  391 +
  392 + $this->oTriggerInstance->setConfig($config);
  393 + $res = $this->oTriggerInstance->update();
  394 +
  395 + return $res;
  396 + }
  397 +
  398 + function getConfigDescription() {
  399 + if (!$this->isLoaded()) {
  400 + return _kt('This trigger has no configuration.');
  401 + }
  402 + // the actual permissions are stored in the array.
  403 + $perms = array();
  404 + if (empty($this->aConfig) || is_null($this->aConfig['condition_id'])) {
  405 + return _kt('No condition must be fulfilled before this transition becomes available.');
  406 + }
  407 + $oCondition = KTSavedSearch::get($this->aConfig['condition_id']);
  408 + if (PEAR::isError($oCondition)) {
  409 + return _kt('The condition required for this trigger has been deleted, so it is always available.');
  410 + } else {
  411 + return sprintf(_kt("The document must match condition \"<strong>%s</strong>\"."), htmlentities($oCondition->getName(), ENT_NOQUOTES, 'UTF-8'));
  412 + }
  413 + }
  414 +}
  415 +
131 416 ?>
132 417 \ No newline at end of file
... ...
plugins/ktcore/admin/workflows.php
... ... @@ -663,8 +663,10 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
663 663 }
664 664 $aOptions['vocab'] = $vocab;
665 665 $add_transition_fields[] = new KTLookupWidget(_kt('Destination State'), _kt('Once this transition is complete, which state should the document be in?'), 'fTargetStateId', $oWorkflow->getStartStateId(), $this->oPage, true, null, null, $aOptions);
  666 + /*
666 667 $aOptions = array();
667 668 $vocab = array();
  669 + $vocab[0] = _kt('None');
668 670 foreach($aInfo['permissions'] as $permission) {
669 671 $vocab[$permission->getId()] = $permission->getHumanName();
670 672 }
... ... @@ -687,7 +689,7 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
687 689 }
688 690 $aOptions['vocab'] = $vocab;
689 691 $add_transition_fields[] = new KTLookupWidget(_kt('Guard Role.'), _kt('Which role must the user have in order to follow this transition?'), 'fRoleId', NULL, $this->oPage, false, null, null, $aOptions);
690   -
  692 +
691 693 if (!empty($aConditions)) {
692 694 $aOptions = array();
693 695 $vocab = array();
... ... @@ -698,7 +700,7 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
698 700 $aOptions['vocab'] = $vocab;
699 701 $edit_fields[] = new KTLookupWidget(_kt('Guard Condition.'), _kt('Which condition (stored search) must be satisfied before the transition can take place?'), 'fConditionId', NULL, $this->oPage, false, null, null, $aOptions);
700 702 }
701   -
  703 + */
702 704  
703 705  
704 706 $oTemplate->setData(array(
... ... @@ -1448,6 +1450,42 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
1448 1450 exit(0);
1449 1451 }
1450 1452  
  1453 + function do_deleteTrigger() {
  1454 + $aRequest = $this->oValidator->validateDict($_REQUEST, array(
  1455 + 'fWorkflowId' => array('type' => 'workflow'),
  1456 + 'fTransitionId' => array('type' => 'workflowtransition'),
  1457 + ));
  1458 + $oWorkflow =& $this->oValidator->validateWorkflow($_REQUEST['fWorkflowId']);
  1459 + $oTransition =& $this->oValidator->validateWorkflowTransition($_REQUEST['fTransitionId']);
  1460 + $oTriggerInstance =& KTWorkflowTriggerInstance::get($_REQUEST['fTriggerInstanceId']);
  1461 + if (PEAR::isError($oTriggerInstance)) {
  1462 + $this->errorRedirectTo('editTransition', _kt('Unable to load trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId());
  1463 + exit(0);
  1464 + }
  1465 +
  1466 + // grab the transition ns from the request.
  1467 + $KTWFTriggerReg =& KTWorkflowTriggerRegistry::getSingleton();
  1468 +
  1469 + $this->startTransaction();
  1470 +
  1471 + $oTrigger = $KTWFTriggerReg->getWorkflowTrigger($oTriggerInstance->getNamespace());
  1472 + if (PEAR::isError($oTrigger)) {
  1473 + $this->errorRedirectTo('editTransition', _kt('Unable to load trigger.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId());
  1474 + exit(0);
  1475 + }
  1476 + $oTrigger->loadConfig($oTriggerInstance);
  1477 +
  1478 + $res = $oTriggerInstance->delete();
  1479 + if (PEAR::isError($res)) {
  1480 + $this->errorRedirectTo('editTransition', _kt('Unable to delete trigger: ') . $res->getMessage(), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId());
  1481 + exit(0);
  1482 + }
  1483 +
  1484 + $this->successRedirectTo('editTransition', _kt('Trigger deleted.'), 'fWorkflowId=' . $oWorkflow->getId() . '&fTransitionId=' . $oTransition->getId());
  1485 + exit(0);
  1486 + }
  1487 +
  1488 +
1451 1489 // }}}
1452 1490  
1453 1491 }
... ...
templates/ktcore/workflow/editTransition.smarty
... ... @@ -71,8 +71,8 @@ requirement.{/i18n}&lt;/p&gt;
71 71 <tr>
72 72 <td>{$oTrigger->getName()}</td>
73 73 <td>{$oTrigger->getConfigDescription()}</td>
74   - <td><a href="{addQS}action=editTrigger&fWorkflowId={$oWorkflow->getId()}&fTransitionId={$oTransition->getId()}&fTriggerInstanceId={$oTrigger->getConfigId()}{/addQS}">edit</a></td>
75   - <td>x</td>
  74 + <td><a class="ktAction ktEdit" href="{addQS}action=editTrigger&fWorkflowId={$oWorkflow->getId()}&fTransitionId={$oTransition->getId()}&fTriggerInstanceId={$oTrigger->getConfigId()}{/addQS}">edit</a></td>
  75 + <td><a class="ktAction ktDelete" href="{addQS}action=deleteTrigger&fWorkflowId={$oWorkflow->getId()}&fTransitionId={$oTransition->getId()}&fTriggerInstanceId={$oTrigger->getConfigId()}{/addQS}">delete</a></td>
76 76 </tr>
77 77 {/foreach}
78 78 </tbody>
... ... @@ -85,5 +85,27 @@ requirement.{/i18n}&lt;/p&gt;
85 85 {if empty($aActionTriggers)}
86 86 <div class="ktInfo"><p>{i18n}No actions are performed when this transition occurs.{/i18n}</p></div>
87 87 {else}
  88 +
  89 + <table class="kt_collection narrow" cellspacing="0">
  90 + <thead>
  91 + <tr>
  92 + <th>{i18n}Trigger{/i18n}</th>
  93 + <th>{i18n}Configuration{/i18n}</th>
  94 + <th>{i18n}Edit{/i18n}</th>
  95 + <th>{i18n}Delete{/i18n}</th>
  96 + </tr>
  97 + </thead>
  98 + <tbody>
  99 + {foreach from=$aActionTriggers item=oTrigger}
  100 + <tr>
  101 + <td>{$oTrigger->getName()}</td>
  102 + <td>{$oTrigger->getConfigDescription()}</td>
  103 + <td><a href="{addQS}action=editTrigger&fWorkflowId={$oWorkflow->getId()}&fTransitionId={$oTransition->getId()}&fTriggerInstanceId={$oTrigger->getConfigId()}{/addQS}">edit</a></td>
  104 + <td>x</td>
  105 + </tr>
  106 + {/foreach}
  107 + </tbody>
  108 + </table>
  109 +
88 110 {/if}
89 111 </fieldset>
... ...
templates/ktcore/workflowtriggers/condition.smarty 0 โ†’ 100644
  1 +<h2>{i18n}Guard Condition for Transition{/i18n}</h2>
  2 +
  3 +<form action="{$smarty.server.PHP_SELF}" method="POST">
  4 +<fieldset>
  5 + <legend>{i18n}Guard Condition{/i18n}</legend>
  6 +
  7 +{* misc, boring args *}
  8 +{foreach from=$args item=val key=name}
  9 +<input type="hidden" name="{$name}" value="{$val}" />
  10 +{/foreach}
  11 +
  12 +<p class="descriptiveText">{i18n}Specify which condition the document must fulfill before this transition becomes available.{/i18n}</p>
  13 +
  14 +{* FIXME keep old selection *}
  15 +
  16 +{foreach from=$conditions item=sConditionName key=iConditionId}
  17 + <input type="radio" name="condition_id" value="{$iConditionId}" /> {i18n}{$sConditionName}{/i18n} <br />
  18 +{/foreach}
  19 +
  20 +<div class="form_action">
  21 + <input type="submit" value="{i18n}Save Trigger{/i18n}" />
  22 + {* FIXME how do I cancel again? *}
  23 +</div>
  24 +</fieldset>
  25 +</form>
0 26 \ No newline at end of file
... ...
templates/ktcore/workflowtriggers/group.smarty 0 โ†’ 100644
  1 +<h2>{i18n}Guard Groups for Transition{/i18n}</h2>
  2 +
  3 +<form action="{$smarty.server.PHP_SELF}" method="POST">
  4 +<fieldset>
  5 + <legend>{i18n}Guard Groups{/i18n}</legend>
  6 +
  7 +{* misc, boring args *}
  8 +{foreach from=$args item=val key=name}
  9 +<input type="hidden" name="{$name}" value="{$val}" />
  10 +{/foreach}
  11 +
  12 +<p class="descriptiveText">{i18n}Specify which group the user will require in order to perform this transition.{/i18n}</p>
  13 +
  14 +{* FIXME keep old selection *}
  15 +
  16 +{foreach from=$groups item=sGroupName key=iGroupId}
  17 + <input type="radio" name="group_id" value="{$iGroupId}" /> {i18n}{$sGroupName}{/i18n} <br />
  18 +{/foreach}
  19 +
  20 +<div class="form_action">
  21 + <input type="submit" value="{i18n}Save Trigger{/i18n}" />
  22 + {* FIXME how do I cancel again? *}
  23 +</div>
  24 +</fieldset>
  25 +</form>
0 26 \ No newline at end of file
... ...
templates/ktcore/workflowtriggers/roles.smarty 0 โ†’ 100644
  1 +<h2>{i18n}Guard Roles for Transition{/i18n}</h2>
  2 +
  3 +<form action="{$smarty.server.PHP_SELF}" method="POST">
  4 +<fieldset>
  5 + <legend>{i18n}Guard Roles{/i18n}</legend>
  6 +
  7 +{* misc, boring args *}
  8 +{foreach from=$args item=val key=name}
  9 +<input type="hidden" name="{$name}" value="{$val}" />
  10 +{/foreach}
  11 +
  12 +<p class="descriptiveText">{i18n}Specify which role the user will require in order to perform this transition.{/i18n}</p>
  13 +
  14 +{* FIXME keep old selection *}
  15 +
  16 +{foreach from=$roles item=sRoleName key=iRoleId}
  17 + <input type="radio" name="role_id" value="{$iRoleId}" /> {i18n}{$sRoleName}{/i18n} <br />
  18 +{/foreach}
  19 +
  20 +<div class="form_action">
  21 + <input type="submit" value="{i18n}Save Trigger{/i18n}" />
  22 + {* FIXME how do I cancel again? *}
  23 +</div>
  24 +</fieldset>
  25 +</form>
0 26 \ No newline at end of file
... ...