diff --git a/Controller/GrouperGroupsController.php b/Controller/GrouperGroupsController.php index 21dfedd..659496b 100644 --- a/Controller/GrouperGroupsController.php +++ b/Controller/GrouperGroupsController.php @@ -155,20 +155,14 @@ public function groupSubscribers(): void $groupName = urldecode($this->request->query['groupname']); $subscribers = 0; - //Need to see if coming from AdHoc or from a WG (Working Group) - $groupNameFormatted = strpos($groupName, ':') === false ? 'ref:incommon-collab:' . $groupName . ':users' - : $groupName; - //Set initial - $scope = [ - 'groupName' => $groupNameFormatted - ]; +// //Need to see if coming from AdHoc or from a WG (Working Group) +// $groupNameFormatted = strpos($groupName, ':') === false ? 'ref:incommon-collab:' . $groupName . ':users' +// : $groupName; try { - $subscribers = $this->GrouperGroup->membersInGroup($scope, - $this->userId, - $this->CoGrouperLiteWidget->getConfig()); - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($subscribers, true)); - + $subscribers = $this->GrouperGroup->getGrouperGroupMembers($this->userId, + $groupName, + $this->CoGrouperLiteWidget->getConfig()); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); @@ -198,15 +192,14 @@ public function addSubscriber(): void $resultAdd = false; //Need to see if coming from AdHoc or from a WG (Working Group) - $groupNameFormatted = strpos($groupName, ':') === false ? 'ref:incommon-collab:' . $groupName . ':users' - : $groupName; +// $groupNameFormatted = strpos($groupName, ':') === false ? 'ref:incommon-collab:' . $groupName . ':users' +// : $groupName; try { $resultAdd = $this->GrouperGroup->addGrouperGroupMember($this->userId, - $groupNameFormatted, + $groupName, $addUserId, $this->CoGrouperLiteWidget->getConfig()); - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($resultAdd, true)); } catch (Exception $e) { // $this->restResponse(HttpStatusCodesEnum::HTTP_UNAUTHORIZED, ErrorsEnum::NotAdded); CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); @@ -219,109 +212,27 @@ public function addSubscriber(): void } /** - * The majority of this code is copied from the find() function in the CoPeopleController that was added by - * Arlen. Eventually this will become a component, so for now just tweaking the code needed for the Grouper plugin - * to work. + * Perform a "keyword" search for CO People, sort of like the CO Dashboard + * cross controller search, but intended specifically for "people finder" + * search while you type API calls. * + * @since COmanage Registry v3.3.0 */ - public function findSubscriber() { - $findUserId = urldecode($this->request->query['term']); - $co = urldecode($this->request->query['co']); - - $mode = urldecode($this->request->query['mode']); - - // jquery Autocomplete sends the search as url?term=foo - $coPersonIds = array(); - if(!empty($findUserId)) { - // Leverage model specific keyword search - // Note EmailAddress and Identifier don't support substring search - foreach(array('Name', 'EmailAddress', 'Identifier') as $m) { - $hits = $this->CoPerson->$m->search($co, $findUserId, 25); + public function findSubscriber(): void + { + $this->request->allowMethod('ajax'); + $this->layout = 'ajax'; - foreach($hits as $hit) { - if ($hit['CoPerson']['status'] == 'A') { - $coPersonIds[] = $hit['CoPerson']['id']; - } - } - // $coPersonIds = array_merge($coPersonIds, Hash::extract($hits, '{n}.CoPerson.id')); - } + // What search mode should we use? + if(empty($this->request->query['mode'])) { + $this->Api->restResultHeader(HttpStatusCodesEnum::HTTP_BAD_REQUEST, 'Mode Not Specified'); + return; } - $coPersonIds = array_unique($coPersonIds); - - // Look up additional information to provide hints as to which person is which. - // We only do this when there are relatively small numbers of results to - // avoid making a bunch of database queries early in the search. - - $matches = array(); - - if(count($coPersonIds) > 100) { - // We don't return large sets to avoid slow performance - - $this->response->type('json'); - $this->response->statusCode(401); - $this->response->body(json_encode(array('status' => 'ERROR', 'message' => 'Too Many Results'))); - $this->response->send(); - $matches = ''; - } else { - $people = $this->CoPerson->filterPicker($co, $coPersonIds, $mode); - $pickerEmailType = $this->Co->CoSetting->getPersonPickerEmailType($co); - $pickerIdentifierType = $this->Co->CoSetting->getPersonPickerIdentifierType($co); - $pickerDisplayTypes = $this->Co->CoSetting->getPersonPickerDisplayTypes($co); - - foreach($people as $p) { - $label = generateCn($p['Name'][0]); - $idArr = $p['Identifier']; - $emailArr = $p['EmailAddress']; - $email = ''; - $emailLabel = ''; - $id = ''; - $idLabel = ''; - - // Iterate over the email array - if(!empty($emailArr) && !empty($pickerEmailType)) { - if(!empty($pickerDisplayTypes)) { - $emailLabel = _txt('fd.extended_type.generic.label', array(_txt('fd.email_address.mail'), $pickerEmailType)); - } - else { - $emailLabel = _txt('fd.email_address.mail') . ': '; - } - foreach($emailArr as $e) { - if($e['type'] == $pickerEmailType) { - $email = $e['mail']; - break; - } - } - } - - // Set the identifier for display (and limit it to 30 characters max) - if(!empty($idArr[0]['identifier']) && !empty($pickerIdentifierType)) { - if(!empty($pickerDisplayTypes)) { - $idLabel = _txt('fd.extended_type.generic.label', array(_txt('fd.identifier.identifier'), $pickerIdentifierType)); - } - else { - $idLabel = _txt('fd.identifier.identifier') . ': '; - } - foreach($idArr as $i) { - if($i['type'] == $pickerIdentifierType) { - //$id = mb_strimwidth($i['identifier'], 0, 30, '...'); - $id = $i['identifier']; - break; - } - } - } - - $matches[] = array( - 'value' => $p['CoPerson']['id'], - 'label' => $label, - 'email' => $email, - 'emailLabel' => $emailLabel, - 'identifier' => $id, - 'identifierLabel' => $idLabel - ); - } - } + $matches = $this->GrouperGroup->findForPicker($this->cur_co['Co']['id'], + $this->request->query['mode'], + $this->request->query['term'] ?? null); $this->set(compact('matches')); $this->set('_serialize', 'matches'); @@ -331,8 +242,6 @@ public function findSubscriber() { * Remove a member from a group * Called from all pages via AJAX call * - * TODO: We should probably save extra metadata in a data attribute and then pass that attribute to the request and not - * the Human Readable name * TODO: We need to appropriately handle Unathenticated call. We have to bubble up the response and do something. */ public function removeSubscriber(): void @@ -350,8 +259,6 @@ public function removeSubscriber(): void $groupNameFormatted, $remUserId, $this->CoGrouperLiteWidget->getConfig()); - - CakeLog::write('debug', __METHOD__ . '::response: ' . var_export($resultRemove, true)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': ' . var_export($e->getMessage(), true)); } @@ -389,7 +296,6 @@ public function groupOwnerApi() { $func = $scope['method']; $groupowners = $this->GrouperGroup->$func($scope, $this->CoGrouperLiteWidget->getConfig()); - CakeLog::write('debug', __METHOD__ . "::response: " . var_export($groupowners, true)); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . "::{$errorHint}: " . var_export($e->getMessage(), true)); $this->restResponse(HttpStatusCodesEnum::HTTP_INTERNAL_SERVER_ERROR, ErrorsEnum::Exception); diff --git a/Lib/GrouperApiAccess.php b/Lib/GrouperApiAccess.php index f904923..c84e8de 100644 --- a/Lib/GrouperApiAccess.php +++ b/Lib/GrouperApiAccess.php @@ -206,36 +206,28 @@ public function createGroupWithTemplate(array $queryData): array * * Create or Update a Group where User is Admin/Owner * - * @param array $queryData Array of conditions and data adding new Grouper Group + * @param string $actAsUserId + * @param string $groupName + * @param string $stem + * @param string $groupDescription * * @return bool True if added or updated successful - * @throws GrouperLiteWidgetException|JsonException + * @throws GrouperLiteWidgetException + * @throws JsonException */ - public function createUpdateGroup(array $queryData) + public function createUpdateGroup(string $actAsUserId, string $groupName, string $stem, string $groupDescription ): bool { - - $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 = [ 'WsRestGroupSaveRequest' => [ 'actAsSubjectLookup' => [ - 'subjectId' => $userId + 'subjectId' => $actAsUserId ], 'wsGroupToSaves' => [ [ 'wsGroup' => [ 'description' => $groupDescription, - 'name' => $newGroupToSave, + 'name' => "{$stem}:{$groupName}", 'displayExtension' => $groupName ], 'wsGroupLookup' => [ @@ -322,14 +314,14 @@ public function deleteGroupWithTemplate(array $queryData) * * Grouper Group information plus a listing of attributes in Grouper for that given Group * - * @param array $queryData Array of conditions for querying + * @param string $groupName * * @return array Record of Grouper attributes for given GroupName - * @throws GrouperLiteWidgetException|JsonException + * @throws GrouperLiteWidgetException + * @throws JsonException */ - public function getGrouperGroupInfo(array $queryData) + public function getGrouperGroupInfo(string $groupName) { - $groupName = $queryData['groupName']; $groupInfo = []; //Build request logic @@ -372,13 +364,14 @@ public function getGrouperGroupInfo(array $queryData) * Note: Params added at end make sure that the groups returned can only be viewed by the member logged into * Grouper Lite * - * @param string $userId + * @param string $actorUserId + * @param string $userId * * @return array Membership records that User is a member of in Grouper * * @throws GrouperLiteWidgetException */ - public function getGrouperMemberOfGroups(string $userId) + public function getUserGrouperGroupMemberships(string $actorUserId, string $userId): array { if(empty($userId)) { return []; @@ -386,7 +379,7 @@ public function getGrouperMemberOfGroups(string $userId) $actionEndpoint = "/subjects/{$userId}/groups?" . 'wsLiteObjectType=WsRestGetGroupsLiteRequest' - . "&actAsSubjectId={$userId}"; + . "&actAsSubjectId={$actorUserId}"; try { $results = $this->http->sendRequest('GET', $actionEndpoint); } catch (Exception $e) { @@ -400,20 +393,23 @@ public function getGrouperMemberOfGroups(string $userId) /** * Get members associated to a specific Grouper Group * - * @param array $queryData Array of conditions for querying + * @param string $actorUserId + * @param string $groupName * * @return array Listing of Members belonging to Grouper Group - * @throws GrouperLiteWidgetException|JsonException + * @throws GrouperLiteWidgetException + * @throws JsonException */ - public function getMembersInGroup(array $queryData) { + public function getGrouperGroupMembers(string $actorUserId, string $groupName): array + { //Build request logic $usersToShow = [ 'WsRestGetMembersRequest' => [ 'actAsSubjectLookup' => [ - 'subjectId' => $queryData['userId'] + 'subjectId' => $actorUserId ], 'wsGroupLookups' => [ - ['groupName' => $queryData['groupName']] + ['groupName' => $groupName] ], 'subjectAttributeNames' => ['name'] ] @@ -432,7 +428,7 @@ public function getMembersInGroup(array $queryData) { // 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'] === GrouperResultCodesEnum::GROUP_NOT_FOUND) { return [ [ 'sourceId' => 'NoAccess', @@ -445,21 +441,77 @@ public function getMembersInGroup(array $queryData) { return $results['WsGetMembersResults']['results'][0]['wsSubjects'] ?? []; } + /** + * Used for requests made to Membership endpoint in Grouper WS + * + * @param string $userId + * @param string $groupType + * + * @return array Group records associated to calling method + * @throws GrouperLiteWidgetException + * @see getOwnedStems() + * @see getOptinGroups() + * @see getOptOutGroups() + * @see getOwnedGroups() + */ + private function getGrouperUserMemberships(string $userId, string $groupType): array + { + if (in_array($groupType, [ + GrouperGroupTypeEnum::OPTINS, + GrouperGroupTypeEnum::OPTOUTS], true) + ) { + $subjectId = 'GrouperAll'; + } elseif (in_array($groupType, [ + GrouperGroupTypeEnum::ADMIN, + GrouperGroupTypeEnum::UPDATE, + GrouperGroupTypeEnum::STEM_ADMIN], true) + ) { + $subjectId = $userId; + } else { + CakeLog::write('error', __METHOD__ . ": Option of {$groupType} is not supported"); + throw new GrouperLiteWidgetException("Option of {$groupType} is not supported"); + } + + //Build request logic + $data = []; + $data['WsRestGetMembershipsRequest']['fieldName'] = $groupType; + $data['WsRestGetMembershipsRequest']['wsSubjectLookups'][0]['subjectId'] = $subjectId; + + if ($groupType == GrouperGroupTypeEnum::OPTINS + || $groupType == GrouperGroupTypeEnum::OPTOUTS) { + // Build request logic, 2 subjectId's, second is for when user in "Secret" Optin/Optout Group + $data['WsRestGetMembershipsRequest']['wsSubjectLookups'][1]['subjectId'] = $userId; + } + + $actionEndpoint = '/memberships'; + + try { + $results = $this->http->sendRequest('POST', + $actionEndpoint, + json_encode($data)); + } catch (Exception $e) { + CakeLog::write('error', __METHOD__ . ': An error occurred'); + throw $e; + } + + return $results; + } + /** * 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 + * @param string $userId + * @param string $groupType + * * @return array Optin groups from Grouper * @throws GrouperLiteWidgetException - * */ - public function getOptionalGroups(array $queryData) + public function getOptionalGroups(string $userId, string $groupType): array { - try { - $results = $this->useMembershipUrl($queryData); + $results = $this->getGrouperUserMemberships($userId, $groupType); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -470,21 +522,16 @@ public function getOptionalGroups(array $queryData) /** * 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 + * @param string $userId * + * @return array Array of groups from Grouper + * @throws Exception */ - public function getOwnedGroups(array $queryData) + public function getOwnedGroups(string $userId): 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)); + $resultsAdmin = $this->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::ADMIN); + $resultsUpdate = $this->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::UPDATE); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -499,16 +546,15 @@ public function getOwnedGroups(array $queryData) * * Gets all Stems/Folders where User is admin/owner * - * @param array $queryData Array of conditions for querying + * @param string $userId + * * @return array Array of Stems/Folders from Grouper * @throws GrouperLiteWidgetException */ - public function getOwnedStems(array $queryData) + public function getOwnedStems(string $userId): array { - $queryData['groupType'] = 'stemAdmin'; - try { - $results = $this->useMembershipUrl($queryData); + $results = $this->getGrouperUserMemberships($userId, GrouperGroupTypeEnum::ADMIN); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -643,55 +689,4 @@ public function urlGrouperEncode(string $url): string $url = urlencode($url); return str_replace(':', '%3A', $url); } - - /** - * Used for requests made to Membership endpoint in Grouper WS - * - * @param array $queryData Array of conditions for querying - * @return array Group records associated to calling method - * @throws GrouperLiteWidgetException - * - * @see getOwnedStems() - * @see getOptinGroups() - * @see getOptOutGroups() - * @see getOwnedGroups() - */ - private function useMembershipUrl(array $queryData) - { - $groupType = $queryData['groupType']; - $userId = $queryData['userId']; - - if ($groupType == 'optins' || $groupType == 'optouts') { - $subjectId = 'GrouperAll'; - } 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"); - } - - //Build request logic - $groupsToShow = []; - $groupsToShow['WsRestGetMembershipsRequest']['fieldName'] = $groupType; - $groupsToShow['WsRestGetMembershipsRequest']['wsSubjectLookups'][0]['subjectId'] = $subjectId; - - if ($groupType == 'optins' - || $groupType == 'optouts') { - // Build request logic, 2 subjectId's, second is for when user in "Secret" Optin/Optout Group - $groupsToShow['WsRestGetMembershipsRequest']['wsSubjectLookups'][1]['subjectId'] = $userId; - } - - $actionEndpoint = "/memberships"; - - try { - $results = $this->http->sendRequest('POST', - $actionEndpoint, - json_encode($groupsToShow)); - } catch (Exception $e) { - CakeLog::write('error', __METHOD__ . ': An error occurred'); - throw $e; - } - - return $results; - } } diff --git a/Lib/GrouperHTTPWrapper.php b/Lib/GrouperHTTPWrapper.php index 00a29f9..727ae79 100644 --- a/Lib/GrouperHTTPWrapper.php +++ b/Lib/GrouperHTTPWrapper.php @@ -174,6 +174,8 @@ public function setUser(string $user): void * @throws GrouperLiteWidgetException If issue with Grouper WS connection * * TODO: Handle unauthorized request-responses + * TODO: Handle SUBJECT_NOT_FOUND, + * TODO: Handle GROUP_NOT_FOUND, */ public function sendRequest(string $method, string $endPoint, string $body = ''): array { $uri = "{$this->_serviceUrl}{$endPoint}"; diff --git a/Lib/enum.php b/Lib/enum.php index f9a2cf7..3d84546 100644 --- a/Lib/enum.php +++ b/Lib/enum.php @@ -14,13 +14,25 @@ class GrouperSpecialGroups { /** Group whose members cannot see Grouper button, even if admin */ const GROUPER_VISIBLE_GROUP = 'app:comanage:LiteUI:grouperVisible'; + + /* Stem for email groups, only email groups using this stem are viewable */ + const GROUPER_EMAIL_STEM = 'app:sympa'; } class GrouperResultCodesEnum { const IS_MEMBER = 'IS_MEMBER'; const SUCCESS = 'SUCCESS'; const GROUP_NOT_FOUND = 'GROUP_NOT_FOUND'; + const SUBJECT_NOT_FOUND = 'SUBJECT_NOT_FOUND'; const NO_SUCH_OBJECT = 'NO_SUCH_OBJECT'; const SUCCESS_NO_INFO = 'SUCCESS_NO_INFO'; const EXECUTE_FAILED = 'EXECUTE_FAILED'; +} + +class GrouperGroupTypeEnum { + const OPTINS = 'optins'; + const OPTOUTS = 'optouts'; + const ADMIN = 'admin'; + const UPDATE = 'update'; + const STEM_ADMIN = 'stemAdmin'; } \ No newline at end of file diff --git a/Model/GrouperGroup.php b/Model/GrouperGroup.php index 58216f0..2b52896 100644 --- a/Model/GrouperGroup.php +++ b/Model/GrouperGroup.php @@ -44,12 +44,6 @@ class GrouperGroup extends GrouperLiteWidgetAppModel /** @var GrouperApiAccess $grouperAPI */ private $grouperAPI = null; - /** @var string Group whose members can create Groups via Template process */ - private $templateCreationGroup = 'ref:workinggroupadmins'; - - /** @var string Group whose members cannot see Grouper button, even if admin */ - private $grouperVisibleGroup = 'app:comanage:LiteUI:grouperVisible'; - private $wgStemsTopLevel = array( 'ref:incommon-collab', 'ref:internet2-collab' @@ -65,30 +59,24 @@ class GrouperGroup extends GrouperLiteWidgetAppModel 'ref:internet2-collab' ); - // Stem for email groups, only email groups using this stem are viewable - private $emailStem = 'app:sympa'; - /** - * Verifies if user is an owner/admin of a group and then stores results in Session. + * Verifies if user is an owner/admin of a group. * Session variable is reset on Group Creation and Group Deletion * * @param string $userId Id of User * @return String T or F * @throws GrouperLiteWidgetException * - * @see GrouperGroup::resetUserOwner() - * * @since COmanage Registry v4.4.0 */ public function isUserOwner(string $userId, array $cfg) { $this->initApi($cfg); $args = array(); - $args['userId'] = $userId; try { - $ownGroups = $this->grouperAPI->getOwnedGroups($args); + $ownGroups = $this->grouperAPI->getOwnedGroups($userId); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -148,11 +136,9 @@ public function filteredMemberOfGroups(array $conditions, array $cfg) $this->initApi($cfg); try { - $memberOfGroups = $this->memberOfGroups($conditions, $cfg); - - $conditions['groupType'] = 'optouts'; - // Determine which groups can be left by user, if want. - $optOutGroups = $this->grouperAPI->getOptionalGroups($conditions); + $memberOfGroups = $this->memberOfGroups($conditions['userId'], $conditions['userId'], $cfg); + // Determine which groups can be left by user, if wanted. + $optOutGroups = $this->grouperAPI->getOptionalGroups($conditions['userId'], GrouperGroupTypeEnum::OPTOUTS); $optOutGroupsNames = Hash::combine($optOutGroups, '{n}.name', '{n}.displayExtension'); foreach ($memberOfGroups as &$memberOfGroup) { @@ -167,21 +153,130 @@ public function filteredMemberOfGroups(array $conditions, array $cfg) } } + + /** + * Find People based on mode and term + * + * @param integer $coId CO ID + * @param string $mode Search mode to apply filters for + * @param string|null $term Search block + * + * @return array Array of CO Person records + * @since COmanage Registry v4.4.0 + * + * XXX Remove this as soon as the change is present in COmanage core + * + */ + public function findForPicker(int $coId, string $mode, ?string $term): array + { + $coPersonIds = []; + $this->CoPerson = ClassRegistry::init('CoPerson'); + $this->Co = ClassRegistry::init('Co'); + + // jquery Autocomplete sends the search as url?term=foo + if(!empty($term)) { + // Leverage model specific keyword search + + // Note EmailAddress and Identifier don't support substring search + foreach(array('Name', 'EmailAddress', 'Identifier') as $m) { + $hits = $this->CoPerson->$m->search($coId, $term, 25); + + $coPersonIds = array_merge($coPersonIds, Hash::extract($hits, '{n}.CoPerson.id')); + } + } + + $coPersonIds = array_unique($coPersonIds); + + // Look up additional information to provide hints as to which person is which. + // We only do this when there are relatively small numbers of results to + // avoid making a bunch of database queries early in the search. + + $matches = array(); + + if(count($coPersonIds) > 100) { + // We don't return large sets to avoid slow performance + + $matches[] = array( + 'value' => -1, + 'label' => _txt('er.picker.toomany') + ); + } else { + $people = $this->CoPerson->filterPicker($coId, $coPersonIds, $mode); + $pickerEmailType = $this->Co->CoSetting->getPersonPickerEmailType($coId); + $pickerIdentifierType = $this->Co->CoSetting->getPersonPickerIdentifierType($coId); + $pickerDisplayTypes = $this->Co->CoSetting->getPersonPickerDisplayTypes($coId); + + foreach($people as $p) { + $label = generateCn($p['Name'][0]); + $idArr = $p['Identifier']; + $emailArr = $p['EmailAddress']; + $email = ''; + $emailLabel = ''; + $id = ''; + $idLabel = ''; + + // Iterate over the email array + if(!empty($emailArr) && !empty($pickerEmailType)) { + $emailLabel = !empty($pickerDisplayTypes) ? _txt('fd.extended_type.generic.label', array(_txt('fd.email_address.mail'), $pickerEmailType)) + : _txt('fd.email_address.mail') . ': '; + foreach($emailArr as $e) { + if($e['type'] == $pickerEmailType) { + $email = $e['mail']; + break; + } + } + } + + // Set the identifier for display (and limit it to 30 characters max) + if(!empty($idArr[0]['identifier']) && !empty($pickerIdentifierType)) { + if(!empty($pickerDisplayTypes)) { + $idLabel = _txt('fd.extended_type.generic.label', array(_txt('fd.identifier.identifier'), $pickerIdentifierType)); + } + else { + $idLabel = _txt('fd.identifier.identifier') . ': '; + } + foreach($idArr as $i) { + if($i['type'] == $pickerIdentifierType) { + $id = mb_strimwidth($i['identifier'], 0, 30, '...'); + break; + } + } + } + + // Make sure we don't already have an entry for this CO Person ID + if(!Hash::check($matches, '{n}[value='.$p['CoPerson']['id'].']')) { + $matches[] = array( + 'value' => $p['CoPerson']['id'], + 'label' => $label, + 'email' => $email, + 'emailLabel' => $emailLabel, + 'identifier' => $id, + 'identifierLabel' => $idLabel + ); + } + } + } + + return $matches; + } + /** * Internal process used by other functions to fetch Groups the User is a member of * - * @param array $conditions Listing of conditions for display of records, including UserId + * @param string $actorUserId + * @param string $userId + * @param 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, array $cfg) + private function memberOfGroups(string $actorUserId, string $userId, array $cfg) { $this->initApi($cfg); try { - return $this->grouperAPI->getGrouperMemberOfGroups($conditions['userId']); + return $this->grouperAPI->getUserGrouperGroupMemberships($actorUserId, $userId); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -252,7 +347,7 @@ public function ownerGroups(array $conditions, array $cfg) $this->initApi($cfg); try { - return $this->grouperAPI->getOwnedGroups($conditions); + return $this->grouperAPI->getOwnedGroups($conditions['userId']); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -264,21 +359,21 @@ public function ownerGroups(array $conditions, array $cfg) * NOTE: This list only shows members, it does not pull in other groups that may be attached in Grouper as * members * - * @param array $conditions Listing of conditions for display of records - * @param string $userId Id of User + * @param string $actorUserId Id of User + * @param string $groupName + * @param array $cfg + * * @return array Listing of members in requested Grouper Group * @throws GrouperLiteWidgetException Captured in Controller - * + * @throws JsonException * @since COmanage Registry v4.4.0 */ - public function membersInGroup(array $conditions, string $userId, array $cfg) + public function getGrouperGroupMembers(string $actorUserId, string $groupName, array $cfg): array { $this->initApi($cfg); - $conditions['userId'] = $userId; - try { - $groupMembers = $this->grouperAPI->getMembersInGroup($conditions); + $groupMembers = $this->grouperAPI->getGrouperGroupMembers($actorUserId, $groupName); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -301,7 +396,7 @@ public function membersInGroup(array $conditions, string $userId, array $cfg) /** * Add a member to a specific Grouper Group * - * @param string $userId Id of User + * @param string $actAsUserId Id of User * @param string $groupName * @param string $addUserId * @param array $cfg @@ -311,12 +406,12 @@ public function membersInGroup(array $conditions, string $userId, array $cfg) * @throws JsonException * @since COmanage Registry v4.4.0 */ - public function addGrouperGroupMember(string $userId, string $groupName, string $addUserId, array $cfg) + public function addGrouperGroupMember(string $actAsUserId, string $groupName, string $addUserId, array $cfg) { $this->initApi($cfg); try { - return $this->grouperAPI->addGrouperGroupMember($userId, $groupName, $addUserId); + return $this->grouperAPI->addGrouperGroupMember($actAsUserId, $groupName, $addUserId); } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; @@ -365,13 +460,15 @@ public function removeGrouperGroupMember(string $userId, string $groupName, stri public function optinGroups(array $conditions, array $cfg) { $this->initApi($cfg); - $conditions['groupType'] = 'optins'; try { // Groups the user can join or leave - $joinOrLeave = $this->grouperAPI->getOptionalGroups($conditions); + $joinOrLeave = $this->grouperAPI->getOptionalGroups($conditions['userId'], + GrouperGroupTypeEnum::OPTINS); // Groups the user is a member of - $userGroups = $this->memberOfGroups($conditions, $cfg); + $userGroups = $this->memberOfGroups($conditions['userId'], + $conditions['userId'], + $cfg); // 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 @@ -418,35 +515,28 @@ public function isTemplateUser(string $userId, array $cfg) public function createGroupWithTemplate(string $userId, array $groupData, array $cfg) { $this->initApi($cfg); + //Need to massage incoming data to meet Grouper Template requirements + $fields = array( + 'gsh_input_isSympa', + 'gsh_input_isSympaModerated', + 'gsh_input_isOptin', + 'gsh_input_isConfluence', + 'gsh_input_isJira' + ); + // Template does not except true/false, so convert to string and send that way + foreach ($fields as $field) { + ($groupData[$field] == '0') ? $groupData[$field] = 'false' : $groupData[$field] = 'true'; + } + $args = array(); + $args['userId'] = $userId; + $args['data'] = $groupData; try { - //Need to massage incoming data to meet Grouper Template requirements - $fields = array( - 'gsh_input_isSympa', - 'gsh_input_isSympaModerated', - 'gsh_input_isOptin', - 'gsh_input_isConfluence', - 'gsh_input_isJira' - ); - // Template does not except true/false, so convert to string and send that way - foreach ($fields as $field) { - ($groupData[$field] == '0') ? $groupData[$field] = 'false' : $groupData[$field] = 'true'; - } - - $args = array(); - $args['userId'] = $userId; - $args['data'] = $groupData; - - // Reset Session variable that shows if User is an owner or not - $this->resetUserOwner(); - return $this->grouperAPI->createGroupWithTemplate($args); - } catch (Exception $e) { CakeLog::write('error', __METHOD__ . ': An error occurred'); throw $e; } - } /** @@ -516,9 +606,6 @@ private function getFriendlyWorkingGroupName(array $groups, string $method) { $arrayIndex = 0; $workingGroups = array(); - - CakeLog::write('debug', __METHOD__ . '::raw groups: ', print_r($groups, true)); - //First need to loop through all groups and pull in all top levels $topLevelWG = array(); foreach ($groups as $group) { diff --git a/View/Elements/Components/groupattributes.ctp b/View/Elements/Components/groupattributes.ctp index 91f246f..787ad81 100644 --- a/View/Elements/Components/groupattributes.ctp +++ b/View/Elements/Components/groupattributes.ctp @@ -14,7 +14,7 @@