From cc89e0350d545387e1f89aca854228d308ba597e Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 25 Feb 2026 12:38:53 +0100 Subject: [PATCH 1/2] feat(federation): assign labels Signed-off-by: grnd-alt --- appinfo/routes.php | 1 + lib/Controller/CardOcsController.php | 15 +++++++++++++++ lib/Service/ExternalBoardService.php | 12 ++++++++++++ src/components/card/CardSidebarTabDetails.vue | 1 + src/services/CardApi.js | 2 +- src/store/card.js | 1 + 6 files changed, 31 insertions(+), 1 deletion(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index d8eb2a3d60..334ebd9e98 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -141,6 +141,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' => 'stack_ocs#create', 'url' => '/api/v{apiVersion}/stacks', 'verb' => 'POST'], ['name' => 'stack_ocs#delete', 'url' => '/api/v{apiVersion}/stacks/{stackId}/{boardId}', 'verb' => 'DELETE', 'defaults' => ['boardId' => null]], diff --git a/lib/Controller/CardOcsController.php b/lib/Controller/CardOcsController.php index 48e6a99140..79483829e5 100644 --- a/lib/Controller/CardOcsController.php +++ b/lib/Controller/CardOcsController.php @@ -62,6 +62,21 @@ public function create(string $title, int $stackId, ?int $boardId = null, ?strin return new DataResponse($card); } + + #[NoAdminRequired] + #[PublicPage] + #[NoCSRFRequired] + public function assignLabel(?int $boardId, int $cardId, int $labelId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->assignLabelOnRemote($board, $cardId, $labelId)); + } + } + + return new DataResponse($this->cardService->assignLabel($cardId, $labelId)); + } + #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index 4f5aecb43b..22a6e8b77e 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -135,6 +135,18 @@ public function updateCardOnRemote( return $this->proxy->getOcsData($resp); } + public function assignLabelOnRemote(Board $localBoard, int $cardId, int $labelId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/label/' . $labelId; + $resp = $this->proxy->post($ownerCloudId->getId(), $shareToken, $url, [ + 'boardId' => $localBoard->getExternalId(), + ]); + return $this->proxy->getOcsData($resp); + } + public function createStackOnRemote( Board $localBoard, string $title, diff --git a/src/components/card/CardSidebarTabDetails.vue b/src/components/card/CardSidebarTabDetails.vue index e1d67e20d9..3a0e622d28 100644 --- a/src/components/card/CardSidebarTabDetails.vue +++ b/src/components/card/CardSidebarTabDetails.vue @@ -156,6 +156,7 @@ export default { const data = { card: this.copiedCard, labelId: newLabel.id, + boardId: this.copiedCard.boardId, } this.$store.dispatch('addLabel', data) }, diff --git a/src/services/CardApi.js b/src/services/CardApi.js index 4d772b1561..d06195c18d 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -201,7 +201,7 @@ export class CardApi { } assignLabelToCard(data) { - return axios.post(this.url(`/cards/${data.card.id}/label/${data.labelId}`)) + return axios.post(this.ocsUrl(`/cards/${data.card.id}/label/${data.labelId}`), { boardId: data.boardId ? data.boardId : null }) .then( (response) => { return Promise.resolve(response.data) diff --git a/src/store/card.js b/src/store/card.js index e2ceafa154..9e7d25f3ea 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -350,6 +350,7 @@ export default function cardModuleFactory() { commit('removeUserFromCard', user) }, async addLabel({ commit }, data) { + data.boardId = this.state.currentBoard.id await apiClient.assignLabelToCard(data) commit('updateCardProperty', { property: 'labels', card: data.card }) }, From 62e1ca3f9a5846215671196c9827f3db0d326ffb Mon Sep 17 00:00:00 2001 From: grnd-alt Date: Wed, 25 Feb 2026 15:33:54 +0100 Subject: [PATCH 2/2] feat(federation): unassign labels Signed-off-by: grnd-alt --- appinfo/routes.php | 1 + lib/Controller/CardOcsController.php | 14 ++++++++++++++ lib/Service/ExternalBoardService.php | 12 ++++++++++++ src/services/CardApi.js | 10 +++++++--- src/store/card.js | 1 + 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 334ebd9e98..9752c36db9 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#removeLabel', 'url' => '/api/v{apiVersion}/cards/{cardId}/label/{labelId}', 'verb' => 'DELETE'], ['name' => 'stack_ocs#create', 'url' => '/api/v{apiVersion}/stacks', 'verb' => 'POST'], ['name' => 'stack_ocs#delete', 'url' => '/api/v{apiVersion}/stacks/{stackId}/{boardId}', 'verb' => 'DELETE', 'defaults' => ['boardId' => null]], diff --git a/lib/Controller/CardOcsController.php b/lib/Controller/CardOcsController.php index 79483829e5..d0a4fe70ec 100644 --- a/lib/Controller/CardOcsController.php +++ b/lib/Controller/CardOcsController.php @@ -77,6 +77,20 @@ public function assignLabel(?int $boardId, int $cardId, int $labelId): DataRespo return new DataResponse($this->cardService->assignLabel($cardId, $labelId)); } + #[NoAdminRequired] + #[PublicPage] + #[NoCSRFRequired] + public function removeLabel(?int $boardId, int $cardId, int $labelId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->removeLabelOnRemote($board, $cardId, $labelId)); + } + } + + return new DataResponse($this->cardService->removeLabel($cardId, $labelId)); + } + #[NoAdminRequired] #[PublicPage] #[NoCSRFRequired] diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index 22a6e8b77e..6125c63b30 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -147,6 +147,18 @@ public function assignLabelOnRemote(Board $localBoard, int $cardId, int $labelId return $this->proxy->getOcsData($resp); } + public function removeLabelOnRemote(Board $localBoard, int $cardId, int $labelId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/label/' . $labelId; + $resp = $this->proxy->delete($ownerCloudId->getId(), $shareToken, $url, [ + 'boardId' => $localBoard->getExternalId(), + ]); + return $this->proxy->getOcsData($resp); + } + public function createStackOnRemote( Board $localBoard, string $title, diff --git a/src/services/CardApi.js b/src/services/CardApi.js index d06195c18d..d9491f1f52 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -204,7 +204,7 @@ export class CardApi { return axios.post(this.ocsUrl(`/cards/${data.card.id}/label/${data.labelId}`), { boardId: data.boardId ? data.boardId : null }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) @@ -216,10 +216,14 @@ export class CardApi { } removeLabelFromCard(data) { - return axios.delete(this.url(`/cards/${data.card.id}/label/${data.labelId}`)) + return axios.delete(this.ocsUrl(`/cards/${data.card.id}/label/${data.labelId}`), { + data: { + boardId: data.boardId ? data.boardId : null, + }, + }) .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 9e7d25f3ea..e9d0098a96 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -355,6 +355,7 @@ export default function cardModuleFactory() { commit('updateCardProperty', { property: 'labels', card: data.card }) }, async removeLabel({ commit }, data) { + data.boardId = this.state.currentBoard.id await apiClient.removeLabelFromCard(data) commit('updateCardProperty', { property: 'labels', card: data.card }) },