diff --git a/config/tableMappings.inc b/config/tableMappings.inc index 516d4b3..6be26cc 100644 --- a/config/tableMappings.inc +++ b/config/tableMappings.inc @@ -142,4 +142,5 @@ $default->permission_dynamic_assignments_table = 'permission_dynamic_assignments $default->notifications_table = "notifications"; $default->authentication_sources_table = "authentication_sources"; $default->dashlet_disable_table = "dashlet_disables"; +$default->role_allocations_table = "role_allocations"; ?> diff --git a/lib/permissions/permissionutil.inc.php b/lib/permissions/permissionutil.inc.php index 44e12aa..24c0254 100644 --- a/lib/permissions/permissionutil.inc.php +++ b/lib/permissions/permissionutil.inc.php @@ -10,6 +10,7 @@ require_once(KT_LIB_DIR . "/permissions/permissionlookupassignment.inc.php"); require_once(KT_LIB_DIR . "/permissions/permissionobject.inc.php"); require_once(KT_LIB_DIR . "/permissions/permissiondynamiccondition.inc.php"); require_once(KT_LIB_DIR . "/groups/GroupUtil.php"); +require_once(KT_LIB_DIR . "/roles/roleallocation.inc.php"); class KTPermissionUtil { // {{{ generateDescriptor @@ -182,9 +183,11 @@ class KTPermissionUtil { $oPD = KTPermissionDescriptor::get($oPA->getPermissionDescriptorID()); $aGroupIDs = $oPD->getGroups(); $aUserIDs = array(); + $aRoleIDs = $oPD->getRoles(); $aAllowed = array( "group" => $aGroupIDs, "user" => $aUserIDs, + "role" => $aRoleIDs, ); $aMapPermAllowed[$oPA->getPermissionID()] = $aAllowed; } @@ -207,6 +210,31 @@ class KTPermissionUtil { } } + // if we have roles: nearest folder. + $iRoleSourceFolder = null; + if (is_a($oFolderOrDocument, 'Document')) { $iRoleSourceFolder = $oFolderOrDocument->getFolderID(); } + else { $iRoleSourceFolder = $oFolderOrDocument->getId(); } + + // very minor perf win: map role_id (in context) to PD. + $_roleCache = array(); + + foreach ($aMapPermAllowed as $iPermissionId => $aAllowed) { + if (array_key_exists('role', $aAllowed)) { + foreach ($aAllowed['role'] as $iRoleId) { + // store the PD <-> RoleId map + if (!array_key_exists($iRoleId, $_roleCache)) { + $oRoleAllocation =& RoleAllocation::getAllocationsForFolderAndRole($iRoleSourceFolder, $iRoleId); + $_roleCache[$iRoleId] = $oRoleAllocation; + } + // roles are _not_ always assigned (can be null at root) + if ($_roleCache[$iRoleId] != null) { + $aAllowed['user'] = array_merge($aAllowed['user'], $_roleCache[$iRoleId]->getUsers()); + $aAllowed['group'] = array_merge($aAllowed['group'], $_roleCache[$iRoleId]->getGroups()); + } + } + } + } + $aMapPermDesc = array(); foreach ($aMapPermAllowed as $iPermissionId => $aAllowed) { $oLookupPD = KTPermissionUtil::getOrCreateDescriptor($aAllowed); diff --git a/lib/roles/Role.inc b/lib/roles/Role.inc index f65806d..d424f89 100644 --- a/lib/roles/Role.inc +++ b/lib/roles/Role.inc @@ -21,170 +21,43 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * @version $Revision$ - * @author Rob Cherry, Jam Warehouse (Pty) Ltd, South Africa + * @author Brad Shuttleworth, Jam Warehouse (Pty) Ltd, South Africa * @package lib.roles */ + +require_once(KT_LIB_DIR . "/ktentity.inc"); +require_once(KT_LIB_DIR . "/util/ktutil.inc"); + class Role extends KTEntity { /** role object primary key */ var $iId; - /** role name */ var $sName; - /** is an active user*/ - var $bActive; - /** role has document read permission */ - var $bCanRead; - /** role has document write */ - var $bCanWrite; + var $_aFieldToSelect = array( + 'iId' => 'id', + 'sName' => 'name', + ); - - /** - * Default constructor - * - * @param String Role name - * @param boolean Role has document read permission - * @param boolean Role has document write permission - * - */ - function Role($sNewName = null, $bNewCanRead = false, $bNewCanWrite = false) { + function Role($sNewName = null, $x=null, $y=null) { //object not yet created in database $this->iId = -1; $this->sName = $sNewName; - $this->bCanRead = $bNewCanRead; - $this->bCanWrite = $bNewCanWrite; - } - - function getName() { - return $this->sName; - } - - function getID() { - return $this->iId; - } - - function getActive() { - return $this->bActive; - } - - function setActive($bNewValue) { - $this->bActive = $bNewValue; - } - - function getReadable() { - return $this->bCanRead; - } - - function setReadable($bNewValue) { - $this->bCanRead = $bNewValue; - } - - function getWriteable() { - return $this->bCanWrite; - } - - function setWriteable($bNewValue) { - $this->bCanWrite = $bNewValue; - } - - function setName($bNewValue) { - $this->sName = $bNewValue; } - function _fieldValues () { - return array( + function getName() { return $this->sName; } + function setName($sNewValue) { $this->sName = $sNewValue; } + + function _fieldValues () { return array( + 'id' => $this->iId, 'name' => $this->sName, - 'active' => KTUtil::anyToBool($this->bActive), - 'can_read' => KTUtil::anyToBool($this->bCanRead), - 'can_write' => KTUtil::anyToBool($this->bCanWrite), ); } - function _table () { - global $default; - return $default->roles_table; - } - - /** - * Delete the current object from the database - * - * @return boolean true on successful deletion, false otherwise and set $_SESSION["errorMessage"] - * - */ - function delete() { - global $default, $lang_err_database, $lang_err_object_key; - //only delete the object if it exists in the database - /* - if ($this->iId >= 0) { - - $sql = $default->db; - $query = "SELECT role_id FROM ". $default->groups_folders_approval_table ." WHERE role_id = ?"; - $aParams = array($this->iId); - $sql->query(array($query, $aParams)); - $rows = $sql->num_rows($sql); - - if ($rows > 0){ - $_SESSION["errorMessage"] = "Role::The Role " . $this->sName . " is Assigned to a folder!"; - return false; - } - } - */ - return parent::delete(); - } - - /** - * Static function. - * Given a roles primary key it will create a - * Roles object and populate it with the - * corresponding database values - * - * @return Role populated Role object on successful query, false otherwise and set $_SESSION["errorMessage"] - */ - function & get($iRoleID) { - global $default; - $sql = $default->db; - $result = $sql->query(array("SELECT * FROM $default->roles_table WHERE id = ?", $iRoleID));/*ok*/ - if ($result) { - if ($sql->next_record()) { - $oRole = & new Role($sql->f("name"), $sql->f("can_read"), $sql->f("can_write")); - $oRole->iId = $iRoleID; - $oRole->bActive = $sql->f("active"); - return $oRole; - } - $_SESSION["errorMessage"] = $lang_err_object_not_exist . "id = " . $iRoleID . " table = $default->roles_table"; - return false; - } - $_SESSION["errorMessage"] = $lang_err_database; - return false; - } - -/** - * Static function - * Get a list of web documents - * - * @param String Where clause (not required) - * - * @return Array array of Role objects, false otherwise and set $_SESSION["errorMessage"] - */ - function getList($sWhereClause = null) { - return KTEntityUtil::getList(Role::_table(), 'Role', $sWhereClause); - } -} - - - -/** -* Static function -* -* Creates a roles object from an array -* -* @param Array Array of parameters. Must match order of parameters in constructor -* -* @return User user object -*/ -function & roleCreateFromArray($aParameters) { - $oRole = & new Role($aParameters[0], $aParameters[1], $aParameters[2], $aParameters[3], $aParameters[4], $aParameters[5], $aParameters[6], $aParameters[7], $aParameters[8], $aParameters[9], $aParameters[10]); - return $oRole; + function _table () { return KTUtil::getTableName('roles'); } + function & get($iRoleId) { return KTEntityUtil::get('Role', $iRoleId); } + function & getList($sWhereClause = null) { return KTEntityUtil::getList2('Role', $sWhereClause); } + function & createFromArray($aOptions) { return KTEntityUtil::createFromArray('Role', $aOptions); } } ?> diff --git a/lib/roles/roleallocation.inc.php b/lib/roles/roleallocation.inc.php new file mode 100644 index 0000000..99f09bb --- /dev/null +++ b/lib/roles/roleallocation.inc.php @@ -0,0 +1,188 @@ + 'id', + 'iRoleId' => 'role_id', + 'iFolderId' => 'folder_id', + 'iPermissionDescriptorId' => 'permission_descriptor_id', + ); + + function setFolderId($iFolderId) { $this->iFolderId = $iFolderId; } + function setRoleId($iRoleId) { $this->iRoleId = $iRoleId; } + function setPermissionDescriptorId($iPermissionDescriptorId) { $this->iPermissionDescriptorId = $iPermissionDescriptorId; } + function getFolderId() { return $this->iFolderId; } + function getRoleId() { return $this->iRoleId; } + function getPermissionDescriptorId() { return $this->iPermissionDescriptorId; } + + // aggregate: set (for this alloc) the array('user' => array(), 'group' => array()). + function setAllowed($aAllowed) { + $oDescriptor = KTPermissionUtil::getOrCreateDescriptor($aAllowed); // fully done, etc. + $this->iPermissionDescriptorId = $oDescriptor->getId(); + } + + function _fieldValues () { return array( + 'role_id' => $this->iRoleId, + 'folder_id' => $this->iFolderId, + 'permission_descriptor_id' => $this->iPermissionDescriptorId, + ); + } + + /* getAllocationForFolderAndRole($iFolderId, $iRoleId) + * + * this is the key function: for a given folder and role, + * returns either a RoleAllocation object, or null + * (if there is none). It scans _up_ the hierachy of folders, + * trying to find the nearest such object with a folder_id + * in the mapping. + */ + function & getAllocationsForFolderAndRole($iFolderId, $iRoleId) { + // FIXME the query we use here is ... not very pleasant. + // NBM: is this the "right" way to do this? + $raTable = KTUtil::getTableName('role_allocations'); + + $fTable = Folder::_table(); + + $oFolder =& Folder::get($iFolderId); + $parents = $oFolder->getParentFolderIDs() . ',' . $iFolderId; // this is formatted as 1,2,3,4,5,6 - perfect for "WHERE". + + // FIXME what (if anything) do we need to do to check that this can't be used as an attack? + $folders = '(' . $parents . ')'; + + $sQuery = "SELECT ra.id as `id` FROM " . $raTable . " AS ra " . + ' LEFT JOIN ' . $fTable . ' AS f ON (f.id = ra.folder_id) ' . + ' WHERE f.id IN ' . $folders . + ' ORDER BY CHAR_LENGTH(f.parent_folder_ids) desc, f.parent_folder_ids DESC; '; + $aParams = array(); + + $aRoleAllocIds = DBUtil::getResultArrayKey(array($sQuery, $aParams), 'id'); + + if (false) { + print '
'; + var_dump($aRoleAllocIds); + print ''; + print $sQuery; + print ''; + } + + if (empty($aRoleAllocIds)) { + return null; + } + + $iAllocId = $aRoleAllocIds[0]; // array pop? + return RoleAllocation::get($iAllocId); + } + + // static, boilerplate. + function _table () { return KTUtil::getTableName('role_allocations'); } + function & get($iRoleId) { return KTEntityUtil::get('RoleAllocation', $iRoleId); } + function & getList($sWhereClause = null) { return KTEntityUtil::getList2('RoleAllocation', $sWhereClause); } + function & createFromArray($aOptions) { return KTEntityUtil::createFromArray('RoleAllocation', $aOptions); } + + function getPermissionDescriptor() { + // could be an error - return as-is. + $oDescriptor =& KTPermissionDescriptor::get($this->iPermissionDescriptorId); + return $oDescriptor; + } + + // setting users and groups needs to use permissionutil::getOrCreateDescriptor + function getUsers() { + $oDescriptor = $this->getPermissionDescriptor(); + $aUsers = array(); + if (PEAR::isError($oDescriptor) || ($oDescriptor == false)) { + return $aUsers; + } + $aAllowed = $oDescriptor->getAllowed(); + if ($aAllowed['user'] !== null) { + $aUsers = $aAllowed['user']; + } + + // now we want to map to oUsers, since that's what groups do. + $aFullUsers = array(); + foreach ($aUsers as $iUserId) { + $oUser = User::get($iUserId); + if (!(PEAR::isError($oUser) || ($oUser == false))) { + $aFullUsers[] = $oUser; + } + } + + return $aFullUsers; + } + + function getGroups() { + $oDescriptor = $this->getPermissionDescriptor(); + $aGroups = array(); + if (PEAR::isError($oDescriptor) || ($oDescriptor == false)) { + return $aGroups; + } + $aAllowed = $oDescriptor->getAllowed(); + if ($aAllowed['group'] !== null) { + $aGroups = $aAllowed['group']; + } + + // now we want to map to oUsers, since that's what groups do. + $aFullGroups = array(); + foreach ($aGroups as $iGroupId) { + $oGroup = Group::get($iGroupId); + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { + $aFullGroups[] = $oGroup; + } + } + + return $aFullGroups; + } + + // utility function to establish user membership in this allocation. + // FIXME nbm: is there are more coherent way to do this ITO your PD infrastructure? + function hasMember($oUser) { + $oPD = $this->getPermissionDescriptor(); + if (PEAR::isError($oPD) || ($oPD == false)) { + return false; + } + $aAllowed = $oPD->getAllowed(); + $iUserId = $oUser->getId(); + + if ($aAllowed['user'] != null) { + if (array_search($iUserId, $aAllowed['user']) !== false) { + return true; + } + } + + // now we need the group objects. + // FIXME this could accelerated to a single SQL query on group_user_link. + $aGroups = $this->getGroups(); + if (PEAR::isError($aGroups) || ($aGroups == false)) { + return false; + } else { + foreach ($aGroups as $oGroup) { + if ($oGroup->hasMember($oUser)) { + return true; + } + } + } + + return false; + } + +} + +?> diff --git a/lib/workflow/workflowutil.inc.php b/lib/workflow/workflowutil.inc.php index d92f184..74bcf7a 100644 --- a/lib/workflow/workflowutil.inc.php +++ b/lib/workflow/workflowutil.inc.php @@ -8,6 +8,7 @@ require_once(KT_LIB_DIR . '/permissions/permissionutil.inc.php'); require_once(KT_LIB_DIR . '/groups/GroupUtil.php'); 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'); class KTWorkflowUtil { // {{{ saveTransitionsFrom @@ -338,6 +339,18 @@ class KTWorkflowUtil { continue; } } + $iRoleId = $oTransition->getGuardRoleId(); + if ($iRoleId) { + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($oDocument->getFolderID(), $iRoleId); + + if ($oRoleAllocation == null) { // no role allocation, no fulfillment. + continue; + } + + if (!$oRoleAllocation->hasMember($oUser)) { + continue; + } + } $iConditionId = $oTransition->getGuardConditionId(); if ($iConditionId) { diff --git a/plugins/ktcore/KTPermissions.php b/plugins/ktcore/KTPermissions.php index 9d44e4a..a41723a 100644 --- a/plugins/ktcore/KTPermissions.php +++ b/plugins/ktcore/KTPermissions.php @@ -1,10 +1,15 @@ registerAction('documentaction', 'KTDocumentPermissionsAction', 'ktcore.actions.document.permissions'); + +class KTRoleAllocationPlugin extends KTFolderAction { + var $sDisplayName = 'Allocate Roles'; + var $sName = 'ktcore.actions.folder.roles'; + + var $_sShowPermission = "ktcore.permissions.write"; + var $bAutomaticTransaction = true; + + function do_main() { + $this->oPage->setTitle(_("Allocate Roles")); + $this->oPage->setBreadcrumbDetails(_("Allocate Roles")); + $oTemplating = new KTTemplating; + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles"); + + // we need to have: + // - a list of roles + // - with their users / groups + // - and that allocation id + $aRoles = array(); // stores data for display. + + $aRoleList = Role::getList(); + foreach ($aRoleList as $oRole) { + $iRoleId = $oRole->getId(); + $aRoles[$iRoleId] = array("name" => $oRole->getName()); + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($this->oFolder->getId(), $iRoleId); + + $u = array(); + $g = array(); + $aid = null; + $raid = null; + if ($oRoleAllocation == null) { + ; // nothing. + } else { + $raid = $oRoleAllocation->getId(); // real_alloc_id + if ($oRoleAllocation->getFolderId() == $this->oFolder->getId()) { + $aid = $oRoleAllocation->getid(); // alloc_id + } + $oPermDesc = KTPermissionDescriptor::get($oRoleAllocation->getPermissionDescriptorId()); + if (!PEAR::isError($oPermDesc)) { + $aAllowed = $oPermDesc->getAllowed(); + if (!empty($aAllowed['user'])) { + $u = $aAllowed['user']; + } + if (!empty($aAllowed['group'])) { + $g = $aAllowed['group']; + } + } + } + $aRoles[$iRoleId]['users'] = $u; + $aRoles[$iRoleId]['groups'] = $g; + $aRoles[$iRoleId]['allocation_id'] = $aid; + $aRoles[$iRoleId]['real_allocation_id'] = $raid; + } + + /* + print '
'; + var_dump($aRoles); + print ''; + */ + + + + // FIXME this is test data. + /* + $aRoles = array( + 1 => array('name' => 'Manager', 'users' => array(1), 'groups' => array(1), 'allocation_id' => 1), + 2 => array('name' => 'Peasant', 'users' => array(1), 'groups' => array(), 'allocation_id' => 2), + 3 => array('name' => 'Inherited', 'users' => array(), 'groups' => array(1), 'allocation_id' => null), + ); + */ + + + // final step. + + // map to users, groups. + foreach ($aRoles as $key => $role) { + $_users = array(); + foreach ($aRoles[$key]['users'] as $iUserId) { + $oUser = User::get($iUserId); + if (!(PEAR::isError($oUser) || ($oUser == false))) { + $_users[] = $oUser->getName(); + } + } + if (empty($_users)) { + $aRoles[$key]['users'] = ' ' . _('no users') . ''; + } else { + $aRoles[$key]['users'] = join(', ',$_users); + } + + $_groups = array(); + foreach ($aRoles[$key]['groups'] as $iGroupId) { + $oGroup = Group::get($iGroupId); + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { + $_groups[] = $oGroup->getName(); + } + } + if (empty($_groups)) { + $aRoles[$key]['groups'] = ' ' . _('no groups') . ''; + } else { + $aRoles[$key]['groups'] = join(', ',$_groups); + } + } + + $aTemplateData = array( + 'context' => &$this, + 'roles' => $aRoles, + ); + return $oTemplate->render($aTemplateData); + } + + + + function do_overrideParent() { + $role_id = KTUtil::arrayGet($_REQUEST, 'role_id', null); + $oRole = Role::get($role_id); + if (PEAR::isError($oRole)) { + $this->errorRedirectToMain(_('Invalid Role.')); + } + // FIXME do we need to check that this role _isn't_ allocated? + $oRoleAllocation = new RoleAllocation(); + $oRoleAllocation->setFolderId($this->oFolder->getId()); + $oRoleAllocation->setRoleId($role_id); + + // create a new permission descriptor. + // FIXME we really want to duplicate the original (if it exists) + + $aAllowed = array(); // no-op, for now. + $this->startTransaction(); + + $oRoleAllocation->setAllowed($aAllowed); + $res = $oRoleAllocation->create(); + + if (PEAR::isError($res) || ($res == false)) { + $this->errorRedirectToMain(_('Failed to create the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); + } + + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); + + $this->successRedirectToMain(_('Role allocation created.'), sprintf('fFolderId=%d', $this->oFolder->getId())); + } + + function do_useParent() { + $role_id = KTUtil::arrayGet($_REQUEST, 'role_id', null); + $oRole = Role::get($role_id); + if (PEAR::isError($oRole)) { + $this->errorRedirectToMain(_('Invalid Role.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + $role_id = $oRole->getId(); // numeric, for various testing purposes. + + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($this->oFolder->getId(), $role_id); + + if ($oRoleAllocation->getFolderId() != $this->oFolder->getId()) { + $this->errorRedirectToMain(_('Already using a different descriptor.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + $this->startTransaction(); + + $res = $oRoleAllocation->delete(); + + if (PEAR::isError($res) || ($res == false)) { + $this->errorRedirectToMain(_('Unable to change role allocation.') . print_r($res, true), sprintf('fFolderId=%d',$this->oFolder->getId())); + exit(0); + } + + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); + $this->successRedirectToMain(_('Role now uses parent.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + + function do_editRoleUsers() { + + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'alloc_id'); + $oRoleAllocation = RoleAllocation::get($role_allocation_id); + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + + + $this->oPage->setBreadcrumbDetails(_('Manage Users for Role')); + $this->oPage->setTitle(sprintf(_('Manage Users for Role'))); + + $initJS = 'var optGroup = new OptionTransfer("userSelect","chosenUsers"); ' . + 'function startTrans() { var f = getElement("userroleform"); ' . + ' optGroup.saveNewRightOptions("userFinal"); ' . + ' optGroup.init(f); }; ' . + ' addLoadEvent(startTrans); '; + $this->oPage->requireJSStandalone($initJS); + + $aInitialUsers = $oRoleAllocation->getUsers(); + $aAllUsers = User::getList(); + + + // FIXME this is massively non-performant for large userbases.. + $aRoleUsers = array(); + $aFreeUsers = array(); + foreach ($aInitialUsers as $oUser) { + $aRoleUsers[$oUser->getId()] = $oUser; + } + foreach ($aAllUsers as $oUser) { + if (!array_key_exists($oUser->getId(), $aRoleUsers)) { + $aFreeUsers[$oUser->getId()] = $oUser; + } + } + + $oTemplating = new KTTemplating; + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles_manageusers"); + $aTemplateData = array( + "context" => $this, + "edit_rolealloc" => $oRoleAllocation, + 'unused_users' => $aFreeUsers, + 'role_users' => $aRoleUsers, + ); + return $oTemplate->render($aTemplateData); + } + + function do_editRoleGroups() { + + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'alloc_id'); + $oRoleAllocation = RoleAllocation::get($role_allocation_id); + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + + + $this->oPage->setBreadcrumbDetails(_('Manage Groups for Role')); + $this->oPage->setTitle(sprintf(_('Manage Groups for Role'))); + + $initJS = 'var optGroup = new OptionTransfer("groupSelect","chosenGroups"); ' . + 'function startTrans() { var f = getElement("grouproleform"); ' . + ' optGroup.saveNewRightOptions("groupFinal"); ' . + ' optGroup.init(f); }; ' . + ' addLoadEvent(startTrans); '; + $this->oPage->requireJSStandalone($initJS); + + $aInitialUsers = $oRoleAllocation->getGroups(); + $aAllUsers = Group::getList(); + + + // FIXME this is massively non-performant for large userbases.. + $aRoleUsers = array(); + $aFreeUsers = array(); + foreach ($aInitialUsers as $oGroup) { + $aRoleUsers[$oGroup->getId()] = $oGroup; + } + foreach ($aAllUsers as $oGroup) { + if (!array_key_exists($oGroup->getId(), $aRoleUsers)) { + $aFreeUsers[$oGroup->getId()] = $oGroup; + } + } + + $oTemplating = new KTTemplating; + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles_managegroups"); + $aTemplateData = array( + "context" => $this, + "edit_rolealloc" => $oRoleAllocation, + 'unused_groups' => $aFreeUsers, + 'role_groups' => $aRoleUsers, + ); + return $oTemplate->render($aTemplateData); + } + + function do_setRoleUsers() { + + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'allocation_id'); + $oRoleAllocation = RoleAllocation::get($role_allocation_id); + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + $users = KTUtil::arrayGet($_REQUEST, 'userFinal', ''); + $aUserIds = explode(',', $users); + + // check that its not corrupt.. + $aFinalUserIds = array(); + foreach ($aUserIds as $iUserId) { + $oUser =& User::get($iUserId); + if (!(PEAR::isError($oUser) || ($oUser == false))) { + $aFinalUserIds[] = $iUserId; + } + } + if (empty($aFinalUserIds)) { $aFinalUserIds = null; } + + // hack straight in. + $oPD = $oRoleAllocation->getPermissionDescriptor(); + $aAllowed = $oPD->getAllowed(); + + + + // now, grab the existing allowed and modify. + + $aAllowed['user'] = $aFinalUserIds; + + $oRoleAllocation->setAllowed($aAllowed); + $res = $oRoleAllocation->update(); + + if (PEAR::isError($res) || ($res == false)) { + $this->errorRedirectToMain(_('Failed to change the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); + } + + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); + + $this->successRedirectToMain(_('Allocation changed.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + + function do_setRoleGroups() { + + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'allocation_id'); + $oRoleAllocation = RoleAllocation::get($role_allocation_id); + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + $groups = KTUtil::arrayGet($_REQUEST, 'groupFinal', ''); + $aGroupIds = explode(',', $groups); + + // check that its not corrupt.. + $aFinalGroupIds = array(); + foreach ($aGroupIds as $iGroupId) { + $oGroup =& Group::get($iGroupId); + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { + $aFinalGroupIds[] = $iGroupId; + } + } + if (empty($aFinalGroupIds)) { $aFinalGroupIds = null; } + + // hack straight in. + $oPD = $oRoleAllocation->getPermissionDescriptor(); + $aAllowed = $oPD->getAllowed(); + + + + // now, grab the existing allowed and modify. + + $aAllowed['group'] = $aFinalGroupIds; + + $oRoleAllocation->setAllowed($aAllowed); + $res = $oRoleAllocation->update(); + + if (PEAR::isError($res) || ($res == false)) { + $this->errorRedirectToMain(_('Failed to change the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); + } + + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); + + $this->successRedirectToMain(_('Allocation changed.'), sprintf('fFolderId=%d',$this->oFolder->getId())); + } + + function renegeratePermissionsForRole($iRoleId) { + $iStartFolderId = $this->oFolder->getId(); + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($iStartFolderId, $iRoleId); + /* + * 1. find all folders & documents "below" this one which use the role + * definition _active_ (not necessarily present) at this point. + * 2. tell permissionutil to regen their permissions. + * + * The find algorithm is: + * + * folder_queue <- (iStartFolderId) + * while folder_queue is not empty: + * active_folder = + * for each folder in the active_folder: + * find folders in _this_ folder without a role-allocation on the iRoleId + * add them to the folder_queue + * update the folder's permissions. + * find documents in this folder: + * update their permissions. + */ + + $sQuery = 'SELECT f.id as `id` FROM ' . Folder::_table() . ' AS f LEFT JOIN ' . RoleAllocation::_table() . ' AS ra ON (f.id = ra.folder_id) WHERE f.parent_id = ? AND ra.role_id '; + if ($oRoleAllocation == null) { // no alloc. + $sQuery .= ' IS NULL '; + $hasId = false; + } else { + $sQuery .= ' = ? '; + $aId = $oRoleAllocation->getId(); + $hasId = true; + } + + + $folder_queue = array($iStartFolderId); + while (!empty($folder_queue)) { + $active_folder = array_pop($folder_queue); + + + $aParams = array($active_folder); + if ($hasId) { $aParams[] = $aId; } + + + $aNewFolders = DBUtil::getResultArrayKey(array($sQuery, $aParams), 'id'); + if (PEAR::isError($aNewFolders)) { + $this->errorRedirectToMain(_('Failure to generate folderlisting.')); + } + $folder_queue = array_merge ($folder_queue, $aNewFolders); // push. + + + // update the folder. + $oFolder =& Folder::get($active_folder); + if (PEAR::isError($oFolder) || ($oFolder == false)) { + $this->errorRedirectToMain(_('Unable to locate folder: ') . $active_folder); + } + + KTPermissionUtil::updatePermissionLookup($oFolder); + $aDocList =& Document::getList(array('folder_id = ?', $active_folder)); + if (PEAR::isError($aDocList) || ($aDocList == false)) { + $this->errorRedirectToMain(_('Unable to get documents in folder: ') . $active_folder); + } + + foreach ($aDocList as $oDoc) { + KTPermissionUtil::updatePermissionLookup($oDoc); + } + } + } +} +$oPlugin->registerAction('folderaction', 'KTRoleAllocationPlugin', 'ktcore.actions.folder.roles'); diff --git a/presentation/lookAndFeel/knowledgeTree/browse.php b/presentation/lookAndFeel/knowledgeTree/browse.php index 362e50b..88954af 100644 --- a/presentation/lookAndFeel/knowledgeTree/browse.php +++ b/presentation/lookAndFeel/knowledgeTree/browse.php @@ -46,7 +46,7 @@ $sectionName = "browse"; class BrowseDispatcher extends KTStandardDispatcher { - var $oFolder = null; + var $oFolder = null; var $sSection = "browse"; var $browse_mode = null; var $query = null; @@ -79,12 +79,12 @@ class BrowseDispatcher extends KTStandardDispatcher { // here we need the folder object to do the breadcrumbs. $oFolder =& Folder::get($folder_id); + $this->oFolder =& $oFolder; if (PEAR::isError($oFolder)) { $this->oPage->addError(_("invalid folder")); $folder_id = 1; $oFolder =& Folder::get($folder_id); } - $this->oFolder =& $oFolder; // we now have a folder, and need to create the query. $this->oQuery = new BrowseQuery($oFolder->getId()); diff --git a/resources/css/kt-framing.css b/resources/css/kt-framing.css index ece3376..d2bac2e 100644 --- a/resources/css/kt-framing.css +++ b/resources/css/kt-framing.css @@ -622,6 +622,7 @@ The text will be hidden for screen view. The generic fahrner-ish approach comes .listing td { padding: 0.5em; + vertical-align: top; } .listing thead th { diff --git a/sql/mysql/install/structure.sql b/sql/mysql/install/structure.sql index 3aefe64..9b67e0e 100644 --- a/sql/mysql/install/structure.sql +++ b/sql/mysql/install/structure.sql @@ -1008,9 +1008,6 @@ CREATE TABLE `permissions` ( CREATE TABLE `roles` ( `id` int(11) NOT NULL default '0', `name` char(255) NOT NULL default '', - `active` tinyint(1) NOT NULL default '0', - `can_read` tinyint(1) NOT NULL default '0', - `can_write` tinyint(1) NOT NULL default '0', UNIQUE KEY `id` (`id`), UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB ; @@ -1018,6 +1015,22 @@ CREATE TABLE `roles` ( -- -------------------------------------------------------- -- +-- Table structure for table `role_allocations` +-- + +CREATE TABLE `role_allocations` ( + `id` int(11) NOT NULL default '0', + `folder_id` int(11) NOT NULL default '0', + `role_id` int(11) NOT NULL default '0', + `permission_descriptor_id` int(11) NOT NULL default '0', + UNIQUE KEY `id` (`id`), + KEY `folder_id` (`folder_id`) +) ENGINE=InnoDB ; + +-- -------------------------------------------------------- + + +-- -- Table structure for table `saved_searches` -- @@ -1916,6 +1929,19 @@ CREATE TABLE `zseq_roles` ( -- -------------------------------------------------------- + +-- +-- Table structure for table `zseq_role_allocations` +-- + +CREATE TABLE `zseq_role_allocations` ( + `id` int(10) unsigned NOT NULL auto_increment, + PRIMARY KEY (`id`) +) ENGINE=MyISAM ; + +-- -------------------------------------------------------- + + -- -- Table structure for table `zseq_saved_searches` -- diff --git a/sql/mysql/upgrade/2.99.5/role_allocations.sql b/sql/mysql/upgrade/2.99.5/role_allocations.sql new file mode 100644 index 0000000..36a6caf --- /dev/null +++ b/sql/mysql/upgrade/2.99.5/role_allocations.sql @@ -0,0 +1,18 @@ +SET FOREIGN_KEY_CHECKS=0; + +CREATE TABLE `role_allocations` ( + `id` int(11) NOT NULL default '0', + `folder_id` int(11) NOT NULL default '0', + `role_id` int(11) NOT NULL default '0', + `permission_descriptor_id` int(11) NOT NULL default '0', + UNIQUE KEY `id` (`id`), + KEY `folder_id` (`folder_id`) +) ENGINE=InnoDB ; + +CREATE TABLE `zseq_role_allocations` ( + `id` int(10) unsigned NOT NULL auto_increment, + PRIMARY KEY (`id`) +) ENGINE=MyISAM ; + + +SET FOREIGN_KEY_CHECKS=1; diff --git a/sql/mysql/upgrade/2.99.5/role_changes.sql b/sql/mysql/upgrade/2.99.5/role_changes.sql new file mode 100644 index 0000000..5881d17 --- /dev/null +++ b/sql/mysql/upgrade/2.99.5/role_changes.sql @@ -0,0 +1,7 @@ +SET FOREIGN_KEY_CHECKS=0; + +ALTER TABLE `roles` DROP `active`; +ALTER TABLE `roles` DROP `can_read`; +ALTER TABLE `roles` DROP `can_write`; + +SET FOREIGN_KEY_CHECKS=1; diff --git a/templates/ktcore/folder/roles.smarty b/templates/ktcore/folder/roles.smarty new file mode 100644 index 0000000..dc292b6 --- /dev/null +++ b/templates/ktcore/folder/roles.smarty @@ -0,0 +1,47 @@ +
{i18n} + In many cases, workflow actions will be assigned to certain roles + (e.g. Manager, Interviewer, Researcher, Journalist). You can assign these roles + to specific groups in particular areas of the document management system. +{/i18n}
+ +{i18n}Warning:{/i18n}{i18n} Please note that changing +role allocations can take very long time, depending on the number of folders below this one.{/i18n}
| {i18n}Role{/i18n} | +{i18n}Allocated users{/i18n} | +{i18n}Edit Users{/i18n} | +{i18n}Edit Groups{/i18n} | +{i18n}Use Parent{/i18n} | +|||
|---|---|---|---|---|---|---|---|
| {$aRole.name} | +
+ {if ($aRole.allocation_id === null)}
+ {i18n}inherited from parent folder.{/i18n} + + {/if} + {if ($aRole.users != null)}{i18n}Users:{/i18n} {$aRole.users} {/if} + {if ($aRole.groups != null)}{i18n}Groups:{/i18n} {$aRole.groups}{/if} + {if ($aRole.allocation_id === null)} + + {/if} + |
+ {if ($aRole.allocation_id === null)}
+ {i18n}Override Parent Allocation{/i18n} | + {else} +Edit | +Edit | +{i18n}Use parent's allocation{/i18n} | + {/if} +||
FIXME help text for role-editing.
+ + diff --git a/templates/ktcore/folder/roles_manageusers.smarty b/templates/ktcore/folder/roles_manageusers.smarty new file mode 100644 index 0000000..61413f9 --- /dev/null +++ b/templates/ktcore/folder/roles_manageusers.smarty @@ -0,0 +1,68 @@ +FIXME help text for role-editing.
+ +