Commit cc6510fec12e535219586ce16d9cd05748fd4535
1 parent
3189d37e
- add role-allocations
- ensure that workflow and permission respect role-based allocations. git-svn-id: https://kt-dms.svn.sourceforge.net/svnroot/kt-dms/trunk@4454 c91229c3-7414-0410-bfa2-8a42b809f60b
Showing
14 changed files
with
904 additions
and
151 deletions
config/tableMappings.inc
| @@ -142,4 +142,5 @@ $default->permission_dynamic_assignments_table = 'permission_dynamic_assignments | @@ -142,4 +142,5 @@ $default->permission_dynamic_assignments_table = 'permission_dynamic_assignments | ||
| 142 | $default->notifications_table = "notifications"; | 142 | $default->notifications_table = "notifications"; |
| 143 | $default->authentication_sources_table = "authentication_sources"; | 143 | $default->authentication_sources_table = "authentication_sources"; |
| 144 | $default->dashlet_disable_table = "dashlet_disables"; | 144 | $default->dashlet_disable_table = "dashlet_disables"; |
| 145 | +$default->role_allocations_table = "role_allocations"; | ||
| 145 | ?> | 146 | ?> |
lib/permissions/permissionutil.inc.php
| @@ -10,6 +10,7 @@ require_once(KT_LIB_DIR . "/permissions/permissionlookupassignment.inc.php"); | @@ -10,6 +10,7 @@ require_once(KT_LIB_DIR . "/permissions/permissionlookupassignment.inc.php"); | ||
| 10 | require_once(KT_LIB_DIR . "/permissions/permissionobject.inc.php"); | 10 | require_once(KT_LIB_DIR . "/permissions/permissionobject.inc.php"); |
| 11 | require_once(KT_LIB_DIR . "/permissions/permissiondynamiccondition.inc.php"); | 11 | require_once(KT_LIB_DIR . "/permissions/permissiondynamiccondition.inc.php"); |
| 12 | require_once(KT_LIB_DIR . "/groups/GroupUtil.php"); | 12 | require_once(KT_LIB_DIR . "/groups/GroupUtil.php"); |
| 13 | +require_once(KT_LIB_DIR . "/roles/roleallocation.inc.php"); | ||
| 13 | 14 | ||
| 14 | class KTPermissionUtil { | 15 | class KTPermissionUtil { |
| 15 | // {{{ generateDescriptor | 16 | // {{{ generateDescriptor |
| @@ -182,9 +183,11 @@ class KTPermissionUtil { | @@ -182,9 +183,11 @@ class KTPermissionUtil { | ||
| 182 | $oPD = KTPermissionDescriptor::get($oPA->getPermissionDescriptorID()); | 183 | $oPD = KTPermissionDescriptor::get($oPA->getPermissionDescriptorID()); |
| 183 | $aGroupIDs = $oPD->getGroups(); | 184 | $aGroupIDs = $oPD->getGroups(); |
| 184 | $aUserIDs = array(); | 185 | $aUserIDs = array(); |
| 186 | + $aRoleIDs = $oPD->getRoles(); | ||
| 185 | $aAllowed = array( | 187 | $aAllowed = array( |
| 186 | "group" => $aGroupIDs, | 188 | "group" => $aGroupIDs, |
| 187 | "user" => $aUserIDs, | 189 | "user" => $aUserIDs, |
| 190 | + "role" => $aRoleIDs, | ||
| 188 | ); | 191 | ); |
| 189 | $aMapPermAllowed[$oPA->getPermissionID()] = $aAllowed; | 192 | $aMapPermAllowed[$oPA->getPermissionID()] = $aAllowed; |
| 190 | } | 193 | } |
| @@ -207,6 +210,31 @@ class KTPermissionUtil { | @@ -207,6 +210,31 @@ class KTPermissionUtil { | ||
| 207 | } | 210 | } |
| 208 | } | 211 | } |
| 209 | 212 | ||
| 213 | + // if we have roles: nearest folder. | ||
| 214 | + $iRoleSourceFolder = null; | ||
| 215 | + if (is_a($oFolderOrDocument, 'Document')) { $iRoleSourceFolder = $oFolderOrDocument->getFolderID(); } | ||
| 216 | + else { $iRoleSourceFolder = $oFolderOrDocument->getId(); } | ||
| 217 | + | ||
| 218 | + // very minor perf win: map role_id (in context) to PD. | ||
| 219 | + $_roleCache = array(); | ||
| 220 | + | ||
| 221 | + foreach ($aMapPermAllowed as $iPermissionId => $aAllowed) { | ||
| 222 | + if (array_key_exists('role', $aAllowed)) { | ||
| 223 | + foreach ($aAllowed['role'] as $iRoleId) { | ||
| 224 | + // store the PD <-> RoleId map | ||
| 225 | + if (!array_key_exists($iRoleId, $_roleCache)) { | ||
| 226 | + $oRoleAllocation =& RoleAllocation::getAllocationsForFolderAndRole($iRoleSourceFolder, $iRoleId); | ||
| 227 | + $_roleCache[$iRoleId] = $oRoleAllocation; | ||
| 228 | + } | ||
| 229 | + // roles are _not_ always assigned (can be null at root) | ||
| 230 | + if ($_roleCache[$iRoleId] != null) { | ||
| 231 | + $aAllowed['user'] = array_merge($aAllowed['user'], $_roleCache[$iRoleId]->getUsers()); | ||
| 232 | + $aAllowed['group'] = array_merge($aAllowed['group'], $_roleCache[$iRoleId]->getGroups()); | ||
| 233 | + } | ||
| 234 | + } | ||
| 235 | + } | ||
| 236 | + } | ||
| 237 | + | ||
| 210 | $aMapPermDesc = array(); | 238 | $aMapPermDesc = array(); |
| 211 | foreach ($aMapPermAllowed as $iPermissionId => $aAllowed) { | 239 | foreach ($aMapPermAllowed as $iPermissionId => $aAllowed) { |
| 212 | $oLookupPD = KTPermissionUtil::getOrCreateDescriptor($aAllowed); | 240 | $oLookupPD = KTPermissionUtil::getOrCreateDescriptor($aAllowed); |
lib/roles/Role.inc
| @@ -21,170 +21,43 @@ | @@ -21,170 +21,43 @@ | ||
| 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 22 | * | 22 | * |
| 23 | * @version $Revision$ | 23 | * @version $Revision$ |
| 24 | - * @author Rob Cherry, Jam Warehouse (Pty) Ltd, South Africa | 24 | + * @author Brad Shuttleworth, Jam Warehouse (Pty) Ltd, South Africa |
| 25 | * @package lib.roles | 25 | * @package lib.roles |
| 26 | */ | 26 | */ |
| 27 | + | ||
| 28 | +require_once(KT_LIB_DIR . "/ktentity.inc"); | ||
| 29 | +require_once(KT_LIB_DIR . "/util/ktutil.inc"); | ||
| 30 | + | ||
| 27 | class Role extends KTEntity { | 31 | class Role extends KTEntity { |
| 28 | 32 | ||
| 29 | /** role object primary key */ | 33 | /** role object primary key */ |
| 30 | var $iId; | 34 | var $iId; |
| 31 | - /** role name */ | ||
| 32 | var $sName; | 35 | var $sName; |
| 33 | - /** is an active user*/ | ||
| 34 | - var $bActive; | ||
| 35 | - /** role has document read permission */ | ||
| 36 | - var $bCanRead; | ||
| 37 | - /** role has document write */ | ||
| 38 | - var $bCanWrite; | ||
| 39 | 36 | ||
| 37 | + var $_aFieldToSelect = array( | ||
| 38 | + 'iId' => 'id', | ||
| 39 | + 'sName' => 'name', | ||
| 40 | + ); | ||
| 40 | 41 | ||
| 41 | - | ||
| 42 | - /** | ||
| 43 | - * Default constructor | ||
| 44 | - * | ||
| 45 | - * @param String Role name | ||
| 46 | - * @param boolean Role has document read permission | ||
| 47 | - * @param boolean Role has document write permission | ||
| 48 | - * | ||
| 49 | - */ | ||
| 50 | - function Role($sNewName = null, $bNewCanRead = false, $bNewCanWrite = false) { | 42 | + function Role($sNewName = null, $x=null, $y=null) { |
| 51 | //object not yet created in database | 43 | //object not yet created in database |
| 52 | $this->iId = -1; | 44 | $this->iId = -1; |
| 53 | $this->sName = $sNewName; | 45 | $this->sName = $sNewName; |
| 54 | - $this->bCanRead = $bNewCanRead; | ||
| 55 | - $this->bCanWrite = $bNewCanWrite; | ||
| 56 | - } | ||
| 57 | - | ||
| 58 | - function getName() { | ||
| 59 | - return $this->sName; | ||
| 60 | - } | ||
| 61 | - | ||
| 62 | - function getID() { | ||
| 63 | - return $this->iId; | ||
| 64 | - } | ||
| 65 | - | ||
| 66 | - function getActive() { | ||
| 67 | - return $this->bActive; | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - function setActive($bNewValue) { | ||
| 71 | - $this->bActive = $bNewValue; | ||
| 72 | - } | ||
| 73 | - | ||
| 74 | - function getReadable() { | ||
| 75 | - return $this->bCanRead; | ||
| 76 | - } | ||
| 77 | - | ||
| 78 | - function setReadable($bNewValue) { | ||
| 79 | - $this->bCanRead = $bNewValue; | ||
| 80 | - } | ||
| 81 | - | ||
| 82 | - function getWriteable() { | ||
| 83 | - return $this->bCanWrite; | ||
| 84 | - } | ||
| 85 | - | ||
| 86 | - function setWriteable($bNewValue) { | ||
| 87 | - $this->bCanWrite = $bNewValue; | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - function setName($bNewValue) { | ||
| 91 | - $this->sName = $bNewValue; | ||
| 92 | } | 46 | } |
| 93 | 47 | ||
| 94 | - function _fieldValues () { | ||
| 95 | - return array( | 48 | + function getName() { return $this->sName; } |
| 49 | + function setName($sNewValue) { $this->sName = $sNewValue; } | ||
| 50 | + | ||
| 51 | + function _fieldValues () { return array( | ||
| 52 | + 'id' => $this->iId, | ||
| 96 | 'name' => $this->sName, | 53 | 'name' => $this->sName, |
| 97 | - 'active' => KTUtil::anyToBool($this->bActive), | ||
| 98 | - 'can_read' => KTUtil::anyToBool($this->bCanRead), | ||
| 99 | - 'can_write' => KTUtil::anyToBool($this->bCanWrite), | ||
| 100 | ); | 54 | ); |
| 101 | } | 55 | } |
| 102 | 56 | ||
| 103 | - function _table () { | ||
| 104 | - global $default; | ||
| 105 | - return $default->roles_table; | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - /** | ||
| 109 | - * Delete the current object from the database | ||
| 110 | - * | ||
| 111 | - * @return boolean true on successful deletion, false otherwise and set $_SESSION["errorMessage"] | ||
| 112 | - * | ||
| 113 | - */ | ||
| 114 | - function delete() { | ||
| 115 | - global $default, $lang_err_database, $lang_err_object_key; | ||
| 116 | - //only delete the object if it exists in the database | ||
| 117 | - /* | ||
| 118 | - if ($this->iId >= 0) { | ||
| 119 | - | ||
| 120 | - $sql = $default->db; | ||
| 121 | - $query = "SELECT role_id FROM ". $default->groups_folders_approval_table ." WHERE role_id = ?"; | ||
| 122 | - $aParams = array($this->iId); | ||
| 123 | - $sql->query(array($query, $aParams)); | ||
| 124 | - $rows = $sql->num_rows($sql); | ||
| 125 | - | ||
| 126 | - if ($rows > 0){ | ||
| 127 | - $_SESSION["errorMessage"] = "Role::The Role " . $this->sName . " is Assigned to a folder!"; | ||
| 128 | - return false; | ||
| 129 | - } | ||
| 130 | - } | ||
| 131 | - */ | ||
| 132 | - return parent::delete(); | ||
| 133 | - } | ||
| 134 | - | ||
| 135 | - /** | ||
| 136 | - * Static function. | ||
| 137 | - * Given a roles primary key it will create a | ||
| 138 | - * Roles object and populate it with the | ||
| 139 | - * corresponding database values | ||
| 140 | - * | ||
| 141 | - * @return Role populated Role object on successful query, false otherwise and set $_SESSION["errorMessage"] | ||
| 142 | - */ | ||
| 143 | - function & get($iRoleID) { | ||
| 144 | - global $default; | ||
| 145 | - $sql = $default->db; | ||
| 146 | - $result = $sql->query(array("SELECT * FROM $default->roles_table WHERE id = ?", $iRoleID));/*ok*/ | ||
| 147 | - if ($result) { | ||
| 148 | - if ($sql->next_record()) { | ||
| 149 | - $oRole = & new Role($sql->f("name"), $sql->f("can_read"), $sql->f("can_write")); | ||
| 150 | - $oRole->iId = $iRoleID; | ||
| 151 | - $oRole->bActive = $sql->f("active"); | ||
| 152 | - return $oRole; | ||
| 153 | - } | ||
| 154 | - $_SESSION["errorMessage"] = $lang_err_object_not_exist . "id = " . $iRoleID . " table = $default->roles_table"; | ||
| 155 | - return false; | ||
| 156 | - } | ||
| 157 | - $_SESSION["errorMessage"] = $lang_err_database; | ||
| 158 | - return false; | ||
| 159 | - } | ||
| 160 | - | ||
| 161 | -/** | ||
| 162 | - * Static function | ||
| 163 | - * Get a list of web documents | ||
| 164 | - * | ||
| 165 | - * @param String Where clause (not required) | ||
| 166 | - * | ||
| 167 | - * @return Array array of Role objects, false otherwise and set $_SESSION["errorMessage"] | ||
| 168 | - */ | ||
| 169 | - function getList($sWhereClause = null) { | ||
| 170 | - return KTEntityUtil::getList(Role::_table(), 'Role', $sWhereClause); | ||
| 171 | - } | ||
| 172 | -} | ||
| 173 | - | ||
| 174 | - | ||
| 175 | - | ||
| 176 | -/** | ||
| 177 | -* Static function | ||
| 178 | -* | ||
| 179 | -* Creates a roles object from an array | ||
| 180 | -* | ||
| 181 | -* @param Array Array of parameters. Must match order of parameters in constructor | ||
| 182 | -* | ||
| 183 | -* @return User user object | ||
| 184 | -*/ | ||
| 185 | -function & roleCreateFromArray($aParameters) { | ||
| 186 | - $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]); | ||
| 187 | - return $oRole; | 57 | + function _table () { return KTUtil::getTableName('roles'); } |
| 58 | + function & get($iRoleId) { return KTEntityUtil::get('Role', $iRoleId); } | ||
| 59 | + function & getList($sWhereClause = null) { return KTEntityUtil::getList2('Role', $sWhereClause); } | ||
| 60 | + function & createFromArray($aOptions) { return KTEntityUtil::createFromArray('Role', $aOptions); } | ||
| 188 | } | 61 | } |
| 189 | 62 | ||
| 190 | ?> | 63 | ?> |
lib/roles/roleallocation.inc.php
0 → 100644
| 1 | +<?php | ||
| 2 | + | ||
| 3 | +require_once(KT_LIB_DIR . "/ktentity.inc"); | ||
| 4 | +require_once(KT_LIB_DIR . "/util/ktutil.inc"); | ||
| 5 | +require_once(KT_LIB_DIR . "/database/dbutil.inc"); | ||
| 6 | + | ||
| 7 | +require_once(KT_LIB_DIR . "/permissions/permissiondescriptor.inc.php"); | ||
| 8 | +require_once(KT_LIB_DIR . "/permissions/permissionutil.inc.php"); | ||
| 9 | +require_once(KT_LIB_DIR . "/users/User.inc"); | ||
| 10 | +require_once(KT_LIB_DIR . "/groups/Group.inc"); | ||
| 11 | +require_once(KT_LIB_DIR . "/foldermanagement/Folder.inc"); | ||
| 12 | + | ||
| 13 | +class RoleAllocation extends KTEntity { | ||
| 14 | + | ||
| 15 | + /** role object primary key */ | ||
| 16 | + var $iId=-1; | ||
| 17 | + var $iFolderId; | ||
| 18 | + var $iRoleId; | ||
| 19 | + var $iPermissionDescriptorId; | ||
| 20 | + | ||
| 21 | + var $_bUsePearError = true; | ||
| 22 | + | ||
| 23 | + var $_aFieldToSelect = array( | ||
| 24 | + 'iId' => 'id', | ||
| 25 | + 'iRoleId' => 'role_id', | ||
| 26 | + 'iFolderId' => 'folder_id', | ||
| 27 | + 'iPermissionDescriptorId' => 'permission_descriptor_id', | ||
| 28 | + ); | ||
| 29 | + | ||
| 30 | + function setFolderId($iFolderId) { $this->iFolderId = $iFolderId; } | ||
| 31 | + function setRoleId($iRoleId) { $this->iRoleId = $iRoleId; } | ||
| 32 | + function setPermissionDescriptorId($iPermissionDescriptorId) { $this->iPermissionDescriptorId = $iPermissionDescriptorId; } | ||
| 33 | + function getFolderId() { return $this->iFolderId; } | ||
| 34 | + function getRoleId() { return $this->iRoleId; } | ||
| 35 | + function getPermissionDescriptorId() { return $this->iPermissionDescriptorId; } | ||
| 36 | + | ||
| 37 | + // aggregate: set (for this alloc) the array('user' => array(), 'group' => array()). | ||
| 38 | + function setAllowed($aAllowed) { | ||
| 39 | + $oDescriptor = KTPermissionUtil::getOrCreateDescriptor($aAllowed); // fully done, etc. | ||
| 40 | + $this->iPermissionDescriptorId = $oDescriptor->getId(); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + function _fieldValues () { return array( | ||
| 44 | + 'role_id' => $this->iRoleId, | ||
| 45 | + 'folder_id' => $this->iFolderId, | ||
| 46 | + 'permission_descriptor_id' => $this->iPermissionDescriptorId, | ||
| 47 | + ); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + /* getAllocationForFolderAndRole($iFolderId, $iRoleId) | ||
| 51 | + * | ||
| 52 | + * this is the key function: for a given folder and role, | ||
| 53 | + * returns either a RoleAllocation object, or null | ||
| 54 | + * (if there is none). It scans _up_ the hierachy of folders, | ||
| 55 | + * trying to find the nearest such object with a folder_id | ||
| 56 | + * in the mapping. | ||
| 57 | + */ | ||
| 58 | + function & getAllocationsForFolderAndRole($iFolderId, $iRoleId) { | ||
| 59 | + // FIXME the query we use here is ... not very pleasant. | ||
| 60 | + // NBM: is this the "right" way to do this? | ||
| 61 | + $raTable = KTUtil::getTableName('role_allocations'); | ||
| 62 | + | ||
| 63 | + $fTable = Folder::_table(); | ||
| 64 | + | ||
| 65 | + $oFolder =& Folder::get($iFolderId); | ||
| 66 | + $parents = $oFolder->getParentFolderIDs() . ',' . $iFolderId; // this is formatted as 1,2,3,4,5,6 - perfect for "WHERE". | ||
| 67 | + | ||
| 68 | + // FIXME what (if anything) do we need to do to check that this can't be used as an attack? | ||
| 69 | + $folders = '(' . $parents . ')'; | ||
| 70 | + | ||
| 71 | + $sQuery = "SELECT ra.id as `id` FROM " . $raTable . " AS ra " . | ||
| 72 | + ' LEFT JOIN ' . $fTable . ' AS f ON (f.id = ra.folder_id) ' . | ||
| 73 | + ' WHERE f.id IN ' . $folders . | ||
| 74 | + ' ORDER BY CHAR_LENGTH(f.parent_folder_ids) desc, f.parent_folder_ids DESC; '; | ||
| 75 | + $aParams = array(); | ||
| 76 | + | ||
| 77 | + $aRoleAllocIds = DBUtil::getResultArrayKey(array($sQuery, $aParams), 'id'); | ||
| 78 | + | ||
| 79 | + if (false) { | ||
| 80 | + print '<pre>'; | ||
| 81 | + var_dump($aRoleAllocIds); | ||
| 82 | + print ''; | ||
| 83 | + print $sQuery; | ||
| 84 | + print '</pre>'; | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + if (empty($aRoleAllocIds)) { | ||
| 88 | + return null; | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + $iAllocId = $aRoleAllocIds[0]; // array pop? | ||
| 92 | + return RoleAllocation::get($iAllocId); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + // static, boilerplate. | ||
| 96 | + function _table () { return KTUtil::getTableName('role_allocations'); } | ||
| 97 | + function & get($iRoleId) { return KTEntityUtil::get('RoleAllocation', $iRoleId); } | ||
| 98 | + function & getList($sWhereClause = null) { return KTEntityUtil::getList2('RoleAllocation', $sWhereClause); } | ||
| 99 | + function & createFromArray($aOptions) { return KTEntityUtil::createFromArray('RoleAllocation', $aOptions); } | ||
| 100 | + | ||
| 101 | + function getPermissionDescriptor() { | ||
| 102 | + // could be an error - return as-is. | ||
| 103 | + $oDescriptor =& KTPermissionDescriptor::get($this->iPermissionDescriptorId); | ||
| 104 | + return $oDescriptor; | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + // setting users and groups needs to use permissionutil::getOrCreateDescriptor | ||
| 108 | + function getUsers() { | ||
| 109 | + $oDescriptor = $this->getPermissionDescriptor(); | ||
| 110 | + $aUsers = array(); | ||
| 111 | + if (PEAR::isError($oDescriptor) || ($oDescriptor == false)) { | ||
| 112 | + return $aUsers; | ||
| 113 | + } | ||
| 114 | + $aAllowed = $oDescriptor->getAllowed(); | ||
| 115 | + if ($aAllowed['user'] !== null) { | ||
| 116 | + $aUsers = $aAllowed['user']; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + // now we want to map to oUsers, since that's what groups do. | ||
| 120 | + $aFullUsers = array(); | ||
| 121 | + foreach ($aUsers as $iUserId) { | ||
| 122 | + $oUser = User::get($iUserId); | ||
| 123 | + if (!(PEAR::isError($oUser) || ($oUser == false))) { | ||
| 124 | + $aFullUsers[] = $oUser; | ||
| 125 | + } | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + return $aFullUsers; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + function getGroups() { | ||
| 132 | + $oDescriptor = $this->getPermissionDescriptor(); | ||
| 133 | + $aGroups = array(); | ||
| 134 | + if (PEAR::isError($oDescriptor) || ($oDescriptor == false)) { | ||
| 135 | + return $aGroups; | ||
| 136 | + } | ||
| 137 | + $aAllowed = $oDescriptor->getAllowed(); | ||
| 138 | + if ($aAllowed['group'] !== null) { | ||
| 139 | + $aGroups = $aAllowed['group']; | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + // now we want to map to oUsers, since that's what groups do. | ||
| 143 | + $aFullGroups = array(); | ||
| 144 | + foreach ($aGroups as $iGroupId) { | ||
| 145 | + $oGroup = Group::get($iGroupId); | ||
| 146 | + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { | ||
| 147 | + $aFullGroups[] = $oGroup; | ||
| 148 | + } | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + return $aFullGroups; | ||
| 152 | + } | ||
| 153 | + | ||
| 154 | + // utility function to establish user membership in this allocation. | ||
| 155 | + // FIXME nbm: is there are more coherent way to do this ITO your PD infrastructure? | ||
| 156 | + function hasMember($oUser) { | ||
| 157 | + $oPD = $this->getPermissionDescriptor(); | ||
| 158 | + if (PEAR::isError($oPD) || ($oPD == false)) { | ||
| 159 | + return false; | ||
| 160 | + } | ||
| 161 | + $aAllowed = $oPD->getAllowed(); | ||
| 162 | + $iUserId = $oUser->getId(); | ||
| 163 | + | ||
| 164 | + if ($aAllowed['user'] != null) { | ||
| 165 | + if (array_search($iUserId, $aAllowed['user']) !== false) { | ||
| 166 | + return true; | ||
| 167 | + } | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + // now we need the group objects. | ||
| 171 | + // FIXME this could accelerated to a single SQL query on group_user_link. | ||
| 172 | + $aGroups = $this->getGroups(); | ||
| 173 | + if (PEAR::isError($aGroups) || ($aGroups == false)) { | ||
| 174 | + return false; | ||
| 175 | + } else { | ||
| 176 | + foreach ($aGroups as $oGroup) { | ||
| 177 | + if ($oGroup->hasMember($oUser)) { | ||
| 178 | + return true; | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + } | ||
| 182 | + | ||
| 183 | + return false; | ||
| 184 | + } | ||
| 185 | + | ||
| 186 | +} | ||
| 187 | + | ||
| 188 | +?> |
lib/workflow/workflowutil.inc.php
| @@ -8,6 +8,7 @@ require_once(KT_LIB_DIR . '/permissions/permissionutil.inc.php'); | @@ -8,6 +8,7 @@ require_once(KT_LIB_DIR . '/permissions/permissionutil.inc.php'); | ||
| 8 | require_once(KT_LIB_DIR . '/groups/GroupUtil.php'); | 8 | require_once(KT_LIB_DIR . '/groups/GroupUtil.php'); |
| 9 | require_once(KT_LIB_DIR . '/documentmanagement/DocumentTransaction.inc'); | 9 | require_once(KT_LIB_DIR . '/documentmanagement/DocumentTransaction.inc'); |
| 10 | require_once(KT_LIB_DIR . '/search/searchutil.inc.php'); | 10 | require_once(KT_LIB_DIR . '/search/searchutil.inc.php'); |
| 11 | +require_once(KT_LIB_DIR . '/roles/roleallocation.inc.php'); | ||
| 11 | 12 | ||
| 12 | class KTWorkflowUtil { | 13 | class KTWorkflowUtil { |
| 13 | // {{{ saveTransitionsFrom | 14 | // {{{ saveTransitionsFrom |
| @@ -338,6 +339,18 @@ class KTWorkflowUtil { | @@ -338,6 +339,18 @@ class KTWorkflowUtil { | ||
| 338 | continue; | 339 | continue; |
| 339 | } | 340 | } |
| 340 | } | 341 | } |
| 342 | + $iRoleId = $oTransition->getGuardRoleId(); | ||
| 343 | + if ($iRoleId) { | ||
| 344 | + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($oDocument->getFolderID(), $iRoleId); | ||
| 345 | + | ||
| 346 | + if ($oRoleAllocation == null) { // no role allocation, no fulfillment. | ||
| 347 | + continue; | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + if (!$oRoleAllocation->hasMember($oUser)) { | ||
| 351 | + continue; | ||
| 352 | + } | ||
| 353 | + } | ||
| 341 | 354 | ||
| 342 | $iConditionId = $oTransition->getGuardConditionId(); | 355 | $iConditionId = $oTransition->getGuardConditionId(); |
| 343 | if ($iConditionId) { | 356 | if ($iConditionId) { |
plugins/ktcore/KTPermissions.php
| 1 | <?php | 1 | <?php |
| 2 | 2 | ||
| 3 | +require_once(KT_LIB_DIR . '/actions/folderaction.inc.php'); | ||
| 3 | require_once(KT_LIB_DIR . '/actions/documentaction.inc.php'); | 4 | require_once(KT_LIB_DIR . '/actions/documentaction.inc.php'); |
| 4 | require_once(KT_LIB_DIR . '/widgets/fieldWidgets.php'); | 5 | require_once(KT_LIB_DIR . '/widgets/fieldWidgets.php'); |
| 5 | 6 | ||
| 6 | require_once(KT_LIB_DIR . "/foldermanagement/Folder.inc"); | 7 | require_once(KT_LIB_DIR . "/foldermanagement/Folder.inc"); |
| 7 | require_once(KT_LIB_DIR . "/groups/Group.inc"); | 8 | require_once(KT_LIB_DIR . "/groups/Group.inc"); |
| 9 | +require_once(KT_LIB_DIR . "/users/User.inc"); | ||
| 10 | +require_once(KT_LIB_DIR . "/roles/Role.inc"); | ||
| 11 | +require_once(KT_LIB_DIR . "/roles/roleallocation.inc.php"); | ||
| 12 | + | ||
| 8 | 13 | ||
| 9 | require_once(KT_LIB_DIR . "/permissions/permission.inc.php"); | 14 | require_once(KT_LIB_DIR . "/permissions/permission.inc.php"); |
| 10 | require_once(KT_LIB_DIR . "/permissions/permissionobject.inc.php"); | 15 | require_once(KT_LIB_DIR . "/permissions/permissionobject.inc.php"); |
| @@ -77,3 +82,413 @@ class KTDocumentPermissionsAction extends KTDocumentAction { | @@ -77,3 +82,413 @@ class KTDocumentPermissionsAction extends KTDocumentAction { | ||
| 77 | } | 82 | } |
| 78 | $oPlugin->registerAction('documentaction', 'KTDocumentPermissionsAction', 'ktcore.actions.document.permissions'); | 83 | $oPlugin->registerAction('documentaction', 'KTDocumentPermissionsAction', 'ktcore.actions.document.permissions'); |
| 79 | 84 | ||
| 85 | + | ||
| 86 | +class KTRoleAllocationPlugin extends KTFolderAction { | ||
| 87 | + var $sDisplayName = 'Allocate Roles'; | ||
| 88 | + var $sName = 'ktcore.actions.folder.roles'; | ||
| 89 | + | ||
| 90 | + var $_sShowPermission = "ktcore.permissions.write"; | ||
| 91 | + var $bAutomaticTransaction = true; | ||
| 92 | + | ||
| 93 | + function do_main() { | ||
| 94 | + $this->oPage->setTitle(_("Allocate Roles")); | ||
| 95 | + $this->oPage->setBreadcrumbDetails(_("Allocate Roles")); | ||
| 96 | + $oTemplating = new KTTemplating; | ||
| 97 | + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles"); | ||
| 98 | + | ||
| 99 | + // we need to have: | ||
| 100 | + // - a list of roles | ||
| 101 | + // - with their users / groups | ||
| 102 | + // - and that allocation id | ||
| 103 | + $aRoles = array(); // stores data for display. | ||
| 104 | + | ||
| 105 | + $aRoleList = Role::getList(); | ||
| 106 | + foreach ($aRoleList as $oRole) { | ||
| 107 | + $iRoleId = $oRole->getId(); | ||
| 108 | + $aRoles[$iRoleId] = array("name" => $oRole->getName()); | ||
| 109 | + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($this->oFolder->getId(), $iRoleId); | ||
| 110 | + | ||
| 111 | + $u = array(); | ||
| 112 | + $g = array(); | ||
| 113 | + $aid = null; | ||
| 114 | + $raid = null; | ||
| 115 | + if ($oRoleAllocation == null) { | ||
| 116 | + ; // nothing. | ||
| 117 | + } else { | ||
| 118 | + $raid = $oRoleAllocation->getId(); // real_alloc_id | ||
| 119 | + if ($oRoleAllocation->getFolderId() == $this->oFolder->getId()) { | ||
| 120 | + $aid = $oRoleAllocation->getid(); // alloc_id | ||
| 121 | + } | ||
| 122 | + $oPermDesc = KTPermissionDescriptor::get($oRoleAllocation->getPermissionDescriptorId()); | ||
| 123 | + if (!PEAR::isError($oPermDesc)) { | ||
| 124 | + $aAllowed = $oPermDesc->getAllowed(); | ||
| 125 | + if (!empty($aAllowed['user'])) { | ||
| 126 | + $u = $aAllowed['user']; | ||
| 127 | + } | ||
| 128 | + if (!empty($aAllowed['group'])) { | ||
| 129 | + $g = $aAllowed['group']; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + $aRoles[$iRoleId]['users'] = $u; | ||
| 134 | + $aRoles[$iRoleId]['groups'] = $g; | ||
| 135 | + $aRoles[$iRoleId]['allocation_id'] = $aid; | ||
| 136 | + $aRoles[$iRoleId]['real_allocation_id'] = $raid; | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + /* | ||
| 140 | + print '<pre>'; | ||
| 141 | + var_dump($aRoles); | ||
| 142 | + print '</pre>'; | ||
| 143 | + */ | ||
| 144 | + | ||
| 145 | + | ||
| 146 | + | ||
| 147 | + // FIXME this is test data. | ||
| 148 | + /* | ||
| 149 | + $aRoles = array( | ||
| 150 | + 1 => array('name' => 'Manager', 'users' => array(1), 'groups' => array(1), 'allocation_id' => 1), | ||
| 151 | + 2 => array('name' => 'Peasant', 'users' => array(1), 'groups' => array(), 'allocation_id' => 2), | ||
| 152 | + 3 => array('name' => 'Inherited', 'users' => array(), 'groups' => array(1), 'allocation_id' => null), | ||
| 153 | + ); | ||
| 154 | + */ | ||
| 155 | + | ||
| 156 | + | ||
| 157 | + // final step. | ||
| 158 | + | ||
| 159 | + // map to users, groups. | ||
| 160 | + foreach ($aRoles as $key => $role) { | ||
| 161 | + $_users = array(); | ||
| 162 | + foreach ($aRoles[$key]['users'] as $iUserId) { | ||
| 163 | + $oUser = User::get($iUserId); | ||
| 164 | + if (!(PEAR::isError($oUser) || ($oUser == false))) { | ||
| 165 | + $_users[] = $oUser->getName(); | ||
| 166 | + } | ||
| 167 | + } | ||
| 168 | + if (empty($_users)) { | ||
| 169 | + $aRoles[$key]['users'] = '<span class="descriptiveText"> ' . _('no users') . '</span>'; | ||
| 170 | + } else { | ||
| 171 | + $aRoles[$key]['users'] = join(', ',$_users); | ||
| 172 | + } | ||
| 173 | + | ||
| 174 | + $_groups = array(); | ||
| 175 | + foreach ($aRoles[$key]['groups'] as $iGroupId) { | ||
| 176 | + $oGroup = Group::get($iGroupId); | ||
| 177 | + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { | ||
| 178 | + $_groups[] = $oGroup->getName(); | ||
| 179 | + } | ||
| 180 | + } | ||
| 181 | + if (empty($_groups)) { | ||
| 182 | + $aRoles[$key]['groups'] = '<span class="descriptiveText"> ' . _('no groups') . '</span>'; | ||
| 183 | + } else { | ||
| 184 | + $aRoles[$key]['groups'] = join(', ',$_groups); | ||
| 185 | + } | ||
| 186 | + } | ||
| 187 | + | ||
| 188 | + $aTemplateData = array( | ||
| 189 | + 'context' => &$this, | ||
| 190 | + 'roles' => $aRoles, | ||
| 191 | + ); | ||
| 192 | + return $oTemplate->render($aTemplateData); | ||
| 193 | + } | ||
| 194 | + | ||
| 195 | + | ||
| 196 | + | ||
| 197 | + function do_overrideParent() { | ||
| 198 | + $role_id = KTUtil::arrayGet($_REQUEST, 'role_id', null); | ||
| 199 | + $oRole = Role::get($role_id); | ||
| 200 | + if (PEAR::isError($oRole)) { | ||
| 201 | + $this->errorRedirectToMain(_('Invalid Role.')); | ||
| 202 | + } | ||
| 203 | + // FIXME do we need to check that this role _isn't_ allocated? | ||
| 204 | + $oRoleAllocation = new RoleAllocation(); | ||
| 205 | + $oRoleAllocation->setFolderId($this->oFolder->getId()); | ||
| 206 | + $oRoleAllocation->setRoleId($role_id); | ||
| 207 | + | ||
| 208 | + // create a new permission descriptor. | ||
| 209 | + // FIXME we really want to duplicate the original (if it exists) | ||
| 210 | + | ||
| 211 | + $aAllowed = array(); // no-op, for now. | ||
| 212 | + $this->startTransaction(); | ||
| 213 | + | ||
| 214 | + $oRoleAllocation->setAllowed($aAllowed); | ||
| 215 | + $res = $oRoleAllocation->create(); | ||
| 216 | + | ||
| 217 | + if (PEAR::isError($res) || ($res == false)) { | ||
| 218 | + $this->errorRedirectToMain(_('Failed to create the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); | ||
| 222 | + | ||
| 223 | + $this->successRedirectToMain(_('Role allocation created.'), sprintf('fFolderId=%d', $this->oFolder->getId())); | ||
| 224 | + } | ||
| 225 | + | ||
| 226 | + function do_useParent() { | ||
| 227 | + $role_id = KTUtil::arrayGet($_REQUEST, 'role_id', null); | ||
| 228 | + $oRole = Role::get($role_id); | ||
| 229 | + if (PEAR::isError($oRole)) { | ||
| 230 | + $this->errorRedirectToMain(_('Invalid Role.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 231 | + } | ||
| 232 | + $role_id = $oRole->getId(); // numeric, for various testing purposes. | ||
| 233 | + | ||
| 234 | + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($this->oFolder->getId(), $role_id); | ||
| 235 | + | ||
| 236 | + if ($oRoleAllocation->getFolderId() != $this->oFolder->getId()) { | ||
| 237 | + $this->errorRedirectToMain(_('Already using a different descriptor.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 238 | + } | ||
| 239 | + $this->startTransaction(); | ||
| 240 | + | ||
| 241 | + $res = $oRoleAllocation->delete(); | ||
| 242 | + | ||
| 243 | + if (PEAR::isError($res) || ($res == false)) { | ||
| 244 | + $this->errorRedirectToMain(_('Unable to change role allocation.') . print_r($res, true), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 245 | + exit(0); | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); | ||
| 249 | + $this->successRedirectToMain(_('Role now uses parent.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 250 | + } | ||
| 251 | + | ||
| 252 | + function do_editRoleUsers() { | ||
| 253 | + | ||
| 254 | + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'alloc_id'); | ||
| 255 | + $oRoleAllocation = RoleAllocation::get($role_allocation_id); | ||
| 256 | + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { | ||
| 257 | + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + | ||
| 261 | + $this->oPage->setBreadcrumbDetails(_('Manage Users for Role')); | ||
| 262 | + $this->oPage->setTitle(sprintf(_('Manage Users for Role'))); | ||
| 263 | + | ||
| 264 | + $initJS = 'var optGroup = new OptionTransfer("userSelect","chosenUsers"); ' . | ||
| 265 | + 'function startTrans() { var f = getElement("userroleform"); ' . | ||
| 266 | + ' optGroup.saveNewRightOptions("userFinal"); ' . | ||
| 267 | + ' optGroup.init(f); }; ' . | ||
| 268 | + ' addLoadEvent(startTrans); '; | ||
| 269 | + $this->oPage->requireJSStandalone($initJS); | ||
| 270 | + | ||
| 271 | + $aInitialUsers = $oRoleAllocation->getUsers(); | ||
| 272 | + $aAllUsers = User::getList(); | ||
| 273 | + | ||
| 274 | + | ||
| 275 | + // FIXME this is massively non-performant for large userbases.. | ||
| 276 | + $aRoleUsers = array(); | ||
| 277 | + $aFreeUsers = array(); | ||
| 278 | + foreach ($aInitialUsers as $oUser) { | ||
| 279 | + $aRoleUsers[$oUser->getId()] = $oUser; | ||
| 280 | + } | ||
| 281 | + foreach ($aAllUsers as $oUser) { | ||
| 282 | + if (!array_key_exists($oUser->getId(), $aRoleUsers)) { | ||
| 283 | + $aFreeUsers[$oUser->getId()] = $oUser; | ||
| 284 | + } | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + $oTemplating = new KTTemplating; | ||
| 288 | + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles_manageusers"); | ||
| 289 | + $aTemplateData = array( | ||
| 290 | + "context" => $this, | ||
| 291 | + "edit_rolealloc" => $oRoleAllocation, | ||
| 292 | + 'unused_users' => $aFreeUsers, | ||
| 293 | + 'role_users' => $aRoleUsers, | ||
| 294 | + ); | ||
| 295 | + return $oTemplate->render($aTemplateData); | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + function do_editRoleGroups() { | ||
| 299 | + | ||
| 300 | + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'alloc_id'); | ||
| 301 | + $oRoleAllocation = RoleAllocation::get($role_allocation_id); | ||
| 302 | + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { | ||
| 303 | + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 304 | + } | ||
| 305 | + | ||
| 306 | + | ||
| 307 | + $this->oPage->setBreadcrumbDetails(_('Manage Groups for Role')); | ||
| 308 | + $this->oPage->setTitle(sprintf(_('Manage Groups for Role'))); | ||
| 309 | + | ||
| 310 | + $initJS = 'var optGroup = new OptionTransfer("groupSelect","chosenGroups"); ' . | ||
| 311 | + 'function startTrans() { var f = getElement("grouproleform"); ' . | ||
| 312 | + ' optGroup.saveNewRightOptions("groupFinal"); ' . | ||
| 313 | + ' optGroup.init(f); }; ' . | ||
| 314 | + ' addLoadEvent(startTrans); '; | ||
| 315 | + $this->oPage->requireJSStandalone($initJS); | ||
| 316 | + | ||
| 317 | + $aInitialUsers = $oRoleAllocation->getGroups(); | ||
| 318 | + $aAllUsers = Group::getList(); | ||
| 319 | + | ||
| 320 | + | ||
| 321 | + // FIXME this is massively non-performant for large userbases.. | ||
| 322 | + $aRoleUsers = array(); | ||
| 323 | + $aFreeUsers = array(); | ||
| 324 | + foreach ($aInitialUsers as $oGroup) { | ||
| 325 | + $aRoleUsers[$oGroup->getId()] = $oGroup; | ||
| 326 | + } | ||
| 327 | + foreach ($aAllUsers as $oGroup) { | ||
| 328 | + if (!array_key_exists($oGroup->getId(), $aRoleUsers)) { | ||
| 329 | + $aFreeUsers[$oGroup->getId()] = $oGroup; | ||
| 330 | + } | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + $oTemplating = new KTTemplating; | ||
| 334 | + $oTemplate = $oTemplating->loadTemplate("ktcore/folder/roles_managegroups"); | ||
| 335 | + $aTemplateData = array( | ||
| 336 | + "context" => $this, | ||
| 337 | + "edit_rolealloc" => $oRoleAllocation, | ||
| 338 | + 'unused_groups' => $aFreeUsers, | ||
| 339 | + 'role_groups' => $aRoleUsers, | ||
| 340 | + ); | ||
| 341 | + return $oTemplate->render($aTemplateData); | ||
| 342 | + } | ||
| 343 | + | ||
| 344 | + function do_setRoleUsers() { | ||
| 345 | + | ||
| 346 | + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'allocation_id'); | ||
| 347 | + $oRoleAllocation = RoleAllocation::get($role_allocation_id); | ||
| 348 | + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { | ||
| 349 | + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 350 | + } | ||
| 351 | + $users = KTUtil::arrayGet($_REQUEST, 'userFinal', ''); | ||
| 352 | + $aUserIds = explode(',', $users); | ||
| 353 | + | ||
| 354 | + // check that its not corrupt.. | ||
| 355 | + $aFinalUserIds = array(); | ||
| 356 | + foreach ($aUserIds as $iUserId) { | ||
| 357 | + $oUser =& User::get($iUserId); | ||
| 358 | + if (!(PEAR::isError($oUser) || ($oUser == false))) { | ||
| 359 | + $aFinalUserIds[] = $iUserId; | ||
| 360 | + } | ||
| 361 | + } | ||
| 362 | + if (empty($aFinalUserIds)) { $aFinalUserIds = null; } | ||
| 363 | + | ||
| 364 | + // hack straight in. | ||
| 365 | + $oPD = $oRoleAllocation->getPermissionDescriptor(); | ||
| 366 | + $aAllowed = $oPD->getAllowed(); | ||
| 367 | + | ||
| 368 | + | ||
| 369 | + | ||
| 370 | + // now, grab the existing allowed and modify. | ||
| 371 | + | ||
| 372 | + $aAllowed['user'] = $aFinalUserIds; | ||
| 373 | + | ||
| 374 | + $oRoleAllocation->setAllowed($aAllowed); | ||
| 375 | + $res = $oRoleAllocation->update(); | ||
| 376 | + | ||
| 377 | + if (PEAR::isError($res) || ($res == false)) { | ||
| 378 | + $this->errorRedirectToMain(_('Failed to change the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); | ||
| 379 | + } | ||
| 380 | + | ||
| 381 | + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); | ||
| 382 | + | ||
| 383 | + $this->successRedirectToMain(_('Allocation changed.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 384 | + } | ||
| 385 | + | ||
| 386 | + function do_setRoleGroups() { | ||
| 387 | + | ||
| 388 | + $role_allocation_id = KTUtil::arrayGet($_REQUEST, 'allocation_id'); | ||
| 389 | + $oRoleAllocation = RoleAllocation::get($role_allocation_id); | ||
| 390 | + if ((PEAR::isError($oRoleAllocation)) || ($oRoleAllocation=== false)) { | ||
| 391 | + $this->errorRedirectToMain(_('No such role allocation.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 392 | + } | ||
| 393 | + $groups = KTUtil::arrayGet($_REQUEST, 'groupFinal', ''); | ||
| 394 | + $aGroupIds = explode(',', $groups); | ||
| 395 | + | ||
| 396 | + // check that its not corrupt.. | ||
| 397 | + $aFinalGroupIds = array(); | ||
| 398 | + foreach ($aGroupIds as $iGroupId) { | ||
| 399 | + $oGroup =& Group::get($iGroupId); | ||
| 400 | + if (!(PEAR::isError($oGroup) || ($oGroup == false))) { | ||
| 401 | + $aFinalGroupIds[] = $iGroupId; | ||
| 402 | + } | ||
| 403 | + } | ||
| 404 | + if (empty($aFinalGroupIds)) { $aFinalGroupIds = null; } | ||
| 405 | + | ||
| 406 | + // hack straight in. | ||
| 407 | + $oPD = $oRoleAllocation->getPermissionDescriptor(); | ||
| 408 | + $aAllowed = $oPD->getAllowed(); | ||
| 409 | + | ||
| 410 | + | ||
| 411 | + | ||
| 412 | + // now, grab the existing allowed and modify. | ||
| 413 | + | ||
| 414 | + $aAllowed['group'] = $aFinalGroupIds; | ||
| 415 | + | ||
| 416 | + $oRoleAllocation->setAllowed($aAllowed); | ||
| 417 | + $res = $oRoleAllocation->update(); | ||
| 418 | + | ||
| 419 | + if (PEAR::isError($res) || ($res == false)) { | ||
| 420 | + $this->errorRedirectToMain(_('Failed to change the role allocation.') . print_r($res, true), sprintf('fFolderId=%d', $this->oFolder->getId())); | ||
| 421 | + } | ||
| 422 | + | ||
| 423 | + $this->renegeratePermissionsForRole($oRoleAllocation->getRoleId()); | ||
| 424 | + | ||
| 425 | + $this->successRedirectToMain(_('Allocation changed.'), sprintf('fFolderId=%d',$this->oFolder->getId())); | ||
| 426 | + } | ||
| 427 | + | ||
| 428 | + function renegeratePermissionsForRole($iRoleId) { | ||
| 429 | + $iStartFolderId = $this->oFolder->getId(); | ||
| 430 | + $oRoleAllocation = RoleAllocation::getAllocationsForFolderAndRole($iStartFolderId, $iRoleId); | ||
| 431 | + /* | ||
| 432 | + * 1. find all folders & documents "below" this one which use the role | ||
| 433 | + * definition _active_ (not necessarily present) at this point. | ||
| 434 | + * 2. tell permissionutil to regen their permissions. | ||
| 435 | + * | ||
| 436 | + * The find algorithm is: | ||
| 437 | + * | ||
| 438 | + * folder_queue <- (iStartFolderId) | ||
| 439 | + * while folder_queue is not empty: | ||
| 440 | + * active_folder = | ||
| 441 | + * for each folder in the active_folder: | ||
| 442 | + * find folders in _this_ folder without a role-allocation on the iRoleId | ||
| 443 | + * add them to the folder_queue | ||
| 444 | + * update the folder's permissions. | ||
| 445 | + * find documents in this folder: | ||
| 446 | + * update their permissions. | ||
| 447 | + */ | ||
| 448 | + | ||
| 449 | + $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 '; | ||
| 450 | + if ($oRoleAllocation == null) { // no alloc. | ||
| 451 | + $sQuery .= ' IS NULL '; | ||
| 452 | + $hasId = false; | ||
| 453 | + } else { | ||
| 454 | + $sQuery .= ' = ? '; | ||
| 455 | + $aId = $oRoleAllocation->getId(); | ||
| 456 | + $hasId = true; | ||
| 457 | + } | ||
| 458 | + | ||
| 459 | + | ||
| 460 | + $folder_queue = array($iStartFolderId); | ||
| 461 | + while (!empty($folder_queue)) { | ||
| 462 | + $active_folder = array_pop($folder_queue); | ||
| 463 | + | ||
| 464 | + | ||
| 465 | + $aParams = array($active_folder); | ||
| 466 | + if ($hasId) { $aParams[] = $aId; } | ||
| 467 | + | ||
| 468 | + | ||
| 469 | + $aNewFolders = DBUtil::getResultArrayKey(array($sQuery, $aParams), 'id'); | ||
| 470 | + if (PEAR::isError($aNewFolders)) { | ||
| 471 | + $this->errorRedirectToMain(_('Failure to generate folderlisting.')); | ||
| 472 | + } | ||
| 473 | + $folder_queue = array_merge ($folder_queue, $aNewFolders); // push. | ||
| 474 | + | ||
| 475 | + | ||
| 476 | + // update the folder. | ||
| 477 | + $oFolder =& Folder::get($active_folder); | ||
| 478 | + if (PEAR::isError($oFolder) || ($oFolder == false)) { | ||
| 479 | + $this->errorRedirectToMain(_('Unable to locate folder: ') . $active_folder); | ||
| 480 | + } | ||
| 481 | + | ||
| 482 | + KTPermissionUtil::updatePermissionLookup($oFolder); | ||
| 483 | + $aDocList =& Document::getList(array('folder_id = ?', $active_folder)); | ||
| 484 | + if (PEAR::isError($aDocList) || ($aDocList == false)) { | ||
| 485 | + $this->errorRedirectToMain(_('Unable to get documents in folder: ') . $active_folder); | ||
| 486 | + } | ||
| 487 | + | ||
| 488 | + foreach ($aDocList as $oDoc) { | ||
| 489 | + KTPermissionUtil::updatePermissionLookup($oDoc); | ||
| 490 | + } | ||
| 491 | + } | ||
| 492 | + } | ||
| 493 | +} | ||
| 494 | +$oPlugin->registerAction('folderaction', 'KTRoleAllocationPlugin', 'ktcore.actions.folder.roles'); |
presentation/lookAndFeel/knowledgeTree/browse.php
| @@ -46,7 +46,7 @@ $sectionName = "browse"; | @@ -46,7 +46,7 @@ $sectionName = "browse"; | ||
| 46 | 46 | ||
| 47 | class BrowseDispatcher extends KTStandardDispatcher { | 47 | class BrowseDispatcher extends KTStandardDispatcher { |
| 48 | 48 | ||
| 49 | - var $oFolder = null; | 49 | + var $oFolder = null; |
| 50 | var $sSection = "browse"; | 50 | var $sSection = "browse"; |
| 51 | var $browse_mode = null; | 51 | var $browse_mode = null; |
| 52 | var $query = null; | 52 | var $query = null; |
| @@ -79,12 +79,12 @@ class BrowseDispatcher extends KTStandardDispatcher { | @@ -79,12 +79,12 @@ class BrowseDispatcher extends KTStandardDispatcher { | ||
| 79 | 79 | ||
| 80 | // here we need the folder object to do the breadcrumbs. | 80 | // here we need the folder object to do the breadcrumbs. |
| 81 | $oFolder =& Folder::get($folder_id); | 81 | $oFolder =& Folder::get($folder_id); |
| 82 | + $this->oFolder =& $oFolder; | ||
| 82 | if (PEAR::isError($oFolder)) { | 83 | if (PEAR::isError($oFolder)) { |
| 83 | $this->oPage->addError(_("invalid folder")); | 84 | $this->oPage->addError(_("invalid folder")); |
| 84 | $folder_id = 1; | 85 | $folder_id = 1; |
| 85 | $oFolder =& Folder::get($folder_id); | 86 | $oFolder =& Folder::get($folder_id); |
| 86 | } | 87 | } |
| 87 | - $this->oFolder =& $oFolder; | ||
| 88 | 88 | ||
| 89 | // we now have a folder, and need to create the query. | 89 | // we now have a folder, and need to create the query. |
| 90 | $this->oQuery = new BrowseQuery($oFolder->getId()); | 90 | $this->oQuery = new BrowseQuery($oFolder->getId()); |
resources/css/kt-framing.css
| @@ -622,6 +622,7 @@ The text will be hidden for screen view. The generic fahrner-ish approach comes | @@ -622,6 +622,7 @@ The text will be hidden for screen view. The generic fahrner-ish approach comes | ||
| 622 | 622 | ||
| 623 | .listing td { | 623 | .listing td { |
| 624 | padding: 0.5em; | 624 | padding: 0.5em; |
| 625 | + vertical-align: top; | ||
| 625 | } | 626 | } |
| 626 | 627 | ||
| 627 | .listing thead th { | 628 | .listing thead th { |
sql/mysql/install/structure.sql
| @@ -1008,9 +1008,6 @@ CREATE TABLE `permissions` ( | @@ -1008,9 +1008,6 @@ CREATE TABLE `permissions` ( | ||
| 1008 | CREATE TABLE `roles` ( | 1008 | CREATE TABLE `roles` ( |
| 1009 | `id` int(11) NOT NULL default '0', | 1009 | `id` int(11) NOT NULL default '0', |
| 1010 | `name` char(255) NOT NULL default '', | 1010 | `name` char(255) NOT NULL default '', |
| 1011 | - `active` tinyint(1) NOT NULL default '0', | ||
| 1012 | - `can_read` tinyint(1) NOT NULL default '0', | ||
| 1013 | - `can_write` tinyint(1) NOT NULL default '0', | ||
| 1014 | UNIQUE KEY `id` (`id`), | 1011 | UNIQUE KEY `id` (`id`), |
| 1015 | UNIQUE KEY `name` (`name`) | 1012 | UNIQUE KEY `name` (`name`) |
| 1016 | ) ENGINE=InnoDB ; | 1013 | ) ENGINE=InnoDB ; |
| @@ -1018,6 +1015,22 @@ CREATE TABLE `roles` ( | @@ -1018,6 +1015,22 @@ CREATE TABLE `roles` ( | ||
| 1018 | -- -------------------------------------------------------- | 1015 | -- -------------------------------------------------------- |
| 1019 | 1016 | ||
| 1020 | -- | 1017 | -- |
| 1018 | +-- Table structure for table `role_allocations` | ||
| 1019 | +-- | ||
| 1020 | + | ||
| 1021 | +CREATE TABLE `role_allocations` ( | ||
| 1022 | + `id` int(11) NOT NULL default '0', | ||
| 1023 | + `folder_id` int(11) NOT NULL default '0', | ||
| 1024 | + `role_id` int(11) NOT NULL default '0', | ||
| 1025 | + `permission_descriptor_id` int(11) NOT NULL default '0', | ||
| 1026 | + UNIQUE KEY `id` (`id`), | ||
| 1027 | + KEY `folder_id` (`folder_id`) | ||
| 1028 | +) ENGINE=InnoDB ; | ||
| 1029 | + | ||
| 1030 | +-- -------------------------------------------------------- | ||
| 1031 | + | ||
| 1032 | + | ||
| 1033 | +-- | ||
| 1021 | -- Table structure for table `saved_searches` | 1034 | -- Table structure for table `saved_searches` |
| 1022 | -- | 1035 | -- |
| 1023 | 1036 | ||
| @@ -1916,6 +1929,19 @@ CREATE TABLE `zseq_roles` ( | @@ -1916,6 +1929,19 @@ CREATE TABLE `zseq_roles` ( | ||
| 1916 | 1929 | ||
| 1917 | -- -------------------------------------------------------- | 1930 | -- -------------------------------------------------------- |
| 1918 | 1931 | ||
| 1932 | + | ||
| 1933 | +-- | ||
| 1934 | +-- Table structure for table `zseq_role_allocations` | ||
| 1935 | +-- | ||
| 1936 | + | ||
| 1937 | +CREATE TABLE `zseq_role_allocations` ( | ||
| 1938 | + `id` int(10) unsigned NOT NULL auto_increment, | ||
| 1939 | + PRIMARY KEY (`id`) | ||
| 1940 | +) ENGINE=MyISAM ; | ||
| 1941 | + | ||
| 1942 | +-- -------------------------------------------------------- | ||
| 1943 | + | ||
| 1944 | + | ||
| 1919 | -- | 1945 | -- |
| 1920 | -- Table structure for table `zseq_saved_searches` | 1946 | -- Table structure for table `zseq_saved_searches` |
| 1921 | -- | 1947 | -- |
sql/mysql/upgrade/2.99.5/role_allocations.sql
0 → 100644
| 1 | +SET FOREIGN_KEY_CHECKS=0; | ||
| 2 | + | ||
| 3 | +CREATE TABLE `role_allocations` ( | ||
| 4 | + `id` int(11) NOT NULL default '0', | ||
| 5 | + `folder_id` int(11) NOT NULL default '0', | ||
| 6 | + `role_id` int(11) NOT NULL default '0', | ||
| 7 | + `permission_descriptor_id` int(11) NOT NULL default '0', | ||
| 8 | + UNIQUE KEY `id` (`id`), | ||
| 9 | + KEY `folder_id` (`folder_id`) | ||
| 10 | +) ENGINE=InnoDB ; | ||
| 11 | + | ||
| 12 | +CREATE TABLE `zseq_role_allocations` ( | ||
| 13 | + `id` int(10) unsigned NOT NULL auto_increment, | ||
| 14 | + PRIMARY KEY (`id`) | ||
| 15 | +) ENGINE=MyISAM ; | ||
| 16 | + | ||
| 17 | + | ||
| 18 | +SET FOREIGN_KEY_CHECKS=1; |
sql/mysql/upgrade/2.99.5/role_changes.sql
0 → 100644
templates/ktcore/folder/roles.smarty
0 → 100644
| 1 | +<h2>{i18n}Allocate Roles{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +<p class="descriptiveText">{i18n} | ||
| 4 | + In many cases, workflow actions will be assigned to certain <strong>roles</strong> | ||
| 5 | + (e.g. Manager, Interviewer, Researcher, Journalist). You can assign these roles | ||
| 6 | + to specific groups in particular areas of the document management system. | ||
| 7 | +{/i18n}</p> | ||
| 8 | + | ||
| 9 | +<div class='ktInfo'><p><strong>{i18n}Warning:{/i18n}</strong>{i18n} Please note that changing | ||
| 10 | +role allocations can take very long time, depending on the number of folders below this one.{/i18n}</p></div> | ||
| 11 | + | ||
| 12 | +<table class="listing"> | ||
| 13 | +<thead> | ||
| 14 | + <tr> | ||
| 15 | + <th>{i18n}Role{/i18n}</th> | ||
| 16 | + <th>{i18n}Allocated users{/i18n}</th> | ||
| 17 | + <th>{i18n}Edit Users{/i18n}</th> | ||
| 18 | + <th>{i18n}Edit Groups{/i18n}</th> | ||
| 19 | + <th>{i18n}Use Parent{/i18n}</th> | ||
| 20 | + </tr> | ||
| 21 | +</thead> | ||
| 22 | +<tbody> | ||
| 23 | +{foreach item=aRole key=role_id from=$roles} | ||
| 24 | + <tr class="{cycle values=odd,even}"> | ||
| 25 | + <td>{$aRole.name}</td> | ||
| 26 | + <td> | ||
| 27 | + {if ($aRole.allocation_id === null)} | ||
| 28 | + <strong>{i18n}inherited from parent folder.{/i18n}</strong><br /> | ||
| 29 | + <span class="descriptiveText"> | ||
| 30 | + {/if} | ||
| 31 | + {if ($aRole.users != null)}<strong>{i18n}Users:{/i18n}</strong> {$aRole.users}<br />{/if} | ||
| 32 | + {if ($aRole.groups != null)}<strong>{i18n}Groups:{/i18n}</strong> {$aRole.groups}{/if} | ||
| 33 | + {if ($aRole.allocation_id === null)} | ||
| 34 | + </span class="descriptiveText"> | ||
| 35 | + {/if} | ||
| 36 | + </td> | ||
| 37 | + {if ($aRole.allocation_id === null)} | ||
| 38 | + <td colspan="3"><a href="{$smarty.server.PHP_SELF}?action=overrideParent&role_id={$role_id}&fFolderId={$context->oFolder->getId()}">{i18n}Override Parent Allocation{/i18n}</a></td> | ||
| 39 | + {else} | ||
| 40 | + <td><a href="{$smarty.server.PHP_SELF}?action=editRoleUsers&alloc_id={$aRole.allocation_id}&fFolderId={$context->oFolder->getId()}" class="ktAction ktEdit" title="{i18n}Edit{/i18n}">Edit</a></td> | ||
| 41 | + <td><a href="{$smarty.server.PHP_SELF}?action=editRoleGroups&alloc_id={$aRole.allocation_id}&fFolderId={$context->oFolder->getId()}" class="ktAction ktEdit" title="{i18n}Edit{/i18n}">Edit</a></td> | ||
| 42 | + <td><a href="{$smarty.server.PHP_SELF}?action=useParent&role_id={$role_id}&fFolderId={$context->oFolder->getId()}" class="ktAction ktDelete" title="{i18n}Use parent's allocation{/i18n}">{i18n}Use parent's allocation{/i18n}</a></td> | ||
| 43 | + {/if} | ||
| 44 | + </tr> | ||
| 45 | +{/foreach} | ||
| 46 | +</tbody> | ||
| 47 | +</table> | ||
| 0 | \ No newline at end of file | 48 | \ No newline at end of file |
templates/ktcore/folder/roles_managegroups.smarty
0 → 100644
| 1 | +<h2>{i18n}Allocate Groups to Role{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +{$context->oPage->requireJSResource('thirdpartyjs/OptionTransfer.js')} | ||
| 4 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/Base.js')} | ||
| 5 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/Iter.js')} | ||
| 6 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/DOM.js')} | ||
| 7 | + | ||
| 8 | +<p class="descriptiveText"><strong>FIXME</strong> help text for role-editing. </p> | ||
| 9 | + | ||
| 10 | +<form action="{$smarty.server.PHP_SELF}" method="POST" id="grouproleform"> | ||
| 11 | + <input type="hidden" name="action" value="setRoleGroups" /> | ||
| 12 | + <input type="hidden" name="allocation_id" value="{$edit_rolealloc->getId()}" /> | ||
| 13 | + <input type="hidden" name="fFolderId" value="{$context->oFolder->getId()}" /> | ||
| 14 | + <!-- erk. FIXME clean up and remove OptionTransfer.js. --> | ||
| 15 | + | ||
| 16 | + <input type="hidden" name="groupFinal" /> | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + <fieldset> | ||
| 20 | + <legend>{i18n}Allocate Groups to Role{/i18n}</legend> | ||
| 21 | + <p class="descriptiveText">{i18n}Select the groups which should be part of this role.{/i18n} <strong>FIXME</strong> this helptext is v. awkward.</p> | ||
| 22 | + | ||
| 23 | +<table border="0" width="600"> | ||
| 24 | + <thead> | ||
| 25 | + <tr> | ||
| 26 | + <th>{i18n}Available Groups{/i18n}</th> | ||
| 27 | + <th> </th> | ||
| 28 | + <th>{i18n}Member groups{/i18n}</th> | ||
| 29 | + </tr> | ||
| 30 | + </thead> | ||
| 31 | + <tbody> | ||
| 32 | + <tr> | ||
| 33 | + <td valign="top" width="1%"> | ||
| 34 | + <select name="groupSelect" size="10" multiple="multiple"> | ||
| 35 | + {foreach item=oGroup from=$unused_groups} | ||
| 36 | + <option value="{$oGroup->getId()}" ondblclick="optGroup.transferRight()">{$oGroup->getName()}</option> | ||
| 37 | + {/foreach} | ||
| 38 | + </select> | ||
| 39 | + </td> | ||
| 40 | + <td align="center"> | ||
| 41 | + <input name="right" style="width: 60px;" value="»" onclick="optGroup.transferRight()" type="button"><br /><br /> | ||
| 42 | + <input name="left" style="width: 60px;" value="«" onclick="optGroup.transferLeft()" type="button"> | ||
| 43 | + </td> | ||
| 44 | + <td valign="top" width="1%"> | ||
| 45 | + <select name="chosenGroups" size="10" multiple="multiple"> | ||
| 46 | + {foreach item=oGroup from=$role_groups} | ||
| 47 | + <option value="{$oGroup->getId()}" ondblclick="optGroup.transferRight()">{$oGroup->getName()}</option> | ||
| 48 | + {/foreach} | ||
| 49 | + </select> | ||
| 50 | + </td> | ||
| 51 | + </tr> | ||
| 52 | + <tr> | ||
| 53 | + <td><label for="ug-filter">{i18n}Filter{/i18n}</label> | ||
| 54 | + <input name="filterUG" id="ug-filter" onkeyup="optGroup.sortSelectMatch(groupSelect, this.value)" onchange="optGroup.sortSelectMatch(groupSelect, this.value)" type="text"> | ||
| 55 | + </td> | ||
| 56 | + <td> </td> | ||
| 57 | + <td><label for="og-filter">{i18n}Filter{/i18n}</label> | ||
| 58 | + <input name="filterOG" id="og-filter" onkeyup="optGroup.sortSelectMatch(chosenGroups, this.value)" onchange="optGroup.sortSelectMatch(chosenGroups, this.value)" type="text"> | ||
| 59 | + </td> | ||
| 60 | + </tr> | ||
| 61 | +</tbody></table> | ||
| 62 | + | ||
| 63 | + <div class="form_actions"> | ||
| 64 | + <input type="submit" value="{i18n}save changes{/i18n}" /> | ||
| 65 | + <a href="?action=main" class="ktCancelLink">{i18n}Cancel{/i18n}</a> | ||
| 66 | + </div> | ||
| 67 | + </fieldset> | ||
| 68 | + </form> |
templates/ktcore/folder/roles_manageusers.smarty
0 → 100644
| 1 | +<h2>{i18n}Allocate User to Role{/i18n}</h2> | ||
| 2 | + | ||
| 3 | +{$context->oPage->requireJSResource('thirdpartyjs/OptionTransfer.js')} | ||
| 4 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/Base.js')} | ||
| 5 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/Iter.js')} | ||
| 6 | +{$context->oPage->requireJSResource('thirdpartyjs/MochiKit/DOM.js')} | ||
| 7 | + | ||
| 8 | +<p class="descriptiveText"><strong>FIXME</strong> help text for role-editing. </p> | ||
| 9 | + | ||
| 10 | +<form action="{$smarty.server.PHP_SELF}" method="POST" id="userroleform"> | ||
| 11 | + <input type="hidden" name="action" value="setRoleUsers" /> | ||
| 12 | + <input type="hidden" name="allocation_id" value="{$edit_rolealloc->getId()}" /> | ||
| 13 | + <input type="hidden" name="fFolderId" value="{$context->oFolder->getId()}" /> | ||
| 14 | + <!-- erk. FIXME clean up and remove OptionTransfer.js. --> | ||
| 15 | + | ||
| 16 | + <input type="hidden" name="userFinal" /> | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + <fieldset> | ||
| 20 | + <legend>{i18n}Allocate User to Role{/i18n}</legend> | ||
| 21 | + <p class="descriptiveText">{i18n}Select the users which should be part of this role.{/i18n} <strong>FIXME</strong> this helptext is v. awkward.</p> | ||
| 22 | + | ||
| 23 | +<table border="0" width="600"> | ||
| 24 | + <thead> | ||
| 25 | + <tr> | ||
| 26 | + <th>{i18n}Available Users{/i18n}</th> | ||
| 27 | + <th> </th> | ||
| 28 | + <th>{i18n}Member users{/i18n}</th> | ||
| 29 | + </tr> | ||
| 30 | + </thead> | ||
| 31 | + <tbody> | ||
| 32 | + <tr> | ||
| 33 | + <td valign="top" width="1%"> | ||
| 34 | + <select name="userSelect" size="10" multiple="multiple"> | ||
| 35 | + {foreach item=oUser from=$unused_users} | ||
| 36 | + <option value="{$oUser->getId()}" ondblclick="optGroup.transferRight()">{$oUser->getName()}</option> | ||
| 37 | + {/foreach} | ||
| 38 | + </select> | ||
| 39 | + </td> | ||
| 40 | + <td align="center"> | ||
| 41 | + <input name="right" style="width: 60px;" value="»" onclick="optGroup.transferRight()" type="button"><br /><br /> | ||
| 42 | + <input name="left" style="width: 60px;" value="«" onclick="optGroup.transferLeft()" type="button"> | ||
| 43 | + </td> | ||
| 44 | + <td valign="top" width="1%"> | ||
| 45 | + <select name="chosenUsers" size="10" multiple="multiple"> | ||
| 46 | + {foreach item=oUser from=$role_users} | ||
| 47 | + <option value="{$oUser->getId()}" ondblclick="optGroup.transferRight()">{$oUser->getName()}</option> | ||
| 48 | + {/foreach} | ||
| 49 | + </select> | ||
| 50 | + </td> | ||
| 51 | + </tr> | ||
| 52 | + <tr> | ||
| 53 | + <td><label for="ug-filter">{i18n}Filter{/i18n}</label> | ||
| 54 | + <input name="filterUG" id="ug-filter" onkeyup="optGroup.sortSelectMatch(userSelect, this.value)" onchange="optGroup.sortSelectMatch(groupSelect, this.value)" type="text"> | ||
| 55 | + </td> | ||
| 56 | + <td> </td> | ||
| 57 | + <td><label for="og-filter">{i18n}Filter{/i18n}</label> | ||
| 58 | + <input name="filterOG" id="og-filter" onkeyup="optGroup.sortSelectMatch(chosenUsers, this.value)" onchange="optGroup.sortSelectMatch(chosenGroups, this.value)" type="text"> | ||
| 59 | + </td> | ||
| 60 | + </tr> | ||
| 61 | +</tbody></table> | ||
| 62 | + | ||
| 63 | + <div class="form_actions"> | ||
| 64 | + <input type="submit" value="{i18n}save changes{/i18n}" /> | ||
| 65 | + <a href="?action=main" class="ktCancelLink">{i18n}Cancel{/i18n}</a> | ||
| 66 | + </div> | ||
| 67 | + </fieldset> | ||
| 68 | + </form> |