From bdf883ad519fb2cd36cbc88520a9f50f3e13268c Mon Sep 17 00:00:00 2001 From: Ioannis Igoumenos Date: Fri, 16 Feb 2024 20:16:03 +0000 Subject: [PATCH] Add authorized users group configuration.Improved/Simplified existing code. --- Config/Schema/schema.xml | 1 + Controller/GrouperGroupsController.php | 10 +- Lib/GrouperApiAccess.php | 823 +++++++++--------- Lib/enum.php | 8 + Lib/lang.php | 3 + Model/CoGrouperLiteWidget.php | 5 + Model/GrouperGroup.php | 153 ++-- View/CoGrouperLiteWidgets/fields.inc | 111 ++- View/CoGrouperLiteWidgets/view.ctp | 118 +++ .../Elements/Components/navigation-groups.ctp | 12 + View/GrouperGroups/groupinfo.ctp | 2 +- webroot/js/groups-table.js | 6 +- webroot/js/groups.js | 8 +- webroot/js/members.js | 10 +- 14 files changed, 705 insertions(+), 565 deletions(-) create mode 100644 View/CoGrouperLiteWidgets/view.ctp diff --git a/Config/Schema/schema.xml b/Config/Schema/schema.xml index f2bfb51..506f630 100644 --- a/Config/Schema/schema.xml +++ b/Config/Schema/schema.xml @@ -37,6 +37,7 @@ + diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index d0379bd..b842c72 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -590,21 +590,21 @@ public function leaveGroup() function isAuthorized() { $roles = $this->Role->calculateCMRoles(); - + $cfg = $this->CoGrouperLiteWidget->getConfig(); // Find the identifier $args = array(); // XXX This should become configuration - $args['conditions']['Identifier.type'] = 'I2CollabPN'; + $args['conditions']['Identifier.type'] = $cfg['CoGrouperLiteWidget']['identifier_type']; $args['conditions']['Identifier.status'] = SuspendableStatusEnum::Active; - $args['conditions']['Identifier.co_person_id'] = $roles["copersonid"]; + $args['conditions']['Identifier.co_person_id'] = $roles['copersonid']; $args['contain'] = false; $identifiers = $this->Identifier->find('first', $args); if(!empty($identifiers) && is_array($identifiers) - && isset($identifiers["Identifier"]["identifier"]) + && isset($identifiers['Identifier']['identifier']) ) { - $this->userId = $identifiers["Identifier"]["identifier"]; + $this->userId = $identifiers['Identifier']['identifier']; } // Determine what operations this user can perform diff --git a/Lib/GrouperApiAccess.php b/Lib/GrouperApiAccess.php index 0ba94e6..144abc9 100644 --- a/Lib/GrouperApiAccess.php +++ b/Lib/GrouperApiAccess.php @@ -71,194 +71,343 @@ public function __construct(array $cfg) } /** - * Get Groups that User is a member of from Grouper. - * - * Note: Params added at end make sure that the groups returned can only be viewed by the member logged into - * Grouper Lite + * Add a member to a specific Grouper Group * * @param array $queryData Array of conditions for querying - * @return array Membership records that User is a member of in Grouper - * @throws GrouperLiteWidgetException + * + * @return string Requests success or not + * @throws GrouperLiteWidgetException|JsonException */ - public function getGrouperMemberOfGroups(array $queryData) + public function addMemberToGroup(array $queryData) { + $groupName = $queryData['groupName']; + $connectionUrl = "{$this->config['fullUrl']}/groups/$groupName/members"; //Build request logic - $userId = $queryData['userId']; - $connectionUrl = "{$this->config['fullUrl']}/subjects/$userId/groups?"; - $connectionUrl .= "wsLiteObjectType=WsRestGetGroupsLiteRequest&actAsSubjectId=$userId"; + $usersToAdd = array( + 'WsRestAddMemberRequest' => array( + 'subjectLookups' => array( + array('subjectId' => $queryData['addUserId']), + ), + 'replaceAllExisting' => 'F', + 'actAsSubjectLookup' => array( + 'subjectId' => $queryData['userId'] + ) + ) + ); + + $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); try { - $results = $this->http->sendRequest('GET', $connectionUrl); + $results = $this->http->sendRequest('PUT', $connectionUrl, json_encode($usersToAdd, JSON_THROW_ON_ERROR)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); // Parse out relevant records to send front end - if (isset($results['WsGetGroupsLiteResult']['wsGroups']) && $results['WsGetGroupsLiteResult']['wsGroups'] != NULL) { - return $results['WsGetGroupsLiteResult']['wsGroups']; + if (isset($results['WsAddMemberResults']['results'][0]['resultMetadata'])) { + return $results['WsAddMemberResults']['results'][0]['resultMetadata']['resultCode']; } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return array(); + return ''; } /** - * For Optin groups, calls Grouper WS to join or leave a group + * Method used to CREATE a new Group in Grouper via the Working Group Template method. * - * @param array $queryData Array of conditions for querying + * @param array $queryData Array of conditions and data adding new Grouper Group + * @return array status and error message, if applicable + * @throws GrouperLiteWidgetException * - * @return bool True if join or leave successful, False if not - * @throws GrouperLiteWidgetException|JsonException */ - public function grouperGroupLeaveOrJoin(array $queryData) + public function createGroupWithTemplate(array $queryData) { - $groupName = $queryData['groupName']; + //Currently, only supporting create group, need to test if update a group will work! + + $data = $queryData['data']; $userId = $queryData['userId']; - $groupLeaveOrJoin = $queryData['LeaveJoin']; - $groupName = urlencode($groupName); - if ($groupLeaveOrJoin == "Leave") { - $memberRequest = "WsRestDeleteMemberRequest"; - $resultResponse = 'WsDeleteMemberResults'; - $resultGroup = 'wsGroup'; - } elseif ($groupLeaveOrJoin == "Join") { - $memberRequest = "WsRestAddMemberRequest"; - $resultResponse = 'WsAddMemberResults'; - $resultGroup = 'wsGroupAssigned'; - } else { - CakeLog::write('error', __METHOD__ . ": Option of $groupLeaveOrJoin is not supported"); - throw new GrouperLiteWidgetException("Received option of $groupLeaveOrJoin which is not supported"); + //need to take out SympaDomain if domain not being created. + if ($data['gsh_input_isSympa'] == 'false') { + unset($data['gsh_input_sympaDomain'], $data['gsh_input_isSympaModerated']); } //Build request logic - $groupCommand = array( - $memberRequest => array( - "actAsSubjectLookup" => array( - "subjectId" => $userId + $inputFields = array(); + foreach ($data as $key => $value) { + $inputFields[] = array('name' => $key, 'value' => $value); + } + + $groupToSave = array( + 'WsRestGshTemplateExecRequest' => array( + 'gshTemplateActAsSubjectLookup' => array( + 'subjectSourceId' => 'ldap', + 'subjectId' => $userId ), - "replaceAllExisting" => "F", - "subjectLookups" => array( - array("subjectId" => $userId) - ) + 'ownerStemLookup' => array( + 'stemName' => 'ref:incommon-collab' + ), + 'ownerType' => 'stem', + 'configId' => 'createNewWorkingGroup', + 'inputs' => $inputFields ) ); $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); - $connectionUrl = "{$this->config['fullUrl']}/groups/$groupName/members"; + $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec"; + $status = true; + $message = ''; try { - $results = $this->http->sendRequest('PUT', $connectionUrl, json_encode($groupCommand, JSON_THROW_ON_ERROR)); + $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - - if (isset($results[$resultResponse][$resultGroup])) { - $groupAssigned = $results[$resultResponse][$resultGroup]['name']; - if ($groupAssigned == urldecode($groupName)) { - return true; - } - } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return false; + if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'])) { + $status = false; + $message = 'An error occurred, please try again later.'; + + CakeLog::write('error', __METHOD__ . ': An error occurred in creating your Working Group!'); + + if (isset($results['WsGshTemplateExecResult']['gshValidationLines'][0]['validationText']) + && stripos( + $results['WsGshTemplateExecResult']['gshValidationLines'][0]['validationText'], + 'already exist', + 0 ) !== false) + { + $message = 'There already is a WG named: ' . $data['gsh_input_workingGroupExtension']; + } elseif (stripos($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], 'EXCEPTION', 0) !== false) { + throw new GrouperLiteWidgetException('An error occurred in creating your Working Group!'); + } + } + + return compact('status', 'message'); } /** - * Gets all available Optin/OptOut groups in Grouper * - * Returns Optin/OptOut groups that can be joined/left + * For creating/updating Ad-Hoc groups not using the Grouper Template format * - * @param array $queryData Array of conditions for querying - * @return array Optin groups from Grouper - * @throws GrouperLiteWidgetException + * Create or Update a Group where User is Admin/Owner + * + * @param array $queryData Array of conditions and data adding new Grouper Group * + * @return bool True if added or updated successful + * @throws GrouperLiteWidgetException|JsonException */ - public function getOptionalGroups(array $queryData) + public function createUpdateGroup(array $queryData) { + $groupName = htmlentities($queryData['name']); + $stemName = htmlentities($queryData['stem']); + $userId = $queryData['userId']; + $groupDescription = htmlentities($queryData['description']); + + //Group name may be in "friendly" format, so need to do some small conversions before saving. + $newGroupName = ucfirst(strtolower($groupName)); + $newGroupName = str_replace(' ', '', $newGroupName); + + $newGroupToSave = "$stemName:$newGroupName"; + + //Build request logic + $groupToSave = array( + 'WsRestGroupSaveRequest' => array( + 'actAsSubjectLookup' => array( + 'subjectId' => $userId + ), + 'wsGroupToSaves' => array( + array( + 'wsGroup' => array( + 'description' => $groupDescription, + 'name' => $newGroupToSave, + 'displayExtension' => $groupName + ), + 'wsGroupLookup' => array( + 'groupName' => $groupDescription + ) + ) + ) + ) + ); + $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); + $connectionUrl = "{$this->config['fullUrl']}/groups"; + try { - $results = $this->useMembershipUrl($queryData); + $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave, JSON_THROW_ON_ERROR)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - - if (isset($results['WsGetMembershipsResults']['wsGroups'])) { - return $results['WsGetMembershipsResults']['wsGroups']; - } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return array(); + + if (isset($results['WsGroupSaveResults']['results']['resultMetadata']['resultCode']) + && stripos( + $results['WsGroupSaveResults']['results']['resultMetadata']['resultCode'], + 'Success', + 0 + ) !== false) { + return true; + } + + return false; } /** - * Gets all groups in Grouper where user is an admin/owner or has update privs * - * @param array $queryData Array of conditions for querying - * @return array Array of groups from Grouper - * @throws GrouperLiteWidgetException + * Method used to DELETE a Group in Grouper via the Template method. + * + * @param array $queryData Array of conditions and data adding new Grouper Group + * + * @return bool True if deleted successfully + * @throws GrouperLiteWidgetException|JsonException * */ - public function getOwnedGroups(array $queryData) + public function deleteGroupWithTemplate(array $queryData) { - $admins = array(); - $updaters = array(); - try { - $queryData['groupType'] = 'admin'; - $resultsAdmin = $this->useMembershipUrl($queryData); - CakeLog::write('debug', __METHOD__ . '::response admins: ' . var_export($resultsAdmin, true)); + $workingGroupExt = $queryData['workingGroupExt']; + $userId = $queryData['userId']; - $queryData['groupType'] = 'update'; - $resultsUpdate = $this->useMembershipUrl($queryData); - CakeLog::write('debug', __METHOD__ . '::response updates: ' . var_export($resultsUpdate, true)); + $groupToDelete = array( + 'WsRestGshTemplateExecRequest' => array( + 'gshTemplateActAsSubjectLookup' => array( + 'subjectSourceId' => 'ldap', + 'subjectId' => $userId + ), + 'ownerStemLookup' => array( + 'stemName' => 'ref:incommon-collab' + ), + 'ownerType' => 'stem', + 'configId' => 'createWorkingGroup', + 'inputs' => array( + array( + 'name' => 'gsh_input_workingGroupExtension', + 'value' => $workingGroupExt + ) + ) + ) + ); - if (isset($resultsAdmin['WsGetMembershipsResults']['wsGroups'])) { - $admins = $resultsAdmin['WsGetMembershipsResults']['wsGroups']; - } - if (isset($resultsUpdate['WsGetMembershipsResults']['wsGroups'])) { - $updaters = $resultsUpdate['WsGetMembershipsResults']['wsGroups']; - } + $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); + $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec"; - return $this->removeDuplicates($admins, $updaters); + try { + $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToDelete, JSON_THROW_ON_ERROR)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); + CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } + + if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode']) && stripos( + $results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], + 'SUCCESS', + 0 + ) !== false) { + return true; + } + + return false; } /** - * Removes duplicates where the user is the owner and the updater of the group. Just one line instead of two. + * Used to be used for Grouper group info page. * - * @param array $arrOne - * @param array $arrTwo - * @return array + * Grouper Group information plus a listing of attributes in Grouper for that given Group + * + * @param array $queryData Array of conditions for querying + * + * @return array Record of Grouper attributes for given GroupName + * @throws GrouperLiteWidgetException|JsonException */ - public function removeDuplicates(array $arrOne, array $arrTwo) + public function getGrouperGroupInfo(array $queryData) { + $groupName = $queryData['groupName']; + $groupInfo = array(); - //Determine which array is bigger and use as base - $countOne = count($arrOne); - $countTwo = count($arrTwo); - if ($countOne >= $countTwo) { - $arrL = $arrOne; - $arrS = $arrTwo; - } else { - $arrL = $arrTwo; - $arrS = $arrOne; + //Build request logic + $stemToFind = array( + 'WsRestGetAttributeAssignmentsRequest' => array( + 'attributeAssignType' => 'group', + 'includeAssignmentsOnAssignments' => 'T', + 'wsOwnerGroupLookups' => array( + array( + 'groupName' => $groupName, + ) + ) + ) + ); + $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); + $connectionUrl = "{$this->config['fullUrl']}/attributeAssignments"; + + try { + $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($stemToFind, JSON_THROW_ON_ERROR)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); + CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); + + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; } - foreach ($arrL as $large) { - foreach ($arrS as $key => $val) { - if ($large['uuid'] == $val['uuid']) { - unset($arrS[$key]); - } - } + //Get the group information + if (isset($results['WsGetAttributeAssignmentsResults']['wsGroups'])) { + $groupInfo = $results['WsGetAttributeAssignmentsResults']['wsGroups']; } - return array_merge_recursive($arrL, $arrS); + //Now get the Group Attributes and add them to group + $groupInfo[0]['attributes'] = $results['WsGetAttributeAssignmentsResults']['wsAttributeAssigns'] ?? array(); + + return $groupInfo; + } + + /** + * Get Groups that User is a member of from Grouper. + * + * Note: Params added at end make sure that the groups returned can only be viewed by the member logged into + * Grouper Lite + * + * @param array $queryData Array of conditions for querying + * @return array Membership records that User is a member of in Grouper + * + * @throws GrouperLiteWidgetException + */ + public function getGrouperMemberOfGroups(array $queryData, string $groupName = null) + { + if(empty($queryData['userId'])) { + return []; + } + + $connectionUrl = "{$this->config['fullUrl']}/subjects/{$queryData['userId']}/groups?"; + $queryParameters = "wsLiteObjectType=WsRestGetGroupsLiteRequest" + . "&actAsSubjectId={$queryData['userId']}"; + if($groupName !== null) { + $queryParameters = "wsLiteObjectType=WsRestGetGroupsLiteRequest" + . "&groupName={$groupName}" + . "&queryFilterType=FIND_BY_GROUP_NAME_EXACT" + . "&actAsSubjectId={$queryData['userId']}"; + } + $connectionUrl .= $queryParameters; + + try { + $results = $this->http->sendRequest('GET', $connectionUrl); + CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + + return $results['WsGetGroupsLiteResult']['wsGroups'] ?? []; } /** @@ -289,12 +438,12 @@ public function getMembersInGroup(array $queryData) { $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($usersToShow, JSON_THROW_ON_ERROR)); - + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); // Parse out relevant records to send front end if(isset($results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode']) - && $results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode'] == 'GROUP_NOT_FOUND') { + && $results['WsGetMembersResults']['results'][0]['resultMetadata']['resultCode'] == 'GROUP_NOT_FOUND') { return array(array( 'sourceId' => 'NoAccess', 'name' => '', @@ -313,48 +462,186 @@ public function getMembersInGroup(array $queryData) { } /** - * Add a member to a specific Grouper Group + * Gets all available Optin/OptOut groups in Grouper + * + * Returns Optin/OptOut groups that can be joined/left * * @param array $queryData Array of conditions for querying + * @return array Optin groups from Grouper + * @throws GrouperLiteWidgetException * - * @return string Requests success or not + */ + public function getOptionalGroups(array $queryData) + { + + try { + $results = $this->useMembershipUrl($queryData); + CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); + + if (isset($results['WsGetMembershipsResults']['wsGroups'])) { + return $results['WsGetMembershipsResults']['wsGroups']; + } + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + return array(); + } + + /** + * Gets all groups in Grouper where user is an admin/owner or has update privs + * + * @param array $queryData Array of conditions for querying + * @return array Array of groups from Grouper + * @throws GrouperLiteWidgetException + * + */ + public function getOwnedGroups(array $queryData) + { + $admins = array(); + $updaters = array(); + + try { + $queryData['groupType'] = 'admin'; + $resultsAdmin = $this->useMembershipUrl($queryData); + CakeLog::write('debug', __METHOD__ . '::response admins: ' . var_export($resultsAdmin, true)); + + $queryData['groupType'] = 'update'; + $resultsUpdate = $this->useMembershipUrl($queryData); + CakeLog::write('debug', __METHOD__ . '::response updates: ' . var_export($resultsUpdate, true)); + + if (isset($resultsAdmin['WsGetMembershipsResults']['wsGroups'])) { + $admins = $resultsAdmin['WsGetMembershipsResults']['wsGroups']; + } + if (isset($resultsUpdate['WsGetMembershipsResults']['wsGroups'])) { + $updaters = $resultsUpdate['WsGetMembershipsResults']['wsGroups']; + } + + return $this->removeDuplicates($admins, $updaters); + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + } + + /** + * Potential use was for creating adhoc group by a user, not associated to WG. + * + * Gets all Stems/Folders where User is admin/owner + * + * @param array $queryData Array of conditions for querying + * @return array Array of Stems/Folders from Grouper + * @throws GrouperLiteWidgetException + */ + public function getOwnedStems(array $queryData) + { + $queryData['groupType'] = 'stemAdmin'; + + try { + $results = $this->useMembershipUrl($queryData); + + if (isset($results['WsGetMembershipsResults']['wsStems']) && $results['WsGetMembershipsResults']['wsStems'] != NULL) { + return $results['WsGetMembershipsResults']['wsStems']; + } + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + return array(); + } + + /** + * For Optin groups, calls Grouper WS to join or leave a group + * + * @param array $queryData Array of conditions for querying + * + * @return bool True if join or leave successful, False if not * @throws GrouperLiteWidgetException|JsonException */ - public function addMemberToGroup(array $queryData) + public function grouperGroupLeaveOrJoin(array $queryData) { $groupName = $queryData['groupName']; - $connectionUrl = "{$this->config['fullUrl']}/groups/$groupName/members"; + $userId = $queryData['userId']; + $groupLeaveOrJoin = $queryData['LeaveJoin']; + $groupName = urlencode($groupName); + + if ($groupLeaveOrJoin == "Leave") { + $memberRequest = "WsRestDeleteMemberRequest"; + $resultResponse = 'WsDeleteMemberResults'; + $resultGroup = 'wsGroup'; + } elseif ($groupLeaveOrJoin == "Join") { + $memberRequest = "WsRestAddMemberRequest"; + $resultResponse = 'WsAddMemberResults'; + $resultGroup = 'wsGroupAssigned'; + } else { + CakeLog::write('error', __METHOD__ . ": Option of $groupLeaveOrJoin is not supported"); + throw new GrouperLiteWidgetException("Received option of $groupLeaveOrJoin which is not supported"); + } //Build request logic - $usersToAdd = array( - 'WsRestAddMemberRequest' => array( - 'subjectLookups' => array( - array('subjectId' => $queryData['addUserId']), + $groupCommand = array( + $memberRequest => array( + "actAsSubjectLookup" => array( + "subjectId" => $userId ), - 'replaceAllExisting' => 'F', - 'actAsSubjectLookup' => array( - 'subjectId' => $queryData['userId'] + "replaceAllExisting" => "F", + "subjectLookups" => array( + array("subjectId" => $userId) ) ) ); $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); + $connectionUrl = "{$this->config['fullUrl']}/groups/$groupName/members"; try { - $results = $this->http->sendRequest('PUT', $connectionUrl, json_encode($usersToAdd, JSON_THROW_ON_ERROR)); - + $results = $this->http->sendRequest('PUT', $connectionUrl, json_encode($groupCommand, JSON_THROW_ON_ERROR)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - // Parse out relevant records to send front end - if (isset($results['WsAddMemberResults']['results'][0]['resultMetadata'])) { - return $results['WsAddMemberResults']['results'][0]['resultMetadata']['resultCode']; + if (isset($results[$resultResponse][$resultGroup])) { + $groupAssigned = $results[$resultResponse][$resultGroup]['name']; + if ($groupAssigned == urldecode($groupName)) { + return true; + } } } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - return ''; + return false; + } + + /** + * Removes duplicates where the user is the owner and the updater of the group. Just one line instead of two. + * + * @param array $arrOne + * @param array $arrTwo + * @return array + */ + public function removeDuplicates(array $arrOne, array $arrTwo) + { + //Determine which array is bigger and use as base + $countOne = count($arrOne); + $countTwo = count($arrTwo); + if ($countOne >= $countTwo) { + $arrL = $arrOne; + $arrS = $arrTwo; + } else { + $arrL = $arrTwo; + $arrS = $arrOne; + } + + foreach ($arrL as $large) { + foreach ($arrS as $key => $val) { + if ($large['uuid'] == $val['uuid']) { + unset($arrS[$key]); + } + } + } + + return array_merge_recursive($arrL, $arrS); } /** @@ -388,7 +675,7 @@ public function removeMemberToGroup(array $queryData) try { $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($userToDelete, JSON_THROW_ON_ERROR)); - + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); // Parse out relevant records to send front end @@ -425,8 +712,8 @@ private function useMembershipUrl(array $queryData) } elseif ($groupType == 'admin' || $groupType == 'update' || $groupType == 'stemAdmin') { $subjectId = $userId; } else { - CakeLog::write('error', __METHOD__ . ": Option of $groupType is not supported"); - throw new GrouperLiteWidgetException("Option of $groupType is not supported"); + CakeLog::write('error', __METHOD__ . ": Option of {$groupType} is not supported"); + throw new GrouperLiteWidgetException("Option of {$groupType} is not supported"); } //Build request logic @@ -436,7 +723,7 @@ private function useMembershipUrl(array $queryData) if ($groupType == 'optins' || $groupType == 'optouts') { - //Build request logic, 2 subjectId's, second is for when user in "Secret" Optin/Optout Group + // Build request logic, 2 subjectId's, second is for when user in "Secret" Optin/Optout Group $groupsToShow['WsRestGetMembershipsRequest']['wsSubjectLookups'][1]['subjectId'] = $userId; } @@ -445,6 +732,7 @@ private function useMembershipUrl(array $queryData) try { $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupsToShow)); + CakeLog::write('debug', __METHOD__ . '::connection url: ' . $connectionUrl); CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); @@ -453,283 +741,4 @@ private function useMembershipUrl(array $queryData) return $results; } - - /** - * Method used to CREATE a new Group in Grouper via the Working Group Template method. - * - * @param array $queryData Array of conditions and data adding new Grouper Group - * @return array status and error message, if applicable - * @throws GrouperLiteWidgetException - * - */ - public function createGroupWithTemplate(array $queryData) - { - //Currently, only supporting create group, need to test if update a group will work! - - $data = $queryData['data']; - $userId = $queryData['userId']; - - //need to take out SympaDomain if domain not being created. - if ($data['gsh_input_isSympa'] == 'false') { - unset($data['gsh_input_sympaDomain'], $data['gsh_input_isSympaModerated']); - } - - //Build request logic - $inputFields = array(); - foreach ($data as $key => $value) { - $inputFields[] = array('name' => $key, 'value' => $value); - } - - $groupToSave = array( - 'WsRestGshTemplateExecRequest' => array( - 'gshTemplateActAsSubjectLookup' => array( - 'subjectSourceId' => 'ldap', - 'subjectId' => $userId - ), - 'ownerStemLookup' => array( - 'stemName' => 'ref:incommon-collab' - ), - 'ownerType' => 'stem', - 'configId' => 'createNewWorkingGroup', - 'inputs' => $inputFields - ) - ); - - $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); - $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec"; - - $status = true; - $message = ''; - try { - $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave)); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'])) { - $status = false; - $message = 'An error occurred, please try again later.'; - - CakeLog::write('error', __METHOD__ . ': An error occurred in creating your Working Group!'); - - if (isset($results['WsGshTemplateExecResult']['gshValidationLines'][0]['validationText']) - && stripos( - $results['WsGshTemplateExecResult']['gshValidationLines'][0]['validationText'], - 'already exist', - 0 ) !== false) - { - $message = 'There already is a WG named: ' . $data['gsh_input_workingGroupExtension']; - } elseif (stripos($results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], 'EXCEPTION', 0) !== false) { - throw new GrouperLiteWidgetException('An error occurred in creating your Working Group!'); - } - } - - return compact('status', 'message'); - } - - /** - * - * Method used to DELETE a Group in Grouper via the Template method. - * - * @param array $queryData Array of conditions and data adding new Grouper Group - * - * @return bool True if deleted successfully - * @throws GrouperLiteWidgetException|JsonException - * - */ - public function deleteGroupWithTemplate(array $queryData) - { - - $workingGroupExt = $queryData['workingGroupExt']; - $userId = $queryData['userId']; - - $groupToDelete = array( - 'WsRestGshTemplateExecRequest' => array( - 'gshTemplateActAsSubjectLookup' => array( - 'subjectSourceId' => 'ldap', - 'subjectId' => $userId - ), - 'ownerStemLookup' => array( - 'stemName' => 'ref:incommon-collab' - ), - 'ownerType' => 'stem', - 'configId' => 'createWorkingGroup', - 'inputs' => array( - array( - 'name' => 'gsh_input_workingGroupExtension', - 'value' => $workingGroupExt - ) - ) - ) - ); - - $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); - $connectionUrl = "{$this->config['fullUrl']}/gshTemplateExec"; - - try { - $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToDelete, JSON_THROW_ON_ERROR)); - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - if (isset($results['WsGshTemplateExecResult']['resultMetadata']['resultCode']) && stripos( - $results['WsGshTemplateExecResult']['resultMetadata']['resultCode'], - 'SUCCESS', - 0 - ) !== false) { - return true; - } - - return false; - } - - /** - * - * For creating/updating Ad-Hoc groups not using the Grouper Template format - * - * Create or Update a Group where User is Admin/Owner - * - * @param array $queryData Array of conditions and data adding new Grouper Group - * - * @return bool True if added or updated successful - * @throws GrouperLiteWidgetException|JsonException - */ - public function createUpdateGroup(array $queryData) - { - - $groupName = htmlentities($queryData['name']); - $stemName = htmlentities($queryData['stem']); - $userId = $queryData['userId']; - $groupDescription = htmlentities($queryData['description']); - - //Group name may be in "friendly" format, so need to do some small conversions before saving. - $newGroupName = ucfirst(strtolower($groupName)); - $newGroupName = str_replace(' ', '', $newGroupName); - - $newGroupToSave = "$stemName:$newGroupName"; - - //Build request logic - $groupToSave = array( - 'WsRestGroupSaveRequest' => array( - 'actAsSubjectLookup' => array( - 'subjectId' => $userId - ), - 'wsGroupToSaves' => array( - array( - 'wsGroup' => array( - 'description' => $groupDescription, - 'name' => $newGroupToSave, - 'displayExtension' => $groupName - ), - 'wsGroupLookup' => array( - 'groupName' => $groupDescription - ) - ) - ) - ) - ); - $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); - $connectionUrl = "{$this->config['fullUrl']}/groups"; - - try { - $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($groupToSave, JSON_THROW_ON_ERROR)); - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - if (isset($results['WsGroupSaveResults']['results']['resultMetadata']['resultCode']) - && stripos( - $results['WsGroupSaveResults']['results']['resultMetadata']['resultCode'], - 'Success', - 0 - ) !== false) { - return true; - } - - return false; - } - - /** - * Used to be used for Grouper group info page. - * - * Grouper Group information plus a listing of attributes in Grouper for that given Group - * - * @param array $queryData Array of conditions for querying - * - * @return array Record of Grouper attributes for given GroupName - * @throws GrouperLiteWidgetException|JsonException - */ - public function getGrouperGroupInfo(array $queryData) - { - $groupName = $queryData['groupName']; - $groupInfo = array(); - - //Build request logic - $stemToFind = array( - 'WsRestGetAttributeAssignmentsRequest' => array( - 'attributeAssignType' => 'group', - 'includeAssignmentsOnAssignments' => 'T', - 'wsOwnerGroupLookups' => array( - array( - 'groupName' => $groupName, - ) - ) - ) - ); - $this->http->setHeader(array('Content-Type' => 'application/json', 'Accept' => 'application/json')); - $connectionUrl = "{$this->config['fullUrl']}/attributeAssignments"; - - try { - $results = $this->http->sendRequest('POST', $connectionUrl, json_encode($stemToFind, JSON_THROW_ON_ERROR)); - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($results, true)); - - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - //Get the group information - if (isset($results['WsGetAttributeAssignmentsResults']['wsGroups'])) { - $groupInfo = $results['WsGetAttributeAssignmentsResults']['wsGroups']; - } - - //Now get the Group Attributes and add them to group - $groupInfo[0]['attributes'] = $results['WsGetAttributeAssignmentsResults']['wsAttributeAssigns'] ?? array(); - - return $groupInfo; - } - - /** - * ======================== NOT BEING USED ======================== - * - * Potential use was for creating adhoc group by a user, not associated to WG. - * - * Gets all Stems/Folders where User is admin/owner - * - * @param array $queryData Array of conditions for querying - * @return array Array of Stems/Folders from Grouper - * @throws GrouperLiteWidgetException - */ - public function getOwnedStems(array $queryData) - { - $queryData['groupType'] = 'stemAdmin'; - - try { - $results = $this->useMembershipUrl($queryData); - - if (isset($results['WsGetMembershipsResults']['wsStems']) && $results['WsGetMembershipsResults']['wsStems'] != NULL) { - return $results['WsGetMembershipsResults']['wsStems']; - } - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - return array(); - } - } diff --git a/Lib/enum.php b/Lib/enum.php index dd4aaa0..172319a 100644 --- a/Lib/enum.php +++ b/Lib/enum.php @@ -6,4 +6,12 @@ class ErrorsEnum { const NotAdded = array('status' => 'ERROR', 'message' => 'NOT ADDED'); const Error = array('status' => 'ERROR', 'message' => 'ERROR'); const NoAccess = array('status' => 'ERROR', 'message' => 'NO ACCESS'); +} + +class GrouperSpecialGroups { + /** Group whose members can create Groups via Template process */ + const TEMPLATE_CREATION_GROUP = 'ref:workinggroupadmins'; + + /** Group whose members cannot see Grouper button, even if admin */ + const GROUPER_VISIBLE_GROUP = 'app:comanage:LiteUI:grouperVisible'; } \ No newline at end of file diff --git a/Lib/lang.php b/Lib/lang.php index 8c26b24..34bad66 100644 --- a/Lib/lang.php +++ b/Lib/lang.php @@ -17,6 +17,8 @@ 'pl.grouperlite.config.default-collapse-subscript' => '', 'pl.grouperlite.config.identifier' => 'Identifier Type', 'pl.grouperlite.config.identifier.desc' => 'Identifier eligible to make requests to Grouper', + 'pl.grouperlite.config.act-as-grp-name' => 'Act-As Group Name', + 'pl.grouperlite.config.act-as-grp-name.desc' => 'Members of the Group are authorized to Act-As another User.', 'pl.grouperlite.crumb.root' => 'Grouper', 'pl.grouperlite.nav.groups-can-join' => 'Groups I can join', @@ -30,6 +32,7 @@ 'pl.grouperlite.nav.create-email' => 'Create email list', 'pl.grouperlite.nav.emaillists' => 'Email Lists', 'pl.grouperlite.nav.groups' => 'Groups', + 'pl.grouperlite.nav.users-presided' => 'Users I manage', 'pl.grouperlite.dashboard.heading.groups' => 'Groups', 'pl.grouperlite.dashboard.heading.email-lists' => 'Email lists', diff --git a/Model/CoGrouperLiteWidget.php b/Model/CoGrouperLiteWidget.php index 2f57fd2..9266390 100644 --- a/Model/CoGrouperLiteWidget.php +++ b/Model/CoGrouperLiteWidget.php @@ -119,6 +119,11 @@ class CoGrouperLiteWidget extends CoDashboardWidgetBackend { 'allowEmpty' => false ) ), + 'act_as_grp_name' => array( + 'rule' => '/.*/', + 'required' => false, + 'allowEmpty' => true + ) ); /** diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index 3baa9fd..599c19d 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -35,9 +35,11 @@ */ class GrouperGroup extends GrouperLiteWidgetAppModel { + // XXX According to the documentation (https://spaces.at.internet2.edu/display/Grouper/UI+Terminology) + // the displayExtension is the Friendly Name of the Group. /** @var string $name used by CakePHP for locating model */ - public $name = "GrouperGroup"; + public $name = 'GrouperGroup'; /** @var GrouperApiAccess $grouperAPI */ private $grouperAPI = null; @@ -63,7 +65,7 @@ class GrouperGroup extends GrouperLiteWidgetAppModel 'ref:internet2-collab' ); - //Stem for email groups, only email groups using this stem are viewable + // Stem for email groups, only email groups using this stem are viewable private $emailStem = 'app:sympa'; @@ -77,6 +79,7 @@ class GrouperGroup extends GrouperLiteWidgetAppModel * * @see GrouperGroup::resetUserOwner() * + * @since COmanage Registry v4.4.0 */ public function isUserOwner(string $userId, array $cfg) { @@ -102,44 +105,43 @@ public function isUserOwner(string $userId, array $cfg) * @return String T or F * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function isGrouperVisible(string $userId, array $cfg) { + $member = 'F'; + $this->initApi($cfg); + $args = []; + $args['userId'] = $userId; try { - $args = array(); - $args['userId'] = $userId; - - $memberOfGroups = $this->grouperAPI->getGrouperMemberOfGroups($args); + $memberOfGroups = $this->grouperAPI->getGrouperMemberOfGroups($args, + GrouperSpecialGroups::GROUPER_VISIBLE_GROUP); - //now cycle through and see if part of correct group to be able to use template - $member = 'F'; foreach ($memberOfGroups as $memberOfGroup) { - if ($memberOfGroup['name'] == $this->grouperVisibleGroup) { + if ($memberOfGroup['name'] == GrouperSpecialGroups::GROUPER_VISIBLE_GROUP) { $member = 'T'; break; } } - - return $member; } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } + + return $member; } /** - * Used to instantiate API class + * Grouper API Instance Singleton * - * Tried to use the constructor for GrouperGroup to set this but it was triggering the GrouperAPIAccess class - * to instantiate before the Controller was able populate the connection settings from the DB into the Session, - * so error was being thrown. - * Now will call this before each function call to verify set. + * @throws GrouperLiteWidgetException + * + * @since COmanage Registry v4.4.0 */ - private function initApi(array $cfg) - { - if ($this->grouperAPI == null) { + private function initApi(array $cfg) { + if ($this->grouperAPI === null) { $this->grouperAPI = new GrouperApiAccess($cfg); } } @@ -152,34 +154,25 @@ private function initApi(array $cfg) * @return array Records of Groups from Grouper that the User belongs to * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function filteredMemberOfGroups(array $conditions, array $cfg) { $this->initApi($cfg); try { - $memberOfGroups = $this->memberOfGroups($conditions); $conditions['groupType'] = 'optouts'; // Determine which groups can be left by user, if want. $optOutGroups = $this->grouperAPI->getOptionalGroups($conditions); + $optOutGroupsNames = Hash::combine($optOutGroups, '{n}.name', '{n}.displayExtension'); foreach ($memberOfGroups as &$memberOfGroup) { - $memberOfGroup['optOut'] = false; - foreach ($optOutGroups as $key => $value) { - if ($value['name'] == $memberOfGroup['name']) { - //Match! - $memberOfGroup['optOut'] = true; - //Remove Optin group since already found and now less loops - unset($optOutGroups[$key]); - break; - } - } + $memberOfGroup['optOut'] = isset($optOutGroupsNames[$memberOfGroup['name']]); } - $groupResults = array_values($memberOfGroups); - return $this->getFriendlyWorkingGroupName($groupResults, 'member'); + return $this->getFriendlyWorkingGroupName($memberOfGroups, 'member'); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); @@ -194,13 +187,12 @@ public function filteredMemberOfGroups(array $conditions, array $cfg) * @return array Records of Groups from Grouper that the User belongs to * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ private function memberOfGroups(array $conditions) { - try { return $this->grouperAPI->getGrouperMemberOfGroups($conditions); - } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -218,6 +210,7 @@ private function memberOfGroups(array $conditions) * @return bool True|False * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function leaveGroup(string $userId, string $groupName, array $cfg) { @@ -245,6 +238,7 @@ public function leaveGroup(string $userId, string $groupName, array $cfg) * @return bool True|False * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function joinGroup(string $userId, string $groupName, array $cfg) { @@ -271,15 +265,14 @@ public function joinGroup(string $userId, string $groupName, array $cfg) * @return array * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function ownerGroups(array $conditions, array $cfg) { $this->initApi($cfg); try { - $resultSet = $this->grouperAPI->getOwnedGroups($conditions); - return $this->getFriendlyName(array_values($resultSet)); - + return $this->grouperAPI->getOwnedGroups($conditions); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -296,6 +289,7 @@ public function ownerGroups(array $conditions, array $cfg) * @return array Listing of members in requested Grouper Group * @throws GrouperLiteWidgetException Captured in Controller * + * @since COmanage Registry v4.4.0 */ public function membersInGroup(array $conditions, string $userId, array $cfg) { @@ -333,6 +327,7 @@ public function membersInGroup(array $conditions, string $userId, array $cfg) * @return string success of Request * @throws GrouperLiteWidgetException Captured in Controller * + * @since COmanage Registry v4.4.0 */ public function addMemberToGroup(array $conditions, string $userId, array $cfg) { @@ -359,6 +354,7 @@ public function addMemberToGroup(array $conditions, string $userId, array $cfg) * @return string success of Request * @throws GrouperLiteWidgetException Captured in Controller * + * @since COmanage Registry v4.4.0 */ public function removeMemberToGroup(array $conditions, string $userId, array $cfg) { @@ -383,31 +379,23 @@ public function removeMemberToGroup(array $conditions, string $userId, array $cf * @param array $conditions Listing of conditions for display of records, including UserId * @return array Listing of Optin groups available in Grouper * @throws GrouperLiteWidgetException Captured in Controller + * + * @since COmanage Registry v4.4.0 */ public function optinGroups(array $conditions, array $cfg) { $this->initApi($cfg); + $conditions['groupType'] = 'optins'; try { - $conditions['groupType'] = 'optins'; - + // Groups the user can join or leave $joinOrLeave = $this->grouperAPI->getOptionalGroups($conditions); + // Groups the user is a member of $userGroups = $this->memberOfGroups($conditions); - - //See if Optin group match any of the groups user already belongs to. - foreach ($joinOrLeave as $key => $value) { - foreach ($userGroups as $userGroup) { - if ($value['name'] == $userGroup['name']) { - //Match!! - //Removing from array since already a member of. - unset($joinOrLeave[$key]); - break; - } - } - } - - return $this->getFriendlyName(array_values($joinOrLeave)); - + // Extract the names of the Groups the user is a member of + $userGroupsNames = Hash::extract($userGroups, '{n}.name'); + // Return the groups the user can join and is not a member of + return array_filter($joinOrLeave, static fn($value) => !in_array($value['name'], $userGroupsNames)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -420,32 +408,34 @@ public function optinGroups(array $conditions, array $cfg) * @param string $userId Id of User * @return string T for True and F for False * @throws GrouperLiteWidgetException + * + * @since COmanage Registry v4.4.0 */ public function isTemplateUser(string $userId, array $cfg) { + $member = 'F'; + $this->initApi($cfg); + $args = array(); + $args['userId'] = $userId; try { - $args = array(); - $args['userId'] = $userId; - - $memberOfGroups = $this->grouperAPI->getGrouperMemberOfGroups($args); - + $memberOfGroups = $this->grouperAPI->getGrouperMemberOfGroups($args, + GrouperSpecialGroups::TEMPLATE_CREATION_GROUP); //now cycle through and see if part of correct group to be able to use template - $member = 'F'; foreach ($memberOfGroups as $memberOfGroup) { - if ($memberOfGroup['name'] == $this->templateCreationGroup) { + if ($memberOfGroup['name'] == GrouperSpecialGroups::TEMPLATE_CREATION_GROUP) { $member = 'T'; break; } } - return $member; } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } + return $member; } /** @@ -456,6 +446,7 @@ public function isTemplateUser(string $userId, array $cfg) * @return array status and error message, if applicable * @throws GrouperLiteWidgetException * + * @since COmanage Registry v4.4.0 */ public function createGroupWithTemplate(string $userId, array $groupData, array $cfg) { @@ -501,6 +492,8 @@ public function createGroupWithTemplate(string $userId, array $groupData, array * @param array $conditions Listing of conditions for display of records, including UserId * @return array Records that meet search criteria * @throws Exception Captured in Controller + + * @since COmanage Registry v4.4.0 */ public function getSearchedGroups(array $conditions, array $cfg) { @@ -540,7 +533,7 @@ public function getSearchedGroups(array $conditions, array $cfg) } /** - * Return array of Working Groups for display on coManage site. + * Return array of Working Groups for display on COManage site. * Logic is for each WG to have one key=>value of main WG name, then array of all associated * Groups. * @@ -549,10 +542,10 @@ public function getSearchedGroups(array $conditions, array $cfg) * * @param array $groups Listing of Groups * @return array Listing of Groups in WG format for display - * + + * @since COmanage Registry v4.4.0 */ - private function getFriendlyWorkingGroupName(array $groups, $method) - { + private function getFriendlyWorkingGroupName(array $groups, string $method) { $arrayIndex = 0; $workingGroups = array(); @@ -628,10 +621,6 @@ private function getFriendlyWorkingGroupName(array $groups, $method) } $tempGroup['WGApp'] = $appName; if ($method == 'member') { - // Screening out groups owned since this page show memberships not ownership! -// if ($tempGroup['WGRole'] !== 'admin' && $tempGroup['WGRole'] !== 'owner' && !$mainGroup ) { -// $workingGroups[] = $tempGroup; -// } if(!$mainGroup) { $workingGroups[] = $tempGroup; unset($groups[$arrayIndex]); @@ -673,7 +662,7 @@ private function getFriendlyWorkingGroupName(array $groups, $method) } } - $friendlyGroups = $this->getFriendlyName(array_values($groups)); + $friendlyGroups = array_values($groups); //Now need to add the groups back together for one set foreach ($friendlyGroups as $friendlyGroup) { @@ -682,26 +671,4 @@ private function getFriendlyWorkingGroupName(array $groups, $method) return $finalWorkingGroups; } - - - /** - * Determine if result set contains friendly name, if so add as a new attribute in result set - * NOTE: Used by Ad-Hoc groups only, since WG are listed under a Grouping - * - * @param array $groups Current result set of Groups - * @return array Same result set with added param for friendly name - * - */ - private function getFriendlyName(array $groups) - { - - //According to Jira ICPCO-200, will only display the last section of the stem in UI and then hover over for rest. - foreach ($groups as &$group) { - $friendlySections = explode(':', $group['displayName']); - $group['friendlyName'] = end($friendlySections); - } - - return $groups; - } - } diff --git a/View/CoGrouperLiteWidgets/fields.inc b/View/CoGrouperLiteWidgets/fields.inc index fea4061..1d96d86 100644 --- a/View/CoGrouperLiteWidgets/fields.inc +++ b/View/CoGrouperLiteWidgets/fields.inc @@ -65,7 +65,9 @@ $this->Html->addCrumb(_txt('ct.co_dashboard_widgets.1'), $args); $crumbTxt = _txt('op.' . $this->action . '-a', array(filter_var($co_grouper_lite_widgets[0]['CoDashboardWidget']['description'], FILTER_SANITIZE_SPECIAL_CHARS))); $this->Html->addCrumb($crumbTxt); -print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)) . "\n"; +if(isset($vv_dwid)) { + print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)) . "\n"; +} ?>
    @@ -73,6 +75,7 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->label('identifier_type', _txt('pl.grouperlite.config.identifier')) : _txt('pl.grouperlite.config.identifier')); ?> + *
    @@ -91,8 +94,8 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if($this->Form->isFieldError('identifier_type')) { print $this->Form->error('identifier_type'); } - } else { - print _txt('pl.grouperlite.config.identifier', null, filter_var($vv_config['identifier_type'],FILTER_SANITIZE_SPECIAL_CHARS)); + } elseif (!empty($vv_config['CoGrouperLiteWidget']['identifier_type'])) { + print filter_var($vv_available_types[ $vv_config['CoGrouperLiteWidget']['identifier_type'] ],FILTER_SANITIZE_SPECIAL_CHARS); } ?>
    @@ -110,7 +113,7 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('grouper_url')) { print $this->Form->error('grouper_url'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['grouper_url'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['grouper_url'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['grouper_url'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['grouper_url'], FILTER_SANITIZE_SPECIAL_CHARS); } ?>
    @@ -138,7 +139,7 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('conn_url')) { print $this->Form->error('conn_url'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['conn_url'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['conn_url'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['conn_url'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['conn_url'], FILTER_SANITIZE_SPECIAL_CHARS); } ?>
    @@ -159,14 +158,14 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
  • - Form->label('conn_ver', _txt('pl.grouperlite.config.grouper-ws-version')) : _txt('pl.grouperlite.config.grouper-version')); ?> + Form->label('conn_ver', _txt('pl.grouperlite.config.grouper-ws-version')) : _txt('pl.grouperlite.config.grouper-ws-version')); ?> *
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('conn_ver')) { print $this->Form->error('conn_ver'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['conn_ver'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['conn_ver'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['conn_ver'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['conn_ver'], FILTER_SANITIZE_SPECIAL_CHARS); } ?>
    @@ -187,14 +184,14 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
  • - Form->label('conn_user', _txt('pl.grouperlite.config.grouper-ws-un')) : _txt('pl.grouperlite.config.grouper-un')); ?> + Form->label('conn_user', _txt('pl.grouperlite.config.grouper-ws-un')) : _txt('pl.grouperlite.config.grouper-ws-un')); ?> *
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('conn_user')) { print $this->Form->error('conn_user'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['conn_user'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['conn_user'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['conn_user'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['conn_user'], FILTER_SANITIZE_SPECIAL_CHARS); } ?>
    @@ -215,14 +210,14 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
  • - Form->label('conn_pass', _txt('pl.grouperlite.config.grouper-ws-pw')) : _txt('pl.grouperlite.config.grouper-pw')); ?> + Form->label('conn_pass', _txt('pl.grouperlite.config.grouper-ws-pw')) : _txt('pl.grouperlite.config.grouper-ws-pw')); ?> *
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('conn_pass')) { print $this->Form->error('conn_pass'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['conn_pass'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['conn_pass'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['conn_pass'])) { + // XXX We do not print the password + print ''; } ?>
  • -
  • @@ -250,7 +243,7 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('adhoc_heading')) { print $this->Form->error('adhoc_heading'); } - } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['adhoc_heading'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['adhoc_heading'], FILTER_SANITIZE_SPECIAL_CHARS); - } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['adhoc_heading'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['adhoc_heading'], FILTER_SANITIZE_SPECIAL_CHARS); } ?>
  • -
  • @@ -278,7 +268,7 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) if ($this->Form->isFieldError('wg_heading')) { print $this->Form->error('wg_heading'); } + } elseif (!empty($vv_config['CoGrouperLiteWidget']['wg_heading'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['wg_heading'], FILTER_SANITIZE_SPECIAL_CHARS); + } + ?> +
    +
  • +
  • +
    +
    + Form->label('act_as_grp_name', _txt('pl.grouperlite.config.act-as-grp-name')) : _txt( + 'pl.grouperlite.config.act-as-grp-name' + )) ?> +
    +
    + +
    +
    +
    + Form->input('act_as_grp_name', $attrs); + + if ($this->Form->isFieldError('act_as_grp_name')) { + print $this->Form->error('act_as_grp_name'); + } } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['wg_heading'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['wg_heading'], FILTER_SANITIZE_SPECIAL_CHARS); + if (!empty($vv_config['CoGrouperLiteWidget']['act_as_grp_name'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['act_as_grp_name'], FILTER_SANITIZE_SPECIAL_CHARS); } } ?>
  • -
  • -
    Form->label('default_collapse', _txt('pl.grouperlite.config.default-collapse')) : _txt('pl.grouperlite.config.default-collapse')); ?> @@ -306,13 +324,12 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid)
    Form->radio('default_collapse', array( 'collapsed' => 'True', 'expanded' => 'False' ), array( - 'value' => $vv_config["CoGrouperLiteWidget"]['default_collapse'], + 'value' => $vv_config['CoGrouperLiteWidget']['default_collapse'], 'legend' => false, 'separator' => '  ' )); @@ -321,8 +338,8 @@ print $this->Form->hidden('co_dashboard_widget_id', array('default' => $vv_dwid) print $this->Form->error('default_collapse'); } } else { - if (!empty($vv_config["CoGrouperLiteWidget"]['default_collapse'])) { - print filter_var($vv_config["CoGrouperLiteWidget"]['default_collapse'], FILTER_SANITIZE_SPECIAL_CHARS); + if (!empty($vv_config['CoGrouperLiteWidget']['default_collapse'])) { + print filter_var($vv_config['CoGrouperLiteWidget']['default_collapse'], FILTER_SANITIZE_SPECIAL_CHARS); } } ?> diff --git a/View/CoGrouperLiteWidgets/view.ctp b/View/CoGrouperLiteWidgets/view.ctp new file mode 100644 index 0000000..5ccb31d --- /dev/null +++ b/View/CoGrouperLiteWidgets/view.ctp @@ -0,0 +1,118 @@ +name; + $req = Inflector::singularize($model); + $modelpl = Inflector::tableize($req); + $modelu = Inflector::underscore($req); + + // Get a pointer to our data + $d = $$modelpl; + + // Add page title + $params = array(); + $params['title'] = $title_for_layout; + + // Add top links + $params['topLinks'] = array(); + + // If user has edit permission, offer an edit button in the sidebar + if(!empty($permissions['edit']) && $permissions['edit']) { + + // special case co_people + $editAction = 'edit'; + if ($modelpl == 'co_people') { + $editAction = 'canvas'; + } + + $a = array('controller' => $modelpl, 'action' => $editAction, $d[0][$req]['id']); + + if(empty($d[0]['OrgIdentity']['OrgIdentitySourceRecord']['id']) + && empty($d[0][$req]['source_'.$modelu.'_id'])) { + // Add edit button to the top links, except for attributes attached to + // an Org Identity that came from an Org Identity Source. + $params['topLinks'][] = $this->Html->link( + _txt('op.edit'), + $a, + array('class' => 'editbutton') + ); + } + } + + // Add locally configured page buttons + if(!empty($this->plugin)) { + if(file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc")) { + include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc"); + } elseif(file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc")) { + include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/buttons.inc"); + } + } else { + if(file_exists(APP . "View/" . $model . "/buttons.inc")) { + include(APP . "View/" . $model . "/buttons.inc"); + } + } + + print $this->element("pageTitleAndButtons", $params); + if(file_exists(APP . "View/" . $model . "/tabs.inc")) { + include(APP . "View/" . $model . "/tabs.inc"); + } +?> + +
    + info +
    + +
    +
    +
    + +
    + info +
    + +
    +
    +
    + +'; + if(!empty($this->plugin)) { + if(file_exists(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc")) { + include(APP . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc"); + } elseif(file_exists(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc")) { + include(LOCAL . "Plugin/" . $this->plugin . "/View/" . $model . "/fields.inc"); + } + } else { + include(APP . "View/" . $model . "/fields.inc"); + } + print '
    '; + +?> diff --git a/View/Elements/Components/navigation-groups.ctp b/View/Elements/Components/navigation-groups.ctp index f437f4c..1163f8a 100644 --- a/View/Elements/Components/navigation-groups.ctp +++ b/View/Elements/Components/navigation-groups.ctp @@ -36,6 +36,18 @@ ) ?>
    +
  • diff --git a/View/GrouperGroups/groupinfo.ctp b/View/GrouperGroups/groupinfo.ctp index 9e74792..aee6008 100644 --- a/View/GrouperGroups/groupinfo.ctp +++ b/View/GrouperGroups/groupinfo.ctp @@ -12,7 +12,7 @@ $attrUrlBase = $baseUrl . $path . $attrOperation;
    -

    +

    diff --git a/webroot/js/groups-table.js b/webroot/js/groups-table.js index f6581f2..310b63e 100644 --- a/webroot/js/groups-table.js +++ b/webroot/js/groups-table.js @@ -20,7 +20,7 @@ export default { - {{ group.friendlyName || 'No Name' }} + {{ group.displayExtension || 'No Name' }} — @@ -53,7 +53,7 @@ export default { class="btn btn-grouper btn-block btn-primary btn-sm m-1 text-nowrap members-btn" @click="$emit('showSubscribers', group)" :data-id="encodeURIComponent(group.name)" - :data-name="group.friendlyName">{{ txt.members }} + :data-name="group.displayExtension">{{ txt.members }} {{ txt.grouper }}   @@ -119,7 +119,7 @@ export default { /*