Commit 2e73f774fe6dddafd09791e362a06b774e2dda77

Authored by bshuttle
1 parent 907deac9

Workflow can now generate notifications (tentative)


git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@4725 c91229c3-7414-0410-bfa2-8a42b809f60b
lib/dashboard/Notification.inc.php
... ... @@ -9,6 +9,10 @@ require_once(KT_LIB_DIR . '/users/User.inc');
9 9 require_once(KT_LIB_DIR . '/documentmanagement/Document.inc');
10 10 require_once(KT_LIB_DIR . '/foldermanagement/Folder.inc');
11 11  
  12 +require_once(KT_LIB_DIR . '/workflow/workflowutil.inc.php');
  13 +
  14 +require_once(KT_LIB_DIR . '/templating/templating.inc.php');
  15 +
12 16 /**
13 17 * class Notification
14 18 *
... ... @@ -71,6 +75,9 @@ class KTNotification extends KTEntity {
71 75 function render() {
72 76 $notificationRegistry =& KTNotificationRegistry::getSingleton();
73 77 $handler = $notificationRegistry->getHandler($this->sType);
  78 +
  79 + if (is_null($handler)) { return null; }
  80 +
74 81 return $handler->handleNotification($this);
75 82 }
76 83  
... ... @@ -313,4 +320,59 @@ class KTSubscriptionNotification extends KTNotificationHandler {
313 320  
314 321 $notificationRegistry->registerNotificationHandler("ktcore/subscriptions","KTSubscriptionNotification");
315 322  
  323 +class KTWorkflowNotification extends KTNotificationHandler {
  324 +
  325 + function & clearNotificationsForDocument($oDocument) {
  326 + $aNotifications = KTNotification::getList('data_int_1 = ' . $oDocument->getId());
  327 + foreach ($aNotifications as $oNotification) {
  328 + $oNotification->delete();
  329 + }
  330 +
  331 + }
  332 +
  333 + function & newNotificationForDocument($oDocument, $oUser, $oState, $oActor, $sComments) {
  334 + $aInfo = array();
  335 + $aInfo['sData1'] = $oState->getName();
  336 + $aInfo['sData2'] = $sComments;
  337 + $aInfo['iData1'] = $oDocument->getId();
  338 + $aInfo['iData2'] = $oActor->getId();
  339 + $aInfo['sType'] = 'ktcore/workflow';
  340 + $aInfo['dCreationDate'] = getCurrentDateTime();
  341 + $aInfo['iUserId'] = $oUser->getId();
  342 + $aInfo['sLabel'] = $oDocument->getName();
  343 +
  344 + return KTNotification::createFromArray($aInfo);
  345 + }
  346 +
  347 + function handleNotification($oKTNotification) {
  348 + $oTemplating =& KTTemplating::getSingleton();
  349 + $oTemplate =& $oTemplating->loadTemplate('ktcore/workflow/workflow_notification');
  350 + $oTemplate->setData(array(
  351 + 'context' => $this,
  352 + 'document_id' => $oKTNotification->getIntData1(),
  353 + 'state_name' => $oKTNotification->getStrData1(),
  354 + 'actor' => User::get($oKTNotification->getIntData2()),
  355 + 'document_name' => $oKTNotification->getLabel(),
  356 + 'notify_id' => $oKTNotification->getId(),
  357 + ));
  358 + return $oTemplate->render();
  359 + }
  360 +
  361 + function resolveNotification($oKTNotification) {
  362 + $notify_action = KTUtil::arrayGet($_REQUEST, 'notify_action', null);
  363 + if ($notify_action == 'clear') {
  364 + $_SESSION['KTInfoMessage'][] = _('Workflow Notification cleared.');
  365 + $oKTNotification->delete();
  366 + exit(redirect(generateControllerLink('dashboard')));
  367 + }
  368 +
  369 + $params = 'fDocumentId=' . $oKTNotification->getIntData1();
  370 + $url = generateControllerLink('viewDocument', $params);
  371 + $oKTNotification->delete(); // clear the alert.
  372 + exit(redirect($url));
  373 + }
  374 +}
  375 +
  376 +$notificationRegistry->registerNotificationHandler("ktcore/workflow","KTWorkflowNotification");
  377 +
316 378 ?>
... ...
lib/roles/roleallocation.inc.php
... ... @@ -121,7 +121,7 @@ class RoleAllocation extends KTEntity {
121 121 foreach ($aUsers as $iUserId) {
122 122 $oUser = User::get($iUserId);
123 123 if (!(PEAR::isError($oUser) || ($oUser == false))) {
124   - $aFullUsers[] = $oUser;
  124 + $aFullUsers[$iUserId] = $oUser;
125 125 }
126 126 }
127 127  
... ... @@ -144,7 +144,7 @@ class RoleAllocation extends KTEntity {
144 144 foreach ($aGroups as $iGroupId) {
145 145 $oGroup = Group::get($iGroupId);
146 146 if (!(PEAR::isError($oGroup) || ($oGroup == false))) {
147   - $aFullGroups[] = $oGroup;
  147 + $aFullGroups[$iGroupId] = $oGroup;
148 148 }
149 149 }
150 150  
... ...
lib/workflow/workflowutil.inc.php
... ... @@ -10,6 +10,13 @@ require_once(KT_LIB_DIR . '/documentmanagement/DocumentTransaction.inc');
10 10 require_once(KT_LIB_DIR . '/search/searchutil.inc.php');
11 11 require_once(KT_LIB_DIR . '/roles/roleallocation.inc.php');
12 12  
  13 +require_once(KT_LIB_DIR . '/groups/Group.inc');
  14 +require_once(KT_LIB_DIR . '/users/User.inc');
  15 +require_once(KT_LIB_DIR . '/roles/Role.inc');
  16 +
  17 +require_once(KT_LIB_DIR . '/dashboard/Notification.inc.php');
  18 +
  19 +
13 20 class KTWorkflowUtil {
14 21 // {{{ saveTransitionsFrom
15 22 /**
... ... @@ -102,7 +109,16 @@ class KTWorkflowUtil {
102 109 'state_id' => $iStartStateId,
103 110 );
104 111 $sTable = KTUtil::getTableName('workflow_documents');
105   - return DBUtil::autoInsert($sTable, $aValues, $aOptions);
  112 + $res = DBUtil::autoInsert($sTable, $aValues, $aOptions);
  113 +
  114 + // FIXME does this function as expected?
  115 + $oUser = User::get($_SESSION['userID']);
  116 +
  117 + $oTargetState = KTWorkflowState::get($iStartStateId);
  118 + KTWorkflowUtil::informUsersForState($oTargetState,
  119 + KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, '');
  120 +
  121 + return $res;
106 122 }
107 123 // }}}
108 124  
... ... @@ -142,6 +158,14 @@ class KTWorkflowUtil {
142 158 'state_id' => $iStartStateId,
143 159 );
144 160 $sTable = KTUtil::getTableName('workflow_documents');
  161 +
  162 + $oUser = User::get($_SESSION['userID']);
  163 + $oTargetState = KTWorkflowState::get($iStartStateId);
  164 +
  165 + KTWorkflowUtil::informUsersForState($oTargetState,
  166 + KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, '');
  167 +
  168 +
145 169 return DBUtil::autoInsert($sTable, $aValues, $aOptions);
146 170 }
147 171 // }}}
... ... @@ -457,10 +481,103 @@ class KTWorkflowUtil {
457 481 $oDocumentTransaction = & new DocumentTransaction($oDocument, $sTransactionComments, 'ktcore.transactions.workflow_state_transition');
458 482 $oDocumentTransaction->create();
459 483  
  484 + KTWorkflowUtil::informUsersForState($oTargetState, KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, $sComments);
  485 +
460 486 return true;
461 487 }
462 488 // }}}
463 489  
  490 + // {{{ informUsersForState
  491 + function informUsersForState($oState, $aInformed, $oDocument, $oUser, $sComments) {
  492 + // say no to duplicates.
  493 +
  494 + KTWorkflowNotification::clearNotificationsForDocument($oDocument);
  495 +
  496 + $aUsers = array();
  497 + $aGroups = array();
  498 + $aRoles = array();
  499 +
  500 + foreach (KTUtil::arrayGet($aInformed,'user',array()) as $iUserId) {
  501 + $oU = User::get($iUserId);
  502 + if (PEAR::isError($oU) || ($oU == false)) {
  503 + continue;
  504 + } else {
  505 + $aUsers[$oU->getId()] = $oU();
  506 + }
  507 + }
  508 +
  509 + foreach (KTUtil::arrayGet($aInformed,'group',array()) as $iGroupId) {
  510 + $oG = Group::get($iGroupId);
  511 + if (PEAR::isError($oG) || ($oG == false)) {
  512 + continue;
  513 + } else {
  514 + $aGroups[$oG->getId()] = $oG();
  515 + }
  516 + }
  517 +
  518 + foreach (KTUtil::arrayGet($aInformed,'role',array()) as $iRoleId) {
  519 + $oR = Role::get($iRoleId);
  520 + if (PEAR::isError($oR) || ($oR == false)) {
  521 + continue;
  522 + } else {
  523 + $aRoles[] = $oR;
  524 + }
  525 + }
  526 +
  527 +
  528 +
  529 + // FIXME extract this into a util - I see us using this again and again.
  530 + // start with roles ... roles _only_ ever contain groups.
  531 + foreach ($aRoles as $oRole) {
  532 + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($oDocument->getFolderID(), $oRole->getId());
  533 + $aRoleUsers = $oRoleAllocation->getUsers();
  534 + $aRoleGroups = $oRoleAllocation->getGroups();
  535 +
  536 + foreach ($aRoleUsers as $id => $oU) {
  537 + $aUsers[$id] = $oU;
  538 + }
  539 + foreach ($aRoleGroups as $id => $oGroup) {
  540 + $aGroups[$id] = $oGroup;
  541 + }
  542 + }
  543 +
  544 +
  545 +
  546 + // we now have a (potentially overlapping) set of groups, which may
  547 + // have subgroups.
  548 + //
  549 + // what we need to do _now_ is build a canonical set of groups, and then
  550 + // generate the singular user-base.
  551 +
  552 + $aGroupMembershipSet = GroupUtil::buildGroupArray();
  553 + $aAllIds = array_keys($aGroups);
  554 + foreach ($aGroups as $id => $oGroup) {
  555 + $aAllIds = array_merge($aGroupMembershipSet[$id], $aAllIds);
  556 + }
  557 +
  558 + foreach ($aAllIds as $id) {
  559 + if (!array_key_exists($id, $aGroups)) {
  560 + $aGroups[$id] = Group::get($id);
  561 + }
  562 + }
  563 +
  564 + // now, merge this (again) into the user-set.
  565 + foreach ($aGroups as $oGroup) {
  566 + $aNewUsers = $oGroup->getUsers();
  567 + foreach ($aNewUsers as $id => $oU) {
  568 + if (!array_key_exists($id, $aUsers)) {
  569 + $aUsers[$id] = $oU;
  570 + }
  571 + }
  572 + }
  573 +
  574 + // and done.
  575 + foreach ($aUsers as $oU) {
  576 + KTWorkflowNotification::newNotificationForDocument($oDocument, $oU, $oState, $oUser, $sComments);
  577 + }
  578 + }
  579 + // }}}
  580 +
464 581 // {{{ setInformedForState
465 582 /**
466 583 * Sets which users/groups/roles are to be informed when a state is
... ...
plugins/ktcore/admin/workflows.php
... ... @@ -20,6 +20,10 @@ require_once(KT_LIB_DIR . '/actions/documentaction.inc.php');
20 20  
21 21 require_once(KT_LIB_DIR . '/widgets/portlet.inc.php');
22 22  
  23 +require_once(KT_LIB_DIR . '/users/User.inc');
  24 +require_once(KT_LIB_DIR . '/groups/Group.inc');
  25 +require_once(KT_LIB_DIR . '/roles/Role.inc');
  26 +
23 27 class WorkflowNavigationPortlet extends KTPortlet {
24 28 var $oWorkflow;
25 29  
... ... @@ -202,7 +206,58 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
202 206 }
203 207  
204 208 function getNotificationStringForState($oState) {
205   - return _('No roles and groups notified.');
  209 + $aAllowed = KTWorkflowUtil::getInformedForState($oState);
  210 +
  211 + $aUsers = array();
  212 + $aGroups = array();
  213 + $aRoles = array();
  214 +
  215 + foreach (KTUtil::arrayGet($aAllowed,'user',array()) as $iUserId) {
  216 + $oU = User::get($iUserId);
  217 + if (PEAR::isError($oU) || ($oU == false)) {
  218 + continue;
  219 + } else {
  220 + $aUsers[] = $oU->getName();
  221 + }
  222 + }
  223 +
  224 + foreach (KTUtil::arrayGet($aAllowed,'group',array()) as $iGroupId) {
  225 + $oG = Group::get($iGroupId);
  226 + if (PEAR::isError($oG) || ($oG == false)) {
  227 + continue;
  228 + } else {
  229 + $aGroups[] = $oG->getName();
  230 + }
  231 + }
  232 +
  233 + foreach (KTUtil::arrayGet($aAllowed,'role',array()) as $iRoleId) {
  234 + $oR = Role::get($iRoleId);
  235 + if (PEAR::isError($oR) || ($oR == false)) {
  236 + continue;
  237 + } else {
  238 + $aRoles[] = $oR->getName();
  239 + }
  240 + }
  241 +
  242 + $sNotify = '';
  243 + if (!empty($aUsers)) {
  244 + $sNotify .= '<em>' . _('Users:') . '</em>';
  245 + $sNotify .= implode(', ', $aUsers);
  246 + }
  247 +
  248 + if (!empty($aGroups)) {
  249 + $sNotify .= '<em>' . _('Groups:') . '</em>';
  250 + $sNotify .= implode(', ', $aGroups);
  251 + }
  252 +
  253 + if (!empty($aRoles)) {
  254 + $sNotify .= '<em>' . _('Roles:') . '</em>';
  255 + $sNotify .= implode(', ', $aRoles);
  256 + }
  257 +
  258 + if (empty($sNotify)) { $sNotify = _('No users to be notified.'); }
  259 +
  260 + return $sNotify;
206 261 }
207 262  
208 263 function transitionAvailable($oTransition, $oState) {
... ... @@ -735,33 +790,28 @@ class KTWorkflowDispatcher extends KTAdminDispatcher {
735 790 $oState =& $this->oValidator->validateWorkflowState($_REQUEST['fStateId']);
736 791 $sTargetAction = 'editState';
737 792 $sTargetParams = 'fWorkflowId=' . $oWorkflow->getId() . '&fStateId=' . $oState->getId();
738   - $aRoleIds = KTUtil::arrayGet($_REQUEST, 'fRoleIds');
739   - if (empty($aRoleIds)) {
740   - $aRoleIds = array();
  793 + $aNotification = (array) KTUtil::arrayGet($_REQUEST, 'fNotification');
  794 +
  795 + if (empty($aNotification['role'])) {
  796 + $aNotification['role'] = array();
741 797 }
742   - if (!is_array($aRoleIds)) {
  798 + if (!is_array($aNotification['role'])) {
743 799 $this->errorRedirectTo($sTargetAction, _('Invalid roles specified'), $sTargetParams);
744 800 }
745   - $aGroupIds = KTUtil::arrayGet($_REQUEST, 'fGroupIds');
746   - if (empty($aGroupIds)) {
747   - $aGroupIds = array();
  801 +
  802 + if (empty($aNotification['group'])) {
  803 + $aNotification['group'] = array();
748 804 }
749   - if (!is_array($aGroupIds)) {
  805 + if (!is_array($aNotification['group'])) {
750 806 $this->errorRedirectTo($sTargetAction, _('Invalid groups specified'), $sTargetParams);
751 807 }
752   - $aUserIds = KTUtil::arrayGet($_REQUEST, 'fUserIds');
753   - if (empty($aUserIds)) {
754   - $aUserIds = array();
755   - }
756   - if (!is_array($aUserIds)) {
757   - $this->errorRedirectTo($sTargetAction, _('Invalid users specified'), $sTargetParams);
  808 +
  809 + $aNotification['user'] = array(); // force override
  810 +
  811 + $res = KTWorkflowUtil::setInformedForState($oState, $aNotification);
  812 + if (PEAR::isError($res)) {
  813 + $this->errorRedirectTo($sTargetAction, sprintf(_('Failed to update the notification lists: %s'),$res->getMessage()), $sTargetParams);
758 814 }
759   - $aAllowed = array(
760   - 'role' => $aRoleIds,
761   - 'group' => $aGroupIds,
762   - 'user' => $aUserIds,
763   - );
764   - KTWorkflowUtil::setInformedForState($oState, $aAllowed);
765 815 $this->successRedirectTo($sTargetAction, _('Changes saved'), $sTargetParams);
766 816 }
767 817 // }}}
... ...
templates/ktcore/workflow/editState.smarty
... ... @@ -19,7 +19,8 @@ the &quot;sent&quot; &lt;strong&gt;transition&lt;/strong&gt; has been performed by a user.&lt;/p&gt;
19 19 </div>
20 20 </fieldset>
21 21 </form>
22   -{*
  22 +
  23 +
23 24 <form action="{$smarty.server.PHP_SELF}" method="POST">
24 25 <input type="hidden" name="action" value="saveInform" />
25 26 <input type="hidden" name="fWorkflowId" value="{$oWorkflow->getId()}" />
... ... @@ -33,7 +34,7 @@ informed when this state is reached.{/i18n}&lt;/p&gt;
33 34  
34 35 {if $aRoles}
35 36 <h3>{i18n}Roles{/i18n}</h3>
36   -{entity_checkboxes entities=$aRoles name="fRoleIds" multiple="true" selected=$aInformed.role assign=aBoxes}
  37 +{entity_checkboxes entities=$aRoles name="fNotification[role]" multiple="true" selected=$aInformed.role assign=aBoxes}
37 38 {foreach from=$aBoxes item=sBox}
38 39 {$sBox}<br />
39 40 {/foreach}
... ... @@ -41,7 +42,7 @@ informed when this state is reached.{/i18n}&lt;/p&gt;
41 42  
42 43 {if $aGroups}
43 44 <h3>{i18n}Groups{/i18n}</h3>
44   -{entity_checkboxes entities=$aGroups name="fGroupIds" multiple="true" selected=$aInformed.group assign=aBoxes}
  45 +{entity_checkboxes entities=$aGroups name="fNotification[group]" multiple="true" selected=$aInformed.group assign=aBoxes}
45 46 {foreach from=$aBoxes item=sBox}
46 47 {$sBox}<br />
47 48 {/foreach}
... ... @@ -49,10 +50,14 @@ informed when this state is reached.{/i18n}&lt;/p&gt;
49 50  
50 51 {if (empty($aGroups) && empty($aRoles))}
51 52 <div class="ktInfo"><p>{i18n}No groups or roles are defined in the DMS.{/i18n}</p></div>
  53 +{else}
  54 +<div class="form_actions">
  55 + <input type="submit" value="{i18n}Update users to inform{/i18n}" />
  56 +</div>
52 57 {/if}
53 58  
54 59 </fieldset>
55   -*}
  60 +</form>
56 61  
57 62 {*
58 63 <h3>{i18n}Assigned Permissions{/i18n}</h3>
... ...
templates/ktcore/workflow/workflow_notification.smarty 0 → 100644
  1 +<dt class="actionitem">{$document_name}</dt>
  2 +<dd class="actionmessage">
  3 + {i18n arg_name=$document_name arg_state=$state_name}The document <strong>#name#</strong> has changed to
  4 + state <strong>#state#</strong>, and you are specified as one of the users to inform
  5 + about documents in this state.{/i18n}
  6 + <div class="actionoptions">
  7 + <a href="{$absoluteRootUrl}/notify.php?id={$notify_id}">{i18n}View Document{/i18n}</a>
  8 + | <a href="{$absoluteRootUrl}/notify.php?id={$notify_id}&notify_action=clear">{i18n}Clear Alert{/i18n}</a>
  9 + </div>
  10 +</dd>
... ...