diff --git a/lib/dashboard/Notification.inc.php b/lib/dashboard/Notification.inc.php index b861066..48406a0 100644 --- a/lib/dashboard/Notification.inc.php +++ b/lib/dashboard/Notification.inc.php @@ -9,6 +9,10 @@ require_once(KT_LIB_DIR . '/users/User.inc'); require_once(KT_LIB_DIR . '/documentmanagement/Document.inc'); require_once(KT_LIB_DIR . '/foldermanagement/Folder.inc'); +require_once(KT_LIB_DIR . '/workflow/workflowutil.inc.php'); + +require_once(KT_LIB_DIR . '/templating/templating.inc.php'); + /** * class Notification * @@ -71,6 +75,9 @@ class KTNotification extends KTEntity { function render() { $notificationRegistry =& KTNotificationRegistry::getSingleton(); $handler = $notificationRegistry->getHandler($this->sType); + + if (is_null($handler)) { return null; } + return $handler->handleNotification($this); } @@ -313,4 +320,59 @@ class KTSubscriptionNotification extends KTNotificationHandler { $notificationRegistry->registerNotificationHandler("ktcore/subscriptions","KTSubscriptionNotification"); +class KTWorkflowNotification extends KTNotificationHandler { + + function & clearNotificationsForDocument($oDocument) { + $aNotifications = KTNotification::getList('data_int_1 = ' . $oDocument->getId()); + foreach ($aNotifications as $oNotification) { + $oNotification->delete(); + } + + } + + function & newNotificationForDocument($oDocument, $oUser, $oState, $oActor, $sComments) { + $aInfo = array(); + $aInfo['sData1'] = $oState->getName(); + $aInfo['sData2'] = $sComments; + $aInfo['iData1'] = $oDocument->getId(); + $aInfo['iData2'] = $oActor->getId(); + $aInfo['sType'] = 'ktcore/workflow'; + $aInfo['dCreationDate'] = getCurrentDateTime(); + $aInfo['iUserId'] = $oUser->getId(); + $aInfo['sLabel'] = $oDocument->getName(); + + return KTNotification::createFromArray($aInfo); + } + + function handleNotification($oKTNotification) { + $oTemplating =& KTTemplating::getSingleton(); + $oTemplate =& $oTemplating->loadTemplate('ktcore/workflow/workflow_notification'); + $oTemplate->setData(array( + 'context' => $this, + 'document_id' => $oKTNotification->getIntData1(), + 'state_name' => $oKTNotification->getStrData1(), + 'actor' => User::get($oKTNotification->getIntData2()), + 'document_name' => $oKTNotification->getLabel(), + 'notify_id' => $oKTNotification->getId(), + )); + return $oTemplate->render(); + } + + function resolveNotification($oKTNotification) { + $notify_action = KTUtil::arrayGet($_REQUEST, 'notify_action', null); + if ($notify_action == 'clear') { + $_SESSION['KTInfoMessage'][] = _('Workflow Notification cleared.'); + $oKTNotification->delete(); + exit(redirect(generateControllerLink('dashboard'))); + } + + $params = 'fDocumentId=' . $oKTNotification->getIntData1(); + $url = generateControllerLink('viewDocument', $params); + $oKTNotification->delete(); // clear the alert. + exit(redirect($url)); + } +} + +$notificationRegistry->registerNotificationHandler("ktcore/workflow","KTWorkflowNotification"); + ?> diff --git a/lib/roles/roleallocation.inc.php b/lib/roles/roleallocation.inc.php index 57cbf38..07b82b5 100644 --- a/lib/roles/roleallocation.inc.php +++ b/lib/roles/roleallocation.inc.php @@ -121,7 +121,7 @@ class RoleAllocation extends KTEntity { foreach ($aUsers as $iUserId) { $oUser = User::get($iUserId); if (!(PEAR::isError($oUser) || ($oUser == false))) { - $aFullUsers[] = $oUser; + $aFullUsers[$iUserId] = $oUser; } } @@ -144,7 +144,7 @@ class RoleAllocation extends KTEntity { foreach ($aGroups as $iGroupId) { $oGroup = Group::get($iGroupId); if (!(PEAR::isError($oGroup) || ($oGroup == false))) { - $aFullGroups[] = $oGroup; + $aFullGroups[$iGroupId] = $oGroup; } } diff --git a/lib/workflow/workflowutil.inc.php b/lib/workflow/workflowutil.inc.php index 88e3886..4e4fcfe 100644 --- a/lib/workflow/workflowutil.inc.php +++ b/lib/workflow/workflowutil.inc.php @@ -10,6 +10,13 @@ require_once(KT_LIB_DIR . '/documentmanagement/DocumentTransaction.inc'); require_once(KT_LIB_DIR . '/search/searchutil.inc.php'); require_once(KT_LIB_DIR . '/roles/roleallocation.inc.php'); +require_once(KT_LIB_DIR . '/groups/Group.inc'); +require_once(KT_LIB_DIR . '/users/User.inc'); +require_once(KT_LIB_DIR . '/roles/Role.inc'); + +require_once(KT_LIB_DIR . '/dashboard/Notification.inc.php'); + + class KTWorkflowUtil { // {{{ saveTransitionsFrom /** @@ -102,7 +109,16 @@ class KTWorkflowUtil { 'state_id' => $iStartStateId, ); $sTable = KTUtil::getTableName('workflow_documents'); - return DBUtil::autoInsert($sTable, $aValues, $aOptions); + $res = DBUtil::autoInsert($sTable, $aValues, $aOptions); + + // FIXME does this function as expected? + $oUser = User::get($_SESSION['userID']); + + $oTargetState = KTWorkflowState::get($iStartStateId); + KTWorkflowUtil::informUsersForState($oTargetState, + KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, ''); + + return $res; } // }}} @@ -142,6 +158,14 @@ class KTWorkflowUtil { 'state_id' => $iStartStateId, ); $sTable = KTUtil::getTableName('workflow_documents'); + + $oUser = User::get($_SESSION['userID']); + $oTargetState = KTWorkflowState::get($iStartStateId); + + KTWorkflowUtil::informUsersForState($oTargetState, + KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, ''); + + return DBUtil::autoInsert($sTable, $aValues, $aOptions); } // }}} @@ -457,10 +481,103 @@ class KTWorkflowUtil { $oDocumentTransaction = & new DocumentTransaction($oDocument, $sTransactionComments, 'ktcore.transactions.workflow_state_transition'); $oDocumentTransaction->create(); + KTWorkflowUtil::informUsersForState($oTargetState, KTWorkflowUtil::getInformedForState($oTargetState), $oDocument, $oUser, $sComments); + return true; } // }}} + // {{{ informUsersForState + function informUsersForState($oState, $aInformed, $oDocument, $oUser, $sComments) { + // say no to duplicates. + + KTWorkflowNotification::clearNotificationsForDocument($oDocument); + + $aUsers = array(); + $aGroups = array(); + $aRoles = array(); + + foreach (KTUtil::arrayGet($aInformed,'user',array()) as $iUserId) { + $oU = User::get($iUserId); + if (PEAR::isError($oU) || ($oU == false)) { + continue; + } else { + $aUsers[$oU->getId()] = $oU(); + } + } + + foreach (KTUtil::arrayGet($aInformed,'group',array()) as $iGroupId) { + $oG = Group::get($iGroupId); + if (PEAR::isError($oG) || ($oG == false)) { + continue; + } else { + $aGroups[$oG->getId()] = $oG(); + } + } + + foreach (KTUtil::arrayGet($aInformed,'role',array()) as $iRoleId) { + $oR = Role::get($iRoleId); + if (PEAR::isError($oR) || ($oR == false)) { + continue; + } else { + $aRoles[] = $oR; + } + } + + + + // FIXME extract this into a util - I see us using this again and again. + // start with roles ... roles _only_ ever contain groups. + foreach ($aRoles as $oRole) { + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($oDocument->getFolderID(), $oRole->getId()); + $aRoleUsers = $oRoleAllocation->getUsers(); + $aRoleGroups = $oRoleAllocation->getGroups(); + + foreach ($aRoleUsers as $id => $oU) { + $aUsers[$id] = $oU; + } + foreach ($aRoleGroups as $id => $oGroup) { + $aGroups[$id] = $oGroup; + } + } + + + + // we now have a (potentially overlapping) set of groups, which may + // have subgroups. + // + // what we need to do _now_ is build a canonical set of groups, and then + // generate the singular user-base. + + $aGroupMembershipSet = GroupUtil::buildGroupArray(); + $aAllIds = array_keys($aGroups); + foreach ($aGroups as $id => $oGroup) { + $aAllIds = array_merge($aGroupMembershipSet[$id], $aAllIds); + } + + foreach ($aAllIds as $id) { + if (!array_key_exists($id, $aGroups)) { + $aGroups[$id] = Group::get($id); + } + } + + // now, merge this (again) into the user-set. + foreach ($aGroups as $oGroup) { + $aNewUsers = $oGroup->getUsers(); + foreach ($aNewUsers as $id => $oU) { + if (!array_key_exists($id, $aUsers)) { + $aUsers[$id] = $oU; + } + } + } + + // and done. + foreach ($aUsers as $oU) { + KTWorkflowNotification::newNotificationForDocument($oDocument, $oU, $oState, $oUser, $sComments); + } + } + // }}} + // {{{ setInformedForState /** * Sets which users/groups/roles are to be informed when a state is diff --git a/plugins/ktcore/admin/workflows.php b/plugins/ktcore/admin/workflows.php index 00472af..a106277 100755 --- a/plugins/ktcore/admin/workflows.php +++ b/plugins/ktcore/admin/workflows.php @@ -20,6 +20,10 @@ require_once(KT_LIB_DIR . '/actions/documentaction.inc.php'); require_once(KT_LIB_DIR . '/widgets/portlet.inc.php'); +require_once(KT_LIB_DIR . '/users/User.inc'); +require_once(KT_LIB_DIR . '/groups/Group.inc'); +require_once(KT_LIB_DIR . '/roles/Role.inc'); + class WorkflowNavigationPortlet extends KTPortlet { var $oWorkflow; @@ -202,7 +206,58 @@ class KTWorkflowDispatcher extends KTAdminDispatcher { } function getNotificationStringForState($oState) { - return _('No roles and groups notified.'); + $aAllowed = KTWorkflowUtil::getInformedForState($oState); + + $aUsers = array(); + $aGroups = array(); + $aRoles = array(); + + foreach (KTUtil::arrayGet($aAllowed,'user',array()) as $iUserId) { + $oU = User::get($iUserId); + if (PEAR::isError($oU) || ($oU == false)) { + continue; + } else { + $aUsers[] = $oU->getName(); + } + } + + foreach (KTUtil::arrayGet($aAllowed,'group',array()) as $iGroupId) { + $oG = Group::get($iGroupId); + if (PEAR::isError($oG) || ($oG == false)) { + continue; + } else { + $aGroups[] = $oG->getName(); + } + } + + foreach (KTUtil::arrayGet($aAllowed,'role',array()) as $iRoleId) { + $oR = Role::get($iRoleId); + if (PEAR::isError($oR) || ($oR == false)) { + continue; + } else { + $aRoles[] = $oR->getName(); + } + } + + $sNotify = ''; + if (!empty($aUsers)) { + $sNotify .= '' . _('Users:') . ''; + $sNotify .= implode(', ', $aUsers); + } + + if (!empty($aGroups)) { + $sNotify .= '' . _('Groups:') . ''; + $sNotify .= implode(', ', $aGroups); + } + + if (!empty($aRoles)) { + $sNotify .= '' . _('Roles:') . ''; + $sNotify .= implode(', ', $aRoles); + } + + if (empty($sNotify)) { $sNotify = _('No users to be notified.'); } + + return $sNotify; } function transitionAvailable($oTransition, $oState) { @@ -735,33 +790,28 @@ class KTWorkflowDispatcher extends KTAdminDispatcher { $oState =& $this->oValidator->validateWorkflowState($_REQUEST['fStateId']); $sTargetAction = 'editState'; $sTargetParams = 'fWorkflowId=' . $oWorkflow->getId() . '&fStateId=' . $oState->getId(); - $aRoleIds = KTUtil::arrayGet($_REQUEST, 'fRoleIds'); - if (empty($aRoleIds)) { - $aRoleIds = array(); + $aNotification = (array) KTUtil::arrayGet($_REQUEST, 'fNotification'); + + if (empty($aNotification['role'])) { + $aNotification['role'] = array(); } - if (!is_array($aRoleIds)) { + if (!is_array($aNotification['role'])) { $this->errorRedirectTo($sTargetAction, _('Invalid roles specified'), $sTargetParams); } - $aGroupIds = KTUtil::arrayGet($_REQUEST, 'fGroupIds'); - if (empty($aGroupIds)) { - $aGroupIds = array(); + + if (empty($aNotification['group'])) { + $aNotification['group'] = array(); } - if (!is_array($aGroupIds)) { + if (!is_array($aNotification['group'])) { $this->errorRedirectTo($sTargetAction, _('Invalid groups specified'), $sTargetParams); } - $aUserIds = KTUtil::arrayGet($_REQUEST, 'fUserIds'); - if (empty($aUserIds)) { - $aUserIds = array(); - } - if (!is_array($aUserIds)) { - $this->errorRedirectTo($sTargetAction, _('Invalid users specified'), $sTargetParams); + + $aNotification['user'] = array(); // force override + + $res = KTWorkflowUtil::setInformedForState($oState, $aNotification); + if (PEAR::isError($res)) { + $this->errorRedirectTo($sTargetAction, sprintf(_('Failed to update the notification lists: %s'),$res->getMessage()), $sTargetParams); } - $aAllowed = array( - 'role' => $aRoleIds, - 'group' => $aGroupIds, - 'user' => $aUserIds, - ); - KTWorkflowUtil::setInformedForState($oState, $aAllowed); $this->successRedirectTo($sTargetAction, _('Changes saved'), $sTargetParams); } // }}} diff --git a/templates/ktcore/workflow/editState.smarty b/templates/ktcore/workflow/editState.smarty index 0589ce0..c672649 100644 --- a/templates/ktcore/workflow/editState.smarty +++ b/templates/ktcore/workflow/editState.smarty @@ -19,7 +19,8 @@ the "sent" transition has been performed by a user.
-{* + + {*