diff --git a/lib/ACL/UserMapping/IUserMapping.php b/lib/ACL/UserMapping/IUserMapping.php index f86d7fbc4..bf5edd1b3 100644 --- a/lib/ACL/UserMapping/IUserMapping.php +++ b/lib/ACL/UserMapping/IUserMapping.php @@ -9,10 +9,12 @@ namespace OCA\GroupFolders\ACL\UserMapping; interface IUserMapping { - /** @return 'user'|'group'|'dummy' */ + /** @return 'user'|'group'|'dummy'|'circle' */ public function getType(): string; public function getId(): string; public function getDisplayName(): string; + + public function getKey(): string; } diff --git a/lib/ACL/UserMapping/IUserMappingManager.php b/lib/ACL/UserMapping/IUserMappingManager.php index 373aba8cf..77f32d1b5 100644 --- a/lib/ACL/UserMapping/IUserMappingManager.php +++ b/lib/ACL/UserMapping/IUserMappingManager.php @@ -18,4 +18,13 @@ interface IUserMappingManager { public function getMappingsForUser(IUser $user, bool $userAssignable = true): array; public function mappingFromId(string $type, string $id): ?IUserMapping; + + /** + * Check if a user is a member of one of the provided user mappings + * + * @param IUser $user + * @param IUserMapping[] $mappings + * @return bool + */ + public function userInMappings(IUser $user, array $mappings): bool; } diff --git a/lib/ACL/UserMapping/UserMapping.php b/lib/ACL/UserMapping/UserMapping.php index c0309c264..74858b110 100644 --- a/lib/ACL/UserMapping/UserMapping.php +++ b/lib/ACL/UserMapping/UserMapping.php @@ -12,7 +12,7 @@ class UserMapping implements IUserMapping { private readonly string $displayName; /** - * @param 'user'|'group'|'dummy' $type + * @param 'user'|'group'|'dummy'|'circle' $type */ public function __construct( private readonly string $type, @@ -33,4 +33,8 @@ public function getId(): string { public function getDisplayName(): string { return $this->displayName; } + + public function getKey(): string { + return $this->getType() . ':' . $this->getId(); + } } diff --git a/lib/ACL/UserMapping/UserMappingManager.php b/lib/ACL/UserMapping/UserMappingManager.php index dac155c45..5f5a1adab 100644 --- a/lib/ACL/UserMapping/UserMappingManager.php +++ b/lib/ACL/UserMapping/UserMappingManager.php @@ -8,34 +8,128 @@ namespace OCA\GroupFolders\ACL\UserMapping; +use OCA\Circles\CirclesManager; +use OCA\Circles\Exceptions\CircleNotFoundException; +use OCA\Circles\Model\Circle; +use OCA\Circles\Model\Probes\CircleProbe; +use OCP\AutoloadNotAllowedException; use OCP\IGroup; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; +use OCP\Server; +use Psr\Container\ContainerExceptionInterface; +use Psr\Log\LoggerInterface; class UserMappingManager implements IUserMappingManager { public function __construct( private readonly IGroupManager $groupManager, private readonly IUserManager $userManager, + private readonly LoggerInterface $logger, ) { } public function getMappingsForUser(IUser $user, bool $userAssignable = true): array { $groupMappings = array_values(array_map(fn (IGroup $group): UserMapping => new UserMapping('group', $group->getGID(), $group->getDisplayName()), $this->groupManager->getUserGroups($user))); + $circleMappings = array_values(array_map(fn (Circle $circle): UserMapping => new UserMapping('circle', $circle->getSingleId(), $circle->getDisplayName()), $this->getUserCircles($user->getUID()))); return array_merge([ new UserMapping('user', $user->getUID(), $user->getDisplayName()), - ], $groupMappings); + ], $groupMappings, $circleMappings); } public function mappingFromId(string $type, string $id): ?IUserMapping { - $mappingObject = ($type === 'group' ? $this->groupManager : $this->userManager)->get($id); - if ($mappingObject) { - $displayName = $mappingObject->getDisplayName(); - /** @var 'user'|'group' $type */ - return new UserMapping($type, $id, $displayName); - } else { + switch ($type) { + case 'group': + $displayName = $this->groupManager->get($id)?->getDisplayName(); + break; + case 'user': + $displayName = $this->userManager->get($id)?->getDisplayName(); + break; + case 'circle': + $displayName = $this->getCircle($id)?->getDisplayName(); + break; + default: + return null; + } + if ($displayName === null) { + return null; + } + + return new UserMapping($type, $id, $displayName); + } + + + + /** + * returns the Circle from its single Id, or NULL if not available + */ + private function getCircle(string $groupId): ?Circle { + $circlesManager = $this->getCirclesManager(); + if ($circlesManager === null) { return null; } + + $circlesManager->startSuperSession(); + $probe = new CircleProbe(); + $probe->includeSystemCircles(); + $probe->includeSingleCircles(); + try { + return $circlesManager->getCircle($groupId, $probe); + } catch (CircleNotFoundException) { + } catch (\Exception $e) { + $this->logger->warning('', ['exception' => $e]); + } finally { + $circlesManager->stopSession(); + } + + return null; + } + + /** + * returns list of circles a user is member of + */ + private function getUserCircles(string $userId): array { + $circlesManager = $this->getCirclesManager(); + if ($circlesManager === null) { + return []; + } + + $circlesManager->startSession($circlesManager->getLocalFederatedUser($userId)); + try { + return $circlesManager->probeCircles(); + } catch (\Exception $e) { + $this->logger->warning('', ['exception' => $e]); + } finally { + $circlesManager->stopSession(); + } + + return []; + } + + public function getCirclesManager(): ?CirclesManager { + try { + return Server::get(CirclesManager::class); + } catch (ContainerExceptionInterface|AutoloadNotAllowedException) { + return null; + } + } + + public function userInMappings(IUser $user, array $mappings): bool { + foreach ($mappings as $mapping) { + if ($mapping->getType() === 'user' && $mapping->getId() === $user->getUID()) { + return true; + } + } + + $mappingKeys = array_map(fn (IUserMapping $mapping) => $mapping->getKey(), $mappings); + + $userMappings = $this->getMappingsForUser($user); + foreach ($userMappings as $userMapping) { + if (in_array($userMapping->getKey(), $mappingKeys, true)) { + return true; + } + } + return false; } } diff --git a/lib/Command/ListCommand.php b/lib/Command/ListCommand.php index 202679e95..925c5e919 100644 --- a/lib/Command/ListCommand.php +++ b/lib/Command/ListCommand.php @@ -106,7 +106,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int }, array_keys($folder['groups']), array_values($folder['groups'])); $folder['groups'] = implode("\n", $groupStrings); $folder['acl'] = $folder['acl'] ? 'Enabled' : 'Disabled'; - $manageStrings = array_map(fn (array $manage): string => $manage['id'] . ' (' . $manage['type'] . ')', $folder['manage']); + $manageStrings = array_map(fn (array $manage): string => $manage['displayname'] . ' (' . $manage['type'] . ')', $folder['manage']); $folder['manage'] = implode("\n", $manageStrings); return $folder; diff --git a/lib/Controller/FolderController.php b/lib/Controller/FolderController.php index a4d5caa50..df8361c79 100644 --- a/lib/Controller/FolderController.php +++ b/lib/Controller/FolderController.php @@ -30,6 +30,7 @@ /** * @psalm-import-type GroupFoldersGroup from ResponseDefinitions + * @psalm-import-type GroupFoldersCircle from ResponseDefinitions * @psalm-import-type GroupFoldersUser from ResponseDefinitions * @psalm-import-type GroupFoldersFolder from ResponseDefinitions * @psalm-import-type InternalFolderOut from FolderManager @@ -474,7 +475,7 @@ private function folderDataForXML(array $data): array { * * @param int $id The ID of the Groupfolder * @param string $search String to search by - * @return DataResponse, groups: list}, array{}> + * @return DataResponse, groups: list, circles: list}, array{}> * @throws OCSForbiddenException Not allowed to search * * 200: ACL Mappings returned @@ -482,8 +483,7 @@ private function folderDataForXML(array $data): array { #[NoAdminRequired] #[FrontpageRoute(verb: 'GET', url: '/folders/{id}/search')] public function aclMappingSearch(int $id, string $search = ''): DataResponse { - $users = []; - $groups = []; + $users = $groups = $circles = []; if ($this->user === null) { throw new OCSForbiddenException(); @@ -492,11 +492,13 @@ public function aclMappingSearch(int $id, string $search = ''): DataResponse { if ($this->manager->canManageACL($id, $this->user) === true) { $groups = $this->manager->searchGroups($id, $search); $users = $this->manager->searchUsers($id, $search); + $circles = $this->manager->searchCircles($id, $search); } return new DataResponse([ 'users' => $users, 'groups' => $groups, + 'circles' => $circles ]); } } diff --git a/lib/Folder/FolderManager.php b/lib/Folder/FolderManager.php index 0f1c04efc..933f1b45c 100644 --- a/lib/Folder/FolderManager.php +++ b/lib/Folder/FolderManager.php @@ -11,10 +11,14 @@ use OC\Files\Node\Node; use OCA\Circles\CirclesManager; use OCA\Circles\Exceptions\CircleNotFoundException; +use OCA\Circles\Model\Circle; +use OCA\Circles\Model\Member; use OCA\Circles\Model\Probes\CircleProbe; +use OCA\GroupFolders\ACL\UserMapping\IUserMapping; +use OCA\GroupFolders\ACL\UserMapping\IUserMappingManager; +use OCA\GroupFolders\ACL\UserMapping\UserMapping; use OCA\GroupFolders\Mount\GroupMountPoint; use OCA\GroupFolders\ResponseDefinitions; -use OCA\GroupFolders\Settings\Admin; use OCP\AutoloadNotAllowedException; use OCP\Constants; use OCP\DB\Exception; @@ -36,6 +40,7 @@ /** * @psalm-import-type GroupFoldersGroup from ResponseDefinitions + * @psalm-import-type GroupFoldersCircle from ResponseDefinitions * @psalm-import-type GroupFoldersUser from ResponseDefinitions * @psalm-import-type GroupFoldersAclManage from ResponseDefinitions * @psalm-import-type GroupFoldersApplicable from ResponseDefinitions @@ -72,6 +77,7 @@ public function __construct( private readonly LoggerInterface $logger, private readonly IEventDispatcher $eventDispatcher, private readonly IConfig $config, + private readonly IUserMappingManager $userMappingManager, ) { } @@ -264,16 +270,33 @@ private function getManageAcl(array $mappings): array { ]; } - $group = Server::get(IGroupManager::class)->get($entry['mapping_id']); - if ($group === null) { - return null; + if ($entry['mapping_type'] === 'group') { + $group = Server::get(IGroupManager::class)->get($entry['mapping_id']); + if ($group === null) { + return null; + } + + return [ + 'type' => 'group', + 'id' => $group->getGID(), + 'displayname' => $group->getDisplayName() + ]; } - return [ - 'type' => 'group', - 'id' => $group->getGID(), - 'displayname' => $group->getDisplayName() - ]; + if ($entry['mapping_type'] === 'circle') { + $circle = $this->getCircle($entry['mapping_id']); + if ($circle === null) { + return null; + } + + return [ + 'type' => 'circle', + 'id' => $circle->getSingleId(), + 'displayname' => $circle->getDisplayName() + ]; + } + + return null; }, $mappings))); } @@ -401,6 +424,20 @@ private function getGroups(int $id): array { ], array_values(array_filter($groups))); } + /** + * @throws Exception + * @return list + */ + private function getCircles(int $id): array { + $circles = $this->getAllApplicable()[$id] ?? []; + $circles = array_map(fn (string $singleId): ?Circle => $this->getCircle($singleId), array_keys($circles)); + + return array_map(fn (Circle $circle): array => [ + 'sid' => $circle->getSingleId(), + 'displayname' => $circle->getDisplayName() + ], array_values(array_filter($circles))); + } + /** * Check if the user is able to configure the advanced folder permissions. This * is the case if the user is an admin, has admin permissions for the group folder @@ -422,29 +459,26 @@ public function canManageACL(int $folderId, IUser $user): bool { } } - $query = $this->connection->getQueryBuilder(); - $query->select('*') - ->from('group_folders_manage') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT))) - ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('user'))) - ->andWhere($query->expr()->eq('mapping_id', $query->createNamedParameter($userId))); - if ($query->executeQuery()->rowCount() === 1) { - return true; - } + $managerMappings = $this->getManagerMappings($folderId); + return $this->userMappingManager->userInMappings($user, $managerMappings); + } + /** + * @param int $folderId + * @return IUserMapping[] + */ + private function getManagerMappings(int $folderId): array { $query = $this->connection->getQueryBuilder(); - $query->select('*') + $query->select('mapping_type', 'mapping_id') ->from('group_folders_manage') - ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId))) - ->andWhere($query->expr()->eq('mapping_type', $query->createNamedParameter('group'))); - $groups = $query->executeQuery()->fetchAll(); - foreach ($groups as $manageRule) { - if ($this->groupManager->isInGroup($userId, $manageRule['mapping_id'])) { - return true; - } - } + ->where($query->expr()->eq('folder_id', $query->createNamedParameter($folderId, IQueryBuilder::PARAM_INT))); + $managerMappings = []; - return false; + $rows = $query->executeQuery()->fetchAll(); + foreach ($rows as $manageRule) { + $managerMappings[] = new UserMapping($manageRule['mapping_type'], $manageRule['mapping_id']); + } + return $managerMappings; } /** @@ -460,6 +494,19 @@ public function searchGroups(int $id, string $search = ''): array { return array_values(array_filter($groups, fn (array $group): bool => (stripos($group['gid'], $search) !== false) || (stripos($group['displayname'], $search) !== false))); } + /** + * @throws Exception + * @return list + */ + public function searchCircles(int $id, string $search = ''): array { + $circles = $this->getCircles($id); + if ($search === '') { + return $circles; + } + + return array_values(array_filter($circles, fn (array $circle): bool => (stripos($circle['displayname'], $search) !== false))); + } + /** * @throws Exception * @return list @@ -482,6 +529,27 @@ public function searchUsers(int $id, string $search = '', int $limit = 10, int $ } } + foreach ($this->getCircles($id) as $circleData) { + $circle = $this->getCircle($circleData['sid']); + if ($circle === null) { + continue; + } + + foreach ($circle->getInheritedMembers(false) as $member) { + if ($member->getUserType() !== Member::TYPE_USER) { + continue; + } + + $uid = $member->getUserId(); + if (!isset($users[$uid])) { + $users[$uid] = [ + 'uid' => $uid, + 'displayname' => $member->getDisplayName() + ]; + } + } + } + return array_values($users); } @@ -918,9 +986,16 @@ public function getFolderPermissionsForUser(IUser $user, int $folderId): int { * returns if the groupId is in fact the singleId of an existing Circle */ public function isACircle(string $groupId): bool { + return ($this->getCircle($groupId) !== null); + } + + /** + * returns the Circle from its single Id, or NULL if not available + */ + public function getCircle(string $groupId): ?Circle { $circlesManager = $this->getCirclesManager(); if ($circlesManager === null) { - return false; + return null; } $circlesManager->startSuperSession(); @@ -928,9 +1003,7 @@ public function isACircle(string $groupId): bool { $probe->includeSystemCircles(); $probe->includeSingleCircles(); try { - $circlesManager->getCircle($groupId, $probe); - - return true; + return $circlesManager->getCircle($groupId, $probe); } catch (CircleNotFoundException) { } catch (\Exception $e) { $this->logger->warning('', ['exception' => $e]); @@ -938,7 +1011,7 @@ public function isACircle(string $groupId): bool { $circlesManager->stopSession(); } - return false; + return null; } public function getCirclesManager(): ?CirclesManager { diff --git a/lib/ResponseDefinitions.php b/lib/ResponseDefinitions.php index ddfa678fb..6ab980540 100644 --- a/lib/ResponseDefinitions.php +++ b/lib/ResponseDefinitions.php @@ -23,6 +23,11 @@ * displayname: string, * } * + * @psalm-type GroupFoldersCircle = array{ + * sid: string, + * displayname: string, + * } + * * @psalm-type GroupFoldersUser = array{ * uid: string, * displayname: string, @@ -31,7 +36,7 @@ * @psalm-type GroupFoldersAclManage = array{ * displayname: string, * id: string, - * type: 'user'|'group', + * type: 'user'|'group'|'circle', * } * * @psalm-type GroupFoldersApplicable = array{ diff --git a/openapi.json b/openapi.json index f0a0039ed..a6fa6eba2 100644 --- a/openapi.json +++ b/openapi.json @@ -38,7 +38,8 @@ "type": "string", "enum": [ "user", - "group" + "group", + "circle" ] } } @@ -87,6 +88,21 @@ } } }, + "Circle": { + "type": "object", + "required": [ + "sid", + "displayname" + ], + "properties": { + "sid": { + "type": "string" + }, + "displayname": { + "type": "string" + } + } + }, "DelegationCircle": { "type": "object", "required": [ @@ -1939,7 +1955,8 @@ "type": "object", "required": [ "users", - "groups" + "groups", + "circles" ], "properties": { "users": { @@ -1953,6 +1970,12 @@ "items": { "$ref": "#/components/schemas/Group" } + }, + "circles": { + "type": "array", + "items": { + "$ref": "#/components/schemas/Circle" + } } } } diff --git a/psalm.xml b/psalm.xml index 3536ec78c..76ed29172 100644 --- a/psalm.xml +++ b/psalm.xml @@ -66,11 +66,13 @@ + + diff --git a/src/components/SharingSidebarView.vue b/src/components/SharingSidebarView.vue index 4dc007de4..6fed2d418 100644 --- a/src/components/SharingSidebarView.vue +++ b/src/components/SharingSidebarView.vue @@ -267,6 +267,9 @@ export default { if (type === 'group') { return `${displayName} (${t('groupfolders', 'Group')})` } + if (type === 'circle') { + return `${displayName} (${t('groupfolders', 'Team')})` + } return displayName }, @@ -298,7 +301,16 @@ export default { label: this.getFullDisplayName(user.displayname, 'user'), } }) - this.options = [...groups, ...users].filter((entry) => { + const circles = Object.values(result.data.ocs.data.circles).map((user) => { + return { + unique: 'circle:' + user.sid, + type: 'circle', + id: user.sid, + displayname: user.displayname, + label: this.getFullDisplayName(user.displayname, 'circle'), + } + }) + this.options = [...groups, ...users, ...circles].filter((entry) => { // filter out existing acl rules return !this.list.find((existingAcl) => entry.unique === existingAcl.getUniqueMappingIdentifier()) }) diff --git a/src/settings/Api.ts b/src/settings/Api.ts index ebd319508..49ccea060 100644 --- a/src/settings/Api.ts +++ b/src/settings/Api.ts @@ -7,7 +7,7 @@ import axios from '@nextcloud/axios' import { confirmPassword } from '@nextcloud/password-confirmation' // eslint-disable-next-line n/no-unpublished-import import type { OCSResponse } from '@nextcloud/typings/lib/ocs' -import type { Folder, Group, User, AclManage, DelegationCircle, DelegationGroup } from '../types' +import type { Folder, Group, User, AclManage, DelegationCircle, DelegationGroup, Circle } from '../types' export class Api { @@ -109,9 +109,10 @@ export class Api { async aclMappingSearch(folderId: number, search: string): Promise<{ groups: AclManage[], - users: AclManage[] + users: AclManage[], + circles: AclManage[], }> { - const response = await axios.get>(this.getUrl(`folders/${folderId}/search`), { params: { search } }) + const response = await axios.get>(this.getUrl(`folders/${folderId}/search`), { params: { search } }) return { groups: Object.values(response.data.ocs.data.groups).map((item) => { return { @@ -127,6 +128,13 @@ export class Api { displayname: item.displayname, } }), + circles: Object.values(response.data.ocs.data.circles).map((item) => { + return { + type: 'circle', + id: item.sid, + displayname: item.displayname, + } + }), } } diff --git a/src/settings/App.tsx b/src/settings/App.tsx index edc74e76e..171919705 100644 --- a/src/settings/App.tsx +++ b/src/settings/App.tsx @@ -369,18 +369,27 @@ export class App extends Component implements OC.Plugin void; - onSearch: (name: string) => Promise<{ groups: AclManage[]; users: AclManage[]; }>; + onSearch: (name: string) => Promise<{ groups: AclManage[]; users: AclManage[]; circles: AclManage[] }>; } // eslint-disable-next-line jsdoc/require-jsdoc function ManageAclSelect({ onChange, onSearch, folder }: ManageAclSelectProps) { const handleSearch = async (inputValue: string) => { const result = await onSearch(inputValue) - return [...result.groups, ...result.users] + return [...result.groups, ...result.users, ...result.circles] } - const typeLabel = (item) => { - return item.type === 'user' ? t('groupfolders', 'User') : t('groupfolders', 'Group') + const typeLabel = (item: AclManage) => { + switch (item.type) { + case 'circle': + return t('groupfolders', 'Team') + case 'group': + return t('groupfolders', 'Group') + case 'user': + return t('groupfolders', 'User') + default: + return t('groupfolders', 'Unknown') + } } return logger = $this->createMock(LoggerInterface::class); $this->eventDispatcher = $this->createMock(IEventDispatcher::class); $this->config = $this->createMock(IConfig::class); + $this->userMappingManager = $this->createMock(IUserMappingManager::class); $this->manager = new FolderManager( Server::get(IDBConnection::class), $this->groupManager, @@ -49,6 +52,7 @@ protected function setUp(): void { $this->logger, $this->eventDispatcher, $this->config, + $this->userMappingManager, ); $this->clean(); } @@ -368,7 +372,7 @@ public function testGetFoldersForUserEmpty(): void { public function testGetFoldersForUserSimple(): void { $db = $this->createMock(IDBConnection::class); $manager = $this->getMockBuilder(FolderManager::class) - ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config]) + ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager]) ->onlyMethods(['getFoldersForGroups']) ->getMock(); @@ -390,7 +394,7 @@ public function testGetFoldersForUserSimple(): void { public function testGetFoldersForUserMerge(): void { $db = $this->createMock(IDBConnection::class); $manager = $this->getMockBuilder(FolderManager::class) - ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config]) + ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager]) ->onlyMethods(['getFoldersForGroups']) ->getMock(); @@ -425,7 +429,7 @@ public function testGetFoldersForUserMerge(): void { public function testGetFolderPermissionsForUserMerge(): void { $db = $this->createMock(IDBConnection::class); $manager = $this->getMockBuilder(FolderManager::class) - ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config]) + ->setConstructorArgs([$db, $this->groupManager, $this->mimeLoader, $this->logger, $this->eventDispatcher, $this->config, $this->userMappingManager]) ->onlyMethods(['getFoldersForGroups']) ->getMock(); diff --git a/tests/stubs/oca_circles_ifederatedmodel.php b/tests/stubs/oca_circles_ifederatedmodel.php new file mode 100644 index 000000000..583191bea --- /dev/null +++ b/tests/stubs/oca_circles_ifederatedmodel.php @@ -0,0 +1,26 @@ + 'single', + 1 => 'user', + 2 => 'group', + 4 => 'mail', + 8 => 'contact', + 16 => 'circle', + 10000 => 'app' + ]; + + /** + * Note: When editing those values, update lib/Application/Capabilities.php + * + * @see Capabilities::generateConstantsMember() + */ + public const STATUS_INVITED = 'Invited'; + public const STATUS_REQUEST = 'Requesting'; + public const STATUS_MEMBER = 'Member'; + public const STATUS_BLOCKED = 'Blocked'; + + + /** + * Note: When editing those values, update lib/Application/Capabilities.php + * + * @see Capabilities::generateConstantsMember() + * @var array + */ + public static $DEF_LEVEL = [ + 1 => 'Member', + 4 => 'Moderator', + 8 => 'Admin', + 9 => 'Owner' + ]; + + + public static $DEF_TYPE_MAX = 31; + + /** + * Member constructor. + */ + public function __construct() { + } + + /** + * @param string $id + * + * @return $this + */ + public function setId(string $id): self { + } + + /** + * @return string + */ + public function getId(): string { + } + + + /** + * @param string $circleId + * + * @return Member + */ + public function setCircleId(string $circleId): self { + } + + /** + * @return string + */ + public function getCircleId(): string { + } + + + /** + * This should replace user_id, user_type and instance; and will use the data from Circle with + * Config=CFG_SINGLE + * + * @param string $singleId + * + * @return $this + */ + public function setSingleId(string $singleId): self { + } + + /** + * @return string + */ + public function getSingleId(): string { + } + + + /** + * @param string $userId + * + * @return Member + */ + public function setUserId(string $userId): self { + } + + /** + * @return string + */ + public function getUserId(): string { + } + + + /** + * @param int $userType + * + * @return Member + */ + public function setUserType(int $userType): self { + } + + /** + * @return int + */ + public function getUserType(): int { + } + + /** + * @return int + * @deprecated 22.0.0 Use `getUserType()` instead + */ + public function getType(): int { + } + + + /** + * @param string $instance + * + * @return Member + */ + public function setInstance(string $instance): self { + } + + /** + * @return string + */ + public function getInstance(): string { + } + + + /** + * @return bool + */ + public function isLocal(): bool { + } + + + /** + * @param FederatedUser $invitedBy + * + * @return Member + */ + public function setInvitedBy(FederatedUser $invitedBy): Member { + } + + /** + * @return FederatedUser + */ + public function getInvitedBy(): FederatedUser { + } + + /** + * @return bool + */ + public function hasInvitedBy(): bool { + } + + + /** + * @return bool + */ + public function hasRemoteInstance(): bool { + } + + /** + * @param RemoteInstance $remoteInstance + * + * @return Member + */ + public function setRemoteInstance(RemoteInstance $remoteInstance): self { + } + + /** + * @return RemoteInstance + */ + public function getRemoteInstance(): RemoteInstance { + } + + + /** + * @return bool + */ + public function hasBasedOn(): bool { + } + + /** + * @param Circle $basedOn + * + * @return $this + */ + public function setBasedOn(Circle $basedOn): self { + } + + /** + * @return Circle + */ + public function getBasedOn(): Circle { + } + + + /** + * @return bool + */ + public function hasInheritedBy(): bool { + } + + /** + * @param FederatedUser $inheritedBy + * + * @return $this + */ + public function setInheritedBy(FederatedUser $inheritedBy): self { + } + + /** + * @return FederatedUser + */ + public function getInheritedBy(): FederatedUser { + } + + + /** + * @return bool + */ + public function hasInheritanceFrom(): bool { + } + + /** + * @param Member $inheritanceFrom + * + * @return $this + */ + public function setInheritanceFrom(Member $inheritanceFrom): self { + } + + /** + * @return Member|null + */ + public function getInheritanceFrom(): ?Member { + } + + + /** + * @param int $level + * + * @return Member + */ + public function setLevel(int $level): self { + } + + /** + * @return int + */ + public function getLevel(): int { + } + + + /** + * @param string $status + * + * @return Member + */ + public function setStatus(string $status): self { + } + + /** + * @return string + */ + public function getStatus(): string { + } + + + /** + * @param array $notes + * + * @return Member + */ + public function setNotes(array $notes): self { + } + + /** + * @return array + */ + public function getNotes(): array { + } + + + /** + * @param string $key + * + * @return string + */ + public function getNote(string $key): string { + } + + /** + * @param string $key + * + * @return array + */ + public function getNoteArray(string $key): array { + } + + /** + * @param string $key + * @param string $note + * + * @return $this + */ + public function setNote(string $key, string $note): self { + } + + /** + * @param string $key + * @param array $note + * + * @return $this + */ + public function setNoteArray(string $key, array $note): self { + } + + /** + * @param string $key + * @param JsonSerializable $obj + * + * @return $this + */ + public function setNoteObj(string $key, JsonSerializable $obj): self { + } + + + /** + * @param string $displayName + * + * @return Member + */ + public function setDisplayName(string $displayName): self { + } + + + /** + * @param int $displayUpdate + * + * @return Member + */ + public function setDisplayUpdate(int $displayUpdate): self { + } + + /** + * @return int + */ + public function getDisplayUpdate(): int { + } + + + /** + * @return string + */ + public function getDisplayName(): string { + } + + + /** + * @param string $contactId + * + * @return Member + */ + public function setContactId(string $contactId): self { + } + + /** + * @return string + */ + public function getContactId(): string { + } + + + /** + * @param string $contactMeta + * + * @return Member + */ + public function setContactMeta(string $contactMeta): self { + } + + /** + * @return string + */ + public function getContactMeta(): string { + } + + + /** + * @param Circle $circle + * + * @return self + */ + public function setCircle(Circle $circle): self { + } + + /** + * @return Circle + */ + public function getCircle(): Circle { + } + + /** + * @return bool + */ + public function hasCircle(): bool { + } + + + /** + * @param int $joined + * + * @return Member + */ + public function setJoined(int $joined): self { + } + + /** + * @return int + */ + public function getJoined(): int { + } + + + /** + * @return bool + */ + public function hasMemberships(): bool { + } + + /** + * @param array $memberships + * + * @return self + */ + public function setMemberships(array $memberships): IEntity { + } + + /** + * @return Membership[] + */ + public function getMemberships(): array { + } + + + /** + * @param string $singleId + * @param bool $detailed + * + * @return Membership + * @throws MembershipNotFoundException + * @throws RequestBuilderException + */ + public function getLink(string $singleId, bool $detailed = false): Membership { + } + + /** + * @param string $circleId + * @param bool $detailed + * + * @return Membership + * @throws MembershipNotFoundException + * @throws RequestBuilderException + * @deprecated - use getLink(); + */ + public function getMembership(string $circleId, bool $detailed = false): Membership { + } + + + /** + * @param Member $member + * @param bool $full + * + * @return bool + */ + public function compareWith(Member $member, bool $full = true): bool { + } + + + /** + * @param array $data + * + * @return $this + * @throws InvalidItemException + */ + public function import(array $data): IDeserializable { + } + + + /** + * @param array $data + * @param string $prefix + * + * @return IQueryRow + * @throws MemberNotFoundException + */ + public function importFromDatabase(array $data, string $prefix = ''): IQueryRow { + } + + + /** + * @return string[] + * @throws UnknownInterfaceException + */ + public function jsonSerialize(): array { + } + + + /** + * @param int $level + * + * @return int + * @throws ParseMemberLevelException + */ + public static function parseLevelInt(int $level): int { + } + + + /** + * @param string $levelString + * + * @return int + * @throws ParseMemberLevelException + */ + public static function parseLevelString(string $levelString): int { + } + + /** + * @param string $typeString + * + * @return int + * @throws UserTypeNotFoundException + */ + public static function parseTypeString(string $typeString): int { + } +}