From d1c46d434a0669249252adc0b022a44f174959a9 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Tue, 3 Mar 2026 10:44:16 +0100 Subject: [PATCH 1/2] feat(federation): assign users Signed-off-by: grnd-alt --- appinfo/routes.php | 1 + lib/Controller/BoardOcsController.php | 2 - lib/Controller/CardOcsController.php | 15 +++++ lib/Db/Assignment.php | 1 + lib/Db/AssignmentMapper.php | 10 +++- lib/Db/FederatedUser.php | 8 +++ lib/Service/AssignmentService.php | 2 +- lib/Service/ExternalBoardService.php | 76 ++++++++++++++++++++++++ lib/Service/PermissionService.php | 7 +++ src/components/cards/AvatarList.vue | 6 ++ src/components/cards/CardMenuEntries.vue | 2 +- src/services/CardApi.js | 6 +- src/store/card.js | 3 +- src/store/main.js | 2 +- 14 files changed, 131 insertions(+), 10 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 9752c36db9..54bd08f2db 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -142,6 +142,7 @@ ['name' => 'card_ocs#create', 'url' => '/api/v{apiVersion}/cards', 'verb' => 'POST'], ['name' => 'card_ocs#update', 'url' => '/api/v{apiVersion}/cards/{cardId}', 'verb' => 'PUT'], ['name' => 'card_ocs#assignLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'POST'], + ['name' => 'card_ocs#assignUser', 'url' => '/api/v{apiVersion}/cards/{cardId}/assign', 'verb' => 'POST'], ['name' => 'card_ocs#removeLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'], ['name' => 'stack_ocs#create', 'url' => '/api/v{apiVersion}/stacks', 'verb' => 'POST'], diff --git a/lib/Controller/BoardOcsController.php b/lib/Controller/BoardOcsController.php index 5786939039..84f28e1008 100644 --- a/lib/Controller/BoardOcsController.php +++ b/lib/Controller/BoardOcsController.php @@ -43,12 +43,10 @@ public function index(): DataResponse { #[NoCSRFRequired] #[RequestHeader(name: 'x-nextcloud-federation', description: 'Set to 1 when the request is performed by another Nextcloud Server to indicate a federation request', indirect: true)] public function read(int $boardId): DataResponse { - // Board on this instance -> get it from database $localBoard = $this->boardService->find($boardId, true, true); if ($localBoard->getExternalId() !== null) { return $this->externalBoardService->getExternalBoardFromRemote($localBoard); } - // Board on other instance -> get it from other instance return new DataResponse($localBoard); } diff --git a/lib/Controller/CardOcsController.php b/lib/Controller/CardOcsController.php index d0a4fe70ec..c980664aa6 100644 --- a/lib/Controller/CardOcsController.php +++ b/lib/Controller/CardOcsController.php @@ -8,6 +8,7 @@ namespace OCA\Deck\Controller; use OCA\Deck\Model\OptionalNullableValue; +use OCA\Deck\Service\AssignmentService; use OCA\Deck\Service\BoardService; use OCA\Deck\Service\CardService; use OCA\Deck\Service\ExternalBoardService; @@ -25,6 +26,7 @@ public function __construct( string $appName, IRequest $request, private CardService $cardService, + private AssignmentService $assignmentService, private StackService $stackService, private BoardService $boardService, private ExternalBoardService $externalBoardService, @@ -77,6 +79,19 @@ public function assignLabel(?int $boardId, int $cardId, int $labelId): DataRespo return new DataResponse($this->cardService->assignLabel($cardId, $labelId)); } + #[NoAdminRequired] + #[PublicPage] + #[NoCSRFRequired] + public function assignUser(?int $boardId, int $cardId, string $userId, int $type = 0): DataResponse { + if ($boardId) { + $localBoard = $this->boardService->find($boardId, false); + if ($localBoard->getExternalId()) { + return new DataResponse($this->externalBoardService->assignUserOnRemote($localBoard, $cardId, $userId, $type)); + } + } + return new DataResponse($this->assignmentService->assignUser($cardId, $userId, $type)); + } + #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] diff --git a/lib/Db/Assignment.php b/lib/Db/Assignment.php index 4160aed12d..ee4062bd10 100644 --- a/lib/Db/Assignment.php +++ b/lib/Db/Assignment.php @@ -17,6 +17,7 @@ class Assignment extends RelationalEntity implements JsonSerializable { public const TYPE_USER = Acl::PERMISSION_TYPE_USER; public const TYPE_GROUP = Acl::PERMISSION_TYPE_GROUP; public const TYPE_CIRCLE = Acl::PERMISSION_TYPE_CIRCLE; + public const TYPE_REMOTE = Acl::PERMISSION_TYPE_REMOTE; public function __construct() { $this->addType('id', 'integer'); diff --git a/lib/Db/AssignmentMapper.php b/lib/Db/AssignmentMapper.php index da1af6405a..8cbb43c61f 100644 --- a/lib/Db/AssignmentMapper.php +++ b/lib/Db/AssignmentMapper.php @@ -13,6 +13,7 @@ use OCA\Deck\Service\CirclesService; use OCP\AppFramework\Db\Entity; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Federation\ICloudIdManager; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IUserManager; @@ -29,13 +30,17 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper { /** @var CirclesService */ private $circleService; - public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager, IGroupManager $groupManager, CirclesService $circleService) { + /** @var ICloudIdManager */ + private $cloudIdManager; + + public function __construct(IDBConnection $db, CardMapper $cardMapper, IUserManager $userManager, IGroupManager $groupManager, CirclesService $circleService, ICloudIdManager $cloudIdManager) { parent::__construct($db, 'deck_assigned_users', Assignment::class); $this->cardMapper = $cardMapper; $this->userManager = $userManager; $this->groupManager = $groupManager; $this->circleService = $circleService; + $this->cloudIdManager = $cloudIdManager; } public function findAll(int $cardId): array { @@ -175,6 +180,9 @@ private function getOrigin(Assignment $assignment) { $origin = $this->circleService->getCircle($assignment->getParticipant()); return $origin ? new Circle($origin) : null; } + if ($assignment->getType() === Assignment::TYPE_REMOTE) { + return new FederatedUser($this->cloudIdManager->resolveCloudId($assignment->getParticipant())); + } return null; } diff --git a/lib/Db/FederatedUser.php b/lib/Db/FederatedUser.php index c48c9eb81a..31f2bfff0d 100644 --- a/lib/Db/FederatedUser.php +++ b/lib/Db/FederatedUser.php @@ -17,6 +17,14 @@ public function __construct(ICloudId $cloudId) { parent::__construct($cloudId->getId(), $cloudId); } + public function getCloudId(): ICloudId { + return $this->cloudId; + } + + public function getUID(): string { + return $this->cloudId->getId(); + } + public function getObjectSerialization(): array { return [ 'uid' => $this->cloudId->getId(), diff --git a/lib/Service/AssignmentService.php b/lib/Service/AssignmentService.php index 4d9a97486d..89849ee471 100644 --- a/lib/Service/AssignmentService.php +++ b/lib/Service/AssignmentService.php @@ -99,7 +99,7 @@ public function __construct( public function assignUser(int $cardId, string $userId, int $type = Assignment::TYPE_USER): Assignment { $this->assignmentServiceValidator->check(compact('cardId', 'userId')); - if ($type !== Assignment::TYPE_USER && $type !== Assignment::TYPE_GROUP) { + if ($type !== Assignment::TYPE_USER && $type !== Assignment::TYPE_GROUP && $type !== Assignment::TYPE_REMOTE) { throw new BadRequestException('Invalid type provided for assignemnt'); } diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index 6125c63b30..3aad6cbd13 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -7,13 +7,18 @@ namespace OCA\Deck\Service; +use OC\Federation\CloudIdManager; use OCA\Deck\Db\Acl; +use OCA\Deck\Db\Assignment; use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; +use OCA\Deck\Db\FederatedUser; +use OCA\Deck\Db\User; use OCA\Deck\Federation\DeckFederationProxy; use OCA\Deck\Model\OptionalNullableValue; use OCP\AppFramework\Http\DataResponse; use OCP\Federation\ICloudIdManager; +use OCP\IURLGenerator; use OCP\IUserManager; class ExternalBoardService { @@ -25,6 +30,7 @@ public function __construct( private BoardService $boardService, private PermissionService $permissionService, private BoardMapper $boardMapper, + private IURLGenerator $urlGenerator, private ?string $userId, ) { } @@ -50,9 +56,37 @@ public function getExternalStacksFromRemote(Board $localBoard):DataResponse { return new DataResponse($this->LocalizeRemoteStacks($ocs, $localBoard)); } + public function localizeRemoteUser(Board $localBoard, array $user): array | User | FederatedUser | null { + // skip invalid users + if (!$user['uid']) { + return null;; + } + // if it's already a valid cloud id the user originates from a third instance and we pass it as is + if ($this->cloudIdManager->isValidCloudId($user['uid'])) { + if ($user['remote'] == $this->urlGenerator->getBaseUrl()) { + // local user from remote: return as local user + $localuid = $this->cloudIdManager->resolveCloudId($user['uid'])->getUser(); + return new User($localuid, $this->userManager); + } + return new FederatedUser($this->cloudIdManager->resolveCloudId($user['uid'])); + } + // if it's not a valid cloud id: it originates from the remote instance and we send it out as a federated user + $owner = $localBoard->resolveOwner(); // retrieve owner to get the remote + if ($owner instanceof FederatedUser) { + return new FederatedUser($this->cloudIdManager->getCloudId($user['uid'], $owner->getCloudId()->getRemote())); + } + return null; + } + public function LocalizeRemoteStacks(array $stacks, Board $localBoard) { foreach ($stacks as $i => $stack) { $stack['boardId'] = $localBoard->getId(); + foreach($stack['cards'] as $j => $card) { + $stack['cards'][$j]['assignedUsers'] = array_map(function ($assignment) use ($localBoard) { + $assignment['participant'] = $this->localizeRemoteUser($localBoard, $assignment['participant']); + return $assignment; + }, $card['assignedUsers']); + } $stacks[$i] = $stack; } return $stacks; @@ -63,9 +97,19 @@ public function LocalizeRemoteBoard(array $remoteBoard, Board $localBoard) { $remoteBoard['owner'] = $localBoard->resolveOwner(); $remoteBoard['acl'] = $localBoard->getAcl(); $remoteBoard['permissions'] = $localBoard->getPermissions(); + $remoteBoard['users'] = $this->localizeRemoteUsers($remoteBoard['users'], $localBoard); return $remoteBoard; } + public function localizeRemoteUsers(array $users, Board $localBoard) { + $localizedUsers = []; + foreach ($users as $i => $user) { + $localizedUsers[] = $this->localizeRemoteUser($localBoard, $user); + } + + return $localizedUsers; + } + public function createCardOnRemote( Board $localBoard, string $title, @@ -159,6 +203,38 @@ public function removeLabelOnRemote(Board $localBoard, int $cardId, int $labelId return $this->proxy->getOcsData($resp); } + public function assignUserOnRemote(Board $localBoard, int $cardId, string $userId, int $type = 0): array { + $this->configService->ensureFederationEnabled(); + + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + + if ($this->cloudIdManager->isValidCloudId($userId)) { + $cloudId = $this->cloudIdManager->resolveCloudId($userId); + // assignee's origin is the same as the board owner's origin: send as local user + if ($cloudId->getRemote() === $ownerCloudId->getRemote()) { + $userId = $cloudId->getUser(); + $type = Assignment::TYPE_USER; + } + } else { + // local user for us = remote user for remote + $userId = $this->cloudIdManager->getCloudId($userId, null)->getId(); + $type = Assignment::TYPE_REMOTE; + } + $shareToken = $localBoard->getShareToken(); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/assign'; + $resp = $this->proxy->post($ownerCloudId->getId(), $shareToken, $url, [ + 'userId' => $userId, + 'type' => $type, + 'boardId' => $localBoard->getExternalId(), + ]); + $result = $this->proxy->getOcsData($resp); + if (isset($result['participant'])) { + $result['participant'] = $this->localizeRemoteUser($localBoard, $result['participant']); + } + return $result; + } + + public function createStackOnRemote( Board $localBoard, string $title, diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index ba99ec44ac..0f4f4c794c 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -13,12 +13,14 @@ use OCA\Deck\Db\Board; use OCA\Deck\Db\BoardMapper; use OCA\Deck\Db\CardMapper; +use OCA\Deck\Db\FederatedUser; use OCA\Deck\Db\IPermissionMapper; use OCA\Deck\Db\User; use OCA\Deck\NoPermissionException; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\Cache\CappedMemoryCache; +use OCP\Federation\ICloudIdManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IUserManager; @@ -47,6 +49,7 @@ public function __construct( private IGroupManager $groupManager, private IManager $shareManager, private IConfig $config, + private ICloudIdManager $cloudIdManager, private ?string $userId, ) { $this->boardCache = new CappedMemoryCache(); @@ -306,6 +309,10 @@ public function findUsers($boardId, $refresh = false) { } } + if ($acl->getType() === Acl::PERMISSION_TYPE_REMOTE) { + $users[(string)$acl->getParticipant()] = new FederatedUser($this->cloudIdManager->resolveCloudId($acl->getParticipant())); + } + if ($this->circlesService->isCirclesEnabled() && $acl->getType() === Acl::PERMISSION_TYPE_CIRCLE) { try { $circle = $this->circlesService->getCircle($acl->getParticipant()); diff --git a/src/components/cards/AvatarList.vue b/src/components/cards/AvatarList.vue index e2d4a8d479..34f29739bf 100644 --- a/src/components/cards/AvatarList.vue +++ b/src/components/cards/AvatarList.vue @@ -19,6 +19,12 @@ :disable-menu="true" :hide-status="true" :size="32" /> + item.type === 0 && item.participant.uid === getCurrentUser()?.uid) + return this.card.assignedUsers.find((item) => (item.type === 0 || item.type === 6) && item.participant.uid === getCurrentUser()?.uid) }, boardId() { return this.card?.boardId ? this.card.boardId : Number(this.$route.params.id) diff --git a/src/services/CardApi.js b/src/services/CardApi.js index d9491f1f52..aca69af1db 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -110,11 +110,11 @@ export class CardApi { }) } - assignUser(cardId, id, type) { - return axios.post(this.url(`/cards/${cardId}/assign`), { userId: id, type }) + assignUser(cardId, id, type, boardId) { + return axios.post(this.ocsUrl(`/cards/${cardId}/assign`), { userId: id, type, boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) diff --git a/src/store/card.js b/src/store/card.js index e9d0098a96..2790682a50 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -342,7 +342,8 @@ export default function cardModuleFactory() { commit('updateCardProperty', { property: 'done', card: updatedCard }) }, async assignCardToUser({ commit }, { card, assignee }) { - const user = await apiClient.assignUser(card.id, assignee.userId, assignee.type) + const boardId = this.state.currentBoard.id + const user = await apiClient.assignUser(card.id, assignee.userId, assignee.type, boardId) commit('assignCardToUser', user) }, async removeUserFromCard({ commit }, { card, assignee }) { diff --git a/src/store/main.js b/src/store/main.js index 857d31073c..9fddd8e4b8 100644 --- a/src/store/main.js +++ b/src/store/main.js @@ -87,7 +87,7 @@ export default function storeFactory() { }, assignables: state => { return [ - ...state.assignableUsers.map((user) => ({ ...user, type: 0 })), + ...state.assignableUsers.map((user) => ({ ...user, type: user.type })), ...state.currentBoard.acl.filter((acl) => acl.type === 1 && typeof acl.participant === 'object').map((group) => ({ ...group.participant, type: 1 })), ...state.currentBoard.acl.filter((acl) => acl.type === 7 && typeof acl.participant === 'object').map((circle) => ({ ...circle.participant, type: 7 })), ] From 275463aeee0e52911f28f178ced26c6d371be7b9 Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Tue, 3 Mar 2026 11:06:21 +0100 Subject: [PATCH 2/2] feat(federation): unassign users Signed-off-by: grnd-alt --- appinfo/routes.php | 1 + lib/Controller/CardOcsController.php | 13 ++++++++ lib/Service/ExternalBoardService.php | 33 +++++++++++++++++--- lib/Service/PermissionService.php | 4 +-- src/components/board/SharingTabSidebar.vue | 3 +- src/services/CardApi.js | 6 ++-- src/store/card.js | 3 +- tests/unit/Service/PermissionServiceTest.php | 4 +++ 8 files changed, 56 insertions(+), 11 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 54bd08f2db..675f5257c5 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -143,6 +143,7 @@ ['name' => 'card_ocs#update', 'url' => '/api/v{apiVersion}/cards/{cardId}', 'verb' => 'PUT'], ['name' => 'card_ocs#assignLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'POST'], ['name' => 'card_ocs#assignUser', 'url' => '/api/v{apiVersion}/cards/{cardId}/assign', 'verb' => 'POST'], + ['name' => 'card_ocs#unAssignUser', 'url' => '/api/v{apiVersion}/cards/{cardId}/unassign', 'verb' => 'PUT'], ['name' => 'card_ocs#removeLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'], ['name' => 'stack_ocs#create', 'url' => '/api/v{apiVersion}/stacks', 'verb' => 'POST'], diff --git a/lib/Controller/CardOcsController.php b/lib/Controller/CardOcsController.php index c980664aa6..9e18697347 100644 --- a/lib/Controller/CardOcsController.php +++ b/lib/Controller/CardOcsController.php @@ -92,6 +92,19 @@ public function assignUser(?int $boardId, int $cardId, string $userId, int $type return new DataResponse($this->assignmentService->assignUser($cardId, $userId, $type)); } + #[NoAdminRequired] + #[PublicPage] + #[NoCSRFRequired] + public function unAssignUser(?int $boardId, int $cardId, string $userId, int $type = 0): DataResponse { + if ($boardId) { + $localBoard = $this->boardService->find($boardId, false); + if ($localBoard->getExternalId()) { + return new DataResponse($this->externalBoardService->unAssignUserOnRemote($localBoard, $cardId, $userId, $type)); + } + } + return new DataResponse($this->assignmentService->unAssignUser($cardId, $userId, $type)); + } + #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index 3aad6cbd13..437b03cafe 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -7,7 +7,6 @@ namespace OCA\Deck\Service; -use OC\Federation\CloudIdManager; use OCA\Deck\Db\Acl; use OCA\Deck\Db\Assignment; use OCA\Deck\Db\Board; @@ -56,10 +55,10 @@ public function getExternalStacksFromRemote(Board $localBoard):DataResponse { return new DataResponse($this->LocalizeRemoteStacks($ocs, $localBoard)); } - public function localizeRemoteUser(Board $localBoard, array $user): array | User | FederatedUser | null { + public function localizeRemoteUser(Board $localBoard, array $user): array|User|FederatedUser|null { // skip invalid users if (!$user['uid']) { - return null;; + return null; } // if it's already a valid cloud id the user originates from a third instance and we pass it as is if ($this->cloudIdManager->isValidCloudId($user['uid'])) { @@ -81,7 +80,7 @@ public function localizeRemoteUser(Board $localBoard, array $user): array | User public function LocalizeRemoteStacks(array $stacks, Board $localBoard) { foreach ($stacks as $i => $stack) { $stack['boardId'] = $localBoard->getId(); - foreach($stack['cards'] as $j => $card) { + foreach ($stack['cards'] as $j => $card) { $stack['cards'][$j]['assignedUsers'] = array_map(function ($assignment) use ($localBoard) { $assignment['participant'] = $this->localizeRemoteUser($localBoard, $assignment['participant']); return $assignment; @@ -234,6 +233,32 @@ public function assignUserOnRemote(Board $localBoard, int $cardId, string $userI return $result; } + public function unAssignUserOnRemote(Board $localBoard, int $cardId, string $userId, int $type = 0): array { + $this->configService->ensureFederationEnabled(); + + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + + if ($this->cloudIdManager->isValidCloudId($userId)) { + $cloudId = $this->cloudIdManager->resolveCloudId($userId); + // assignee's origin is the same as the board owner's origin: send as local user + if ($cloudId->getRemote() === $ownerCloudId->getRemote()) { + $userId = $cloudId->getUser(); + $type = Assignment::TYPE_USER; + } + } else { + // local user for us = remote user for remote + $userId = $this->cloudIdManager->getCloudId($userId, null)->getId(); + $type = Assignment::TYPE_REMOTE; + } + $shareToken = $localBoard->getShareToken(); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/unassign'; + $resp = $this->proxy->put($ownerCloudId->getId(), $shareToken, $url, [ + 'userId' => $userId, + 'type' => $type, + 'boardId' => $localBoard->getExternalId(), + ]); + return $this->proxy->getOcsData($resp); + } public function createStackOnRemote( Board $localBoard, diff --git a/lib/Service/PermissionService.php b/lib/Service/PermissionService.php index 0f4f4c794c..4de5327367 100644 --- a/lib/Service/PermissionService.php +++ b/lib/Service/PermissionService.php @@ -29,7 +29,7 @@ class PermissionService { - /** @var array> */ + /** @var array> */ private $users = []; // accessToken to check permission for federated shares @@ -266,7 +266,7 @@ public function getUserId(): ?string { * * @param $boardId * @param $refresh - * @return array + * @return array * */ public function findUsers($boardId, $refresh = false) { // cache users of a board so we don't query them for every cards diff --git a/src/components/board/SharingTabSidebar.vue b/src/components/board/SharingTabSidebar.vue index 2effaba2cc..3d0a3de03e 100644 --- a/src/components/board/SharingTabSidebar.vue +++ b/src/components/board/SharingTabSidebar.vue @@ -90,7 +90,7 @@ const SOURCE_TO_SHARE_TYPE = { groups: 1, emails: 4, remotes: 6, - teams: 7, + circles: 7, } export default { @@ -139,6 +139,7 @@ export default { displayName: item.displayname || item.name || item.label || item.id, user: item.id, subname: item.shareWithDisplayNameUnique || item.subline || item.id, // NcSelectUser does its own pattern matching to filter things out + type: SOURCE_TO_SHARE_TYPE[item.source] } return res }).slice(0, 10) diff --git a/src/services/CardApi.js b/src/services/CardApi.js index aca69af1db..99b141a3f0 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -125,11 +125,11 @@ export class CardApi { }) } - removeUser(cardId, id, type) { - return axios.put(this.url(`/cards/${cardId}/unassign`), { userId: id, type }) + removeUser(cardId, id, type, boardId) { + return axios.put(this.ocsUrl(`/cards/${cardId}/unassign`), { userId: id, type, boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) diff --git a/src/store/card.js b/src/store/card.js index 2790682a50..2a2cacd483 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -347,7 +347,8 @@ export default function cardModuleFactory() { commit('assignCardToUser', user) }, async removeUserFromCard({ commit }, { card, assignee }) { - const user = await apiClient.removeUser(card.id, assignee.userId, assignee.type) + const boardId = this.state.currentBoard.id + const user = await apiClient.removeUser(card.id, assignee.userId, assignee.type, boardId) commit('removeUserFromCard', user) }, async addLabel({ commit }, data) { diff --git a/tests/unit/Service/PermissionServiceTest.php b/tests/unit/Service/PermissionServiceTest.php index 05139f5fa7..3099b9edd2 100644 --- a/tests/unit/Service/PermissionServiceTest.php +++ b/tests/unit/Service/PermissionServiceTest.php @@ -32,6 +32,7 @@ use OCA\Deck\Db\User; use OCA\Deck\NoPermissionException; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\Federation\ICloudIdManager; use OCP\IConfig; use OCP\IGroup; use OCP\IGroupManager; @@ -53,6 +54,7 @@ class PermissionServiceTest extends \Test\TestCase { private IGroupManager|MockObject $groupManager; private MockObject|IManager $shareManager; private IConfig|MockObject $config; + private ICloudIdManager|MockObject $cloudIdManager; public function setUp(): void { parent::setUp(); @@ -65,6 +67,7 @@ public function setUp(): void { $this->groupManager = $this->createMock(IGroupManager::class); $this->shareManager = $this->createMock(IManager::class); $this->config = $this->createMock(IConfig::class); + $this->cloudIdManager = $this->createMock(ICloudIdManager::class); $this->service = new PermissionService( $this->logger, @@ -76,6 +79,7 @@ public function setUp(): void { $this->groupManager, $this->shareManager, $this->config, + $this->cloudIdManager, 'admin' ); }