From dad066407bd5512f61e05f51bbf39780682d78f3 Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Mon, 13 Jul 2026 16:17:48 +0200 Subject: [PATCH 1/4] feat: add federated sharing Signed-off-by: Luka Trovic --- appinfo/routes.php | 6 + lib/Controller/CardOcsController.php | 64 +++++++- lib/Controller/CommentsApiController.php | 25 ++- lib/Controller/StackOcsController.php | 14 +- lib/Service/CardService.php | 2 +- lib/Service/CommentService.php | 5 +- lib/Service/ExternalBoardService.php | 149 +++++++++++++++++- .../card/CardSidebarTabComments.vue | 3 +- src/services/CardApi.js | 30 ++-- src/services/CommentApi.js | 7 +- src/services/StackApi.js | 4 +- src/store/card.js | 15 +- src/stores/comment.js | 7 +- 13 files changed, 294 insertions(+), 37 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 6e2c273087..f93104faad 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -139,16 +139,22 @@ ['name' => 'board_ocs#index', 'url' => '/api/v{apiVersion}/boards', 'verb' => 'GET'], ['name' => 'board_ocs#read', 'url' => '/api/v{apiVersion}/board/{boardId}', 'verb' => 'GET'], ['name' => 'stack_ocs#index', 'url' => '/api/v{apiVersion}/stacks/{boardId}', 'verb' => 'GET'], + ['name' => 'stack_ocs#getArchived', 'url' => '/api/v{apiVersion}/stacks/{boardId}/archived', 'verb' => 'GET'], ['name' => 'board_ocs#create', 'url' => '/api/v{apiVersion}/boards', 'verb' => 'POST'], ['name' => 'board_ocs#addAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl', 'verb' => 'POST'], ['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#delete', 'url' => '/api/v{apiVersion}/cards/{cardId}', 'verb' => 'DELETE'], ['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' => 'card_ocs#reorder', 'url' => '/api/v{apiVersion}/cards/{cardId}/reorder', 'verb' => 'PUT'], + ['name' => 'card_ocs#archive', 'url' => '/api/v{apiVersion}/cards/{cardId}/archive', 'verb' => 'PUT'], + ['name' => 'card_ocs#unarchive', 'url' => '/api/v{apiVersion}/cards/{cardId}/unarchive', 'verb' => 'PUT'], + ['name' => 'card_ocs#done', 'url' => '/api/v{apiVersion}/cards/{cardId}/done', 'verb' => 'PUT'], + ['name' => 'card_ocs#undone', 'url' => '/api/v{apiVersion}/cards/{cardId}/undone', 'verb' => 'PUT'], ['name' => 'card_ocs#assignDependentCard', 'url' => '/api/v{apiVersion}/cards/{cardId}/dependentCards/{dependentCardId}', 'verb' => 'POST'], ['name' => 'card_ocs#removeDependentCard', 'url' => '/api/v{apiVersion}/cards/{cardId}/dependentCards/{dependentCardId}', 'verb' => 'DELETE'], diff --git a/lib/Controller/CardOcsController.php b/lib/Controller/CardOcsController.php index 7ca27a0ef6..1358a03874 100644 --- a/lib/Controller/CardOcsController.php +++ b/lib/Controller/CardOcsController.php @@ -139,7 +139,9 @@ public function update(int $id, string $title, int $stackId, string $type, int $ $duedate, $deletedAt, $archived, - $done + $done, + $startdate, + $color, )); } @@ -159,6 +161,18 @@ public function update(int $id, string $title, int $stackId, string $type, int $ )); } + #[NoAdminRequired] + #[PublicPage] + public function delete(int $cardId, ?int $boardId = null): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->deleteCardOnRemote($board, $cardId)); + } + } + return new DataResponse($this->cardService->delete($cardId)); + } + #[NoAdminRequired] #[PublicPage] public function reorder(int $cardId, int $stackId, int $order, ?int $boardId): DataResponse { @@ -171,6 +185,30 @@ public function reorder(int $cardId, int $stackId, int $order, ?int $boardId): D return new DataResponse($this->cardService->reorder($cardId, $stackId, $order)); } + #[NoAdminRequired] + #[PublicPage] + public function archive(int $cardId, int $boardId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->archiveCardOnRemote($board, $cardId)); + } + } + return new DataResponse($this->cardService->archive($cardId)); + } + + #[NoAdminRequired] + #[PublicPage] + public function unarchive(int $cardId, int $boardId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->unarchiveCardOnRemote($board, $cardId)); + } + } + return new DataResponse($this->cardService->unarchive($cardId)); + } + #[NoAdminRequired] #[PublicPage] public function assignDependentCard(int $cardId, int $dependentCardId, ?int $boardId = null): DataResponse { @@ -194,4 +232,28 @@ public function removeDependentCard(int $cardId, int $dependentCardId, ?int $boa } return new DataResponse($this->cardService->removeDependentCard($cardId, $dependentCardId)); } + + #[NoAdminRequired] + #[PublicPage] + public function done(int $cardId, ?int $boardId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->setDoneCardOnRemote($board, $cardId)); + } + } + return new DataResponse($this->cardService->done($cardId)); + } + + #[NoAdminRequired] + #[PublicPage] + public function undone(int $cardId, ?int $boardId): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->setUndoneCardOnRemote($board, $cardId)); + } + } + return new DataResponse($this->cardService->undone($cardId)); + } } diff --git a/lib/Controller/CommentsApiController.php b/lib/Controller/CommentsApiController.php index 1794db5eae..db9bb8f589 100644 --- a/lib/Controller/CommentsApiController.php +++ b/lib/Controller/CommentsApiController.php @@ -7,9 +7,12 @@ namespace OCA\Deck\Controller; +use OCA\Deck\Service\BoardService; use OCA\Deck\Service\CommentService; +use OCA\Deck\Service\ExternalBoardService; use OCA\Deck\StatusException; use OCP\AppFramework\Http\Attribute\NoAdminRequired; +use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\OCSController; use OCP\IRequest; @@ -22,6 +25,9 @@ public function __construct( string $appName, IRequest $request, private CommentService $commentService, + private BoardService $boardService, + private ExternalBoardService $externalBoardService, + private ?string $userId, string $corsMethods = 'PUT, POST, GET, DELETE, PATCH', string $corsAllowedHeaders = 'Authorization, Content-Type, Accept', int $corsMaxAge = 1728000, @@ -33,7 +39,14 @@ public function __construct( * @throws StatusException */ #[NoAdminRequired] - public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse { + #[PublicPage] + public function list(int $cardId, int $limit = 20, int $offset = 0, ?int $boardId = null): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->getCardCommentsFromRemote($board, $cardId, $limit, $offset)); + } + } return $this->commentService->list($cardId, $limit, $offset); } @@ -41,7 +54,15 @@ public function list(int $cardId, int $limit = 20, int $offset = 0): DataRespons * @throws StatusException */ #[NoAdminRequired] - public function create(int $cardId, string $message, int $parentId = 0): DataResponse { + #[PublicPage] + public function create(int $cardId, string $message, int $parentId = 0, ?int $boardId = null): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->createCardCommentOnRemote($board, $cardId, $message, $parentId)); + } + } + return $this->commentService->create($cardId, $message, $parentId); } diff --git a/lib/Controller/StackOcsController.php b/lib/Controller/StackOcsController.php index cada24f8e5..ecf73ae175 100644 --- a/lib/Controller/StackOcsController.php +++ b/lib/Controller/StackOcsController.php @@ -48,7 +48,7 @@ public function create(string $title, int $boardId, int $order = 0):DataResponse } else { $stack = $this->stackService->create($title, $boardId, $order); return new DataResponse($stack); - }; + } } #[NoAdminRequired] @@ -91,4 +91,16 @@ public function reorder(int $stackId, int $order, ?int $boardId):DataResponse { return new DataResponse($stacks); } + #[NoAdminRequired] + #[PublicPage] + public function getArchived(int $boardId): DataResponse { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + $stacks = $this->externalBoardService->getArchivedStacksFromRemote($board); + return new DataResponse($stacks); + } + $stacks = $this->stackService->findAllArchived($boardId); + return new DataResponse($stacks); + } + } diff --git a/lib/Service/CardService.php b/lib/Service/CardService.php index 5d30ccaafc..baf3e00d23 100644 --- a/lib/Service/CardService.php +++ b/lib/Service/CardService.php @@ -85,7 +85,7 @@ public function enrichCards(array $cards): array { // TODO We should find a better way just to get the comment count so we can save 1-3 queries per card here $countComments = $this->commentsManager->getNumberOfCommentsForObject('deckCard', (string)$card->getId()); - $lastRead = $countComments > 0 ? $this->commentsManager->getReadMark('deckCard', (string)$card->getId(), $user) : null; + $lastRead = $countComments > 0 && $user ? $this->commentsManager->getReadMark('deckCard', (string)$card->getId(), $user) : null; $countUnreadComments = $lastRead ? $this->commentsManager->getNumberOfCommentsForObject('deckCard', (string)$card->getId(), $lastRead) : 0; $card->setCommentsUnread($countUnreadComments); $card->setCommentsCount($countComments); diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index e5d4bc140e..66487f64f5 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -18,6 +18,7 @@ use OCP\Comments\ICommentsManager; use OCP\Comments\MessageTooLongException; use OCP\Comments\NotFoundException as CommentNotFoundException; +use OCP\Federation\ICloudIdManager; use OCP\IUserManager; use OutOfBoundsException; use Psr\Log\LoggerInterface; @@ -30,6 +31,7 @@ public function __construct( private CardMapper $cardMapper, private IUserManager $userManager, private LoggerInterface $logger, + private ICloudIdManager $cloudIdManager, private ?string $userId, ) { } @@ -110,7 +112,7 @@ public function create(int $cardId, string $message, int $replyTo = 0): DataResp } try { - $comment = $this->commentsManager->create('users', $this->userId, Application::COMMENT_ENTITY_TYPE, (string)$cardId); + $comment = $this->commentsManager->create('users', $this->userId ?? $this->permissionService->getUserId(), Application::COMMENT_ENTITY_TYPE, (string)$cardId); $comment->setMessage($message); $comment->setVerb('comment'); $comment->setParentId((string)$replyTo); @@ -163,6 +165,7 @@ private function formatComment(IComment $comment, bool $addReplyTo = false): arr 'objectId' => (int)$comment->getObjectId(), 'message' => $comment->getMessage(), 'actorId' => $comment->getActorId(), + 'actorRemote' => $this->cloudIdManager->isValidCloudId($comment->getActorId()) ? $this->cloudIdManager->resolveCloudId($comment->getActorId())->getRemote() : null, 'actorType' => $comment->getActorType(), 'actorDisplayName' => $actorDisplayName, 'creationDateTime' => $comment->getCreationDateTime()->format(\DateTime::ATOM), diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index fd48517ec1..ce8b5ae239 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -85,7 +85,7 @@ public function LocalizeRemoteStacks(array $stacks, Board $localBoard) { $stack['cards'][$j]['assignedUsers'] = array_map(function ($assignment) use ($localBoard) { $assignment['participant'] = $this->localizeRemoteUser($localBoard, $assignment['participant']); return $assignment; - }, $card['assignedUsers']); + }, $card['assignedUsers'] ?? []); } $stacks[$i] = $stack; } @@ -110,6 +110,36 @@ public function localizeRemoteUsers(array $users, Board $localBoard) { return $localizedUsers; } + public function localizeRemoteComments(Board $localBoard, array $comments): array { + foreach ($comments as $i => $comment) { + $localizedActor = $this->localizeRemoteUser($localBoard, ['uid' => $comment['actorId'], 'remote' => $comment['actorRemote']]); + if ($localizedActor instanceof FederatedUser) { + $comments[$i]['actorDisplayName'] = $localizedActor->getCloudId()->getId(); + $comments[$i]['actorId'] = $localizedActor->getCloudId()->getId(); + $comments[$i]['actorRemote'] = $localizedActor->getCloudId()->getRemote(); + } + if ($localizedActor instanceof User) { + $comments[$i]['actorDisplayName'] = $localizedActor->getDisplayName(); + $comments[$i]['actorId'] = $localizedActor->getUID(); + $comments[$i]['actorRemote'] = null; + } + if ($comment['replyTo']) { + $localizedReplyActor = $this->localizeRemoteUser($localBoard, ['uid' => $comment['replyTo']['actorId'], 'remote' => $comment['replyTo']['actorRemote']]); + if ($localizedReplyActor instanceof FederatedUser) { + $comments[$i]['replyTo']['actorDisplayName'] = $localizedReplyActor->getCloudId()->getId(); + $comments[$i]['replyTo']['actorId'] = $localizedReplyActor->getCloudId()->getId(); + $comments[$i]['replyTo']['actorRemote'] = $localizedReplyActor->getCloudId()->getRemote(); + } + if ($localizedReplyActor instanceof User) { + $comments[$i]['replyTo']['actorDisplayName'] = $localizedReplyActor->getDisplayName(); + $comments[$i]['replyTo']['actorId'] = $localizedReplyActor->getUID(); + $comments[$i]['replyTo']['actorRemote'] = null; + } + } + } + return $comments; + } + public function createCardOnRemote( Board $localBoard, string $title, @@ -154,6 +184,8 @@ public function updateCardOnRemote( ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null, + ?string $startdate = null, + ?string $color = null, ): array { $this->configService->ensureFederationEnabled(); $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); @@ -173,12 +205,26 @@ public function updateCardOnRemote( 'deletedAt' => $deletedAt, 'archived' => $archived, 'done' => $done->getValue() ?? null, + 'startdate' => $startdate, + 'color' => $color, 'boardId' => $localBoard->getExternalId(), ]; $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); return $this->proxy->getOcsData($resp); } + public function deleteCardOnRemote(Board $localBoard, int $cardId): 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; + $resp = $this->proxy->delete($ownerCloudId->getId(), $shareToken, $url, [ + 'boardId' => $localBoard->getExternalId(), + ]); + 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); @@ -351,4 +397,105 @@ public function reorderStackOnRemote(Board $localBoard, int $stackId, int $order $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); return $this->proxy->getOcsData($resp); } + + public function getArchivedStacksFromRemote(Board $localBoard): array { + $this->configService->ensureFederationEnabled(); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/stacks/' . $localBoard->getExternalId() . '/archived'; + $resp = $this->proxy->get($participantCloudId->getId(), $shareToken, $url); + $ocs = $this->proxy->getOCSData($resp); + return $this->LocalizeRemoteStacks($ocs, $localBoard); + } + + public function archiveCardOnRemote(Board $localBoard, int $cardId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/archive'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + return $this->proxy->getOcsData($resp); + } + + public function unarchiveCardOnRemote(Board $localBoard, int $cardId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/unarchive'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + return $this->proxy->getOcsData($resp); + } + + public function setDoneCardOnRemote(Board $localBoard, int $cardId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/done'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + return $this->proxy->getOcsData($resp); + } + + public function setUndoneCardOnRemote(Board $localBoard, int $cardId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_EDIT, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/undone'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + return $this->proxy->getOcsData($resp); + } + + public function getCardCommentsFromRemote(Board $localBoard, int $cardId, int $limit = 20, int $offset = 0): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_READ, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/comments'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + 'limit' => $limit, + 'offset' => $offset, + ]; + $resp = $this->proxy->get($participantCloudId->getId(), $shareToken, $url, $params); + $comments = $this->proxy->getOcsData($resp); + return $this->localizeRemoteComments($localBoard, $comments); + } + + public function createCardCommentOnRemote(Board $localBoard, int $cardId, string $message, int $parentId = 0): DataResponse { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_READ, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/comments'; + $params = [ + 'boardId' => $localBoard->getExternalId(), + 'message' => $message, + 'parentId' => $parentId, + ]; + $resp = $this->proxy->post($participantCloudId->getId(), $shareToken, $url, $params); + $newComment = $this->proxy->getOcsData($resp); + return new DataResponse($this->localizeRemoteComments($localBoard, [$newComment])[0]); + } } diff --git a/src/components/card/CardSidebarTabComments.vue b/src/components/card/CardSidebarTabComments.vue index 64f8f1649c..24380684eb 100644 --- a/src/components/card/CardSidebarTabComments.vue +++ b/src/components/card/CardSidebarTabComments.vue @@ -114,7 +114,7 @@ export default { this.error = null this.isLoading = true try { - await this.commentStore.fetchComments({ cardId: this.card.id }) + await this.commentStore.fetchComments({ cardId: this.card.id, boardId: this.currentBoard.id }) this.isLoading = false if (this.card.commentsUnread > 0) { await this.commentStore.markCommentsAsRead(this.card.id) @@ -129,6 +129,7 @@ export default { const commentObj = { cardId: this.card.id, comment: content, + boardId: this.currentBoard.id, } await this.commentStore.createComment(commentObj) this.commentStore.setReplyTo(null) diff --git a/src/services/CardApi.js b/src/services/CardApi.js index 9057a738cc..5a51df9bef 100644 --- a/src/services/CardApi.js +++ b/src/services/CardApi.js @@ -50,11 +50,11 @@ export class CardApi { }) } - deleteCard(cardId) { - return axios.delete(this.url(`/cards/${cardId}`)) + deleteCard(card, boardId) { + return axios.delete(this.ocsUrl(`/cards/${card.id}?boardId=${boardId}`)) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) @@ -140,11 +140,11 @@ export class CardApi { }) } - archiveCard(card) { - return axios.put(this.url(`/cards/${card.id}/archive`)) + archiveCard(card, boardId) { + return axios.put(this.ocsUrl(`/cards/${card.id}/archive`), { boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) @@ -155,11 +155,11 @@ export class CardApi { }) } - unArchiveCard(card) { - return axios.put(this.url(`/cards/${card.id}/unarchive`)) + unArchiveCard(card, boardId) { + return axios.put(this.ocsUrl(`/cards/${card.id}/unarchive`), { boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) @@ -170,11 +170,11 @@ export class CardApi { }) } - markCardAsDone(card) { - return axios.put(this.url(`/cards/${card.id}/done`)) + markCardAsDone(card, boardId) { + return axios.put(this.ocsUrl(`/cards/${card.id}/done`), { boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) @@ -185,11 +185,11 @@ export class CardApi { }) } - markCardAsUndone(card) { - return axios.put(this.url(`/cards/${card.id}/undone`)) + markCardAsUndone(card, boardId) { + return axios.put(this.ocsUrl(`/cards/${card.id}/undone`), { boardId }) .then( (response) => { - return Promise.resolve(response.data) + return Promise.resolve(response.data.ocs.data) }, (err) => { return Promise.reject(err) diff --git a/src/services/CommentApi.js b/src/services/CommentApi.js index b353ffbc97..2a8cdd4da3 100644 --- a/src/services/CommentApi.js +++ b/src/services/CommentApi.js @@ -13,18 +13,19 @@ export class CommentApi { return generateRemoteUrl(url) } - async loadComments({ cardId, limit, offset }) { + async loadComments({ cardId, limit, offset, boardId }) { const api = await axios.get(generateOcsUrl(`apps/deck/api/v1.0/cards/${cardId}/comments`), { - params: { limit, offset }, + params: { limit, offset, boardId }, headers: { 'OCS-APIRequest': 'true' }, }) return api.data.ocs.data } - async createComment({ cardId, comment, replyTo }) { + async createComment({ cardId, comment, replyTo, boardId }) { const api = await axios.post(generateOcsUrl(`apps/deck/api/v1.0/cards/${cardId}/comments`), { message: `${comment}`, parentId: replyTo ? replyTo.id : null, + boardId: boardId, }) return api.data.ocs.data } diff --git a/src/services/StackApi.js b/src/services/StackApi.js index f5e26e45c6..ae040ec170 100644 --- a/src/services/StackApi.js +++ b/src/services/StackApi.js @@ -50,10 +50,10 @@ export class StackApi { } loadArchivedStacks(boardId) { - return axios.get(this.url(`/stacks/${boardId}/archived`)) + return axios.get(this.ocsUrl(`/stacks/${boardId}/archived`)) .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 8bd3748d89..a6ad1ae1ef 100644 --- a/src/store/card.js +++ b/src/store/card.js @@ -321,27 +321,30 @@ export default function cardModuleFactory() { commit('updateCardsReorder', Object.values(cards)) }) }, - async deleteCard({ commit }, card) { - await apiClient.deleteCard(card.id) + async deleteCard({ commit, getters }, card) { + const stack = getters.stackById(card.stackId) + await apiClient.deleteCard(card, stack.boardId) commit('deleteCard', card) useTrashbinStore().moveCardToTrash(card) }, - async archiveUnarchiveCard({ commit }, card) { + async archiveUnarchiveCard({ commit, getters }, card) { let call = 'archiveCard' if (card.archived === false) { call = 'unArchiveCard' } - const updatedCard = await apiClient[call](card) + const stack = getters.stackById(card.stackId) + const updatedCard = await apiClient[call](card, stack.boardId) commit('updateCard', updatedCard) }, - async changeCardDoneStatus({ commit, dispatch, rootState }, card) { + async changeCardDoneStatus({ commit, dispatch, rootState, getters }, card) { let call = 'markCardAsDone' if (card.done === false) { call = 'markCardAsUndone' } - const updatedCard = await apiClient[call](card) + const stack = getters.stackById(card.stackId) + const updatedCard = await apiClient[call](card, stack.boardId) commit('updateCardProperty', { property: 'done', card: updatedCard }) if (card.done !== false) { diff --git a/src/stores/comment.js b/src/stores/comment.js index 9526e77c48..b188b22a41 100644 --- a/src/stores/comment.js +++ b/src/stores/comment.js @@ -69,11 +69,12 @@ export const useCommentStore = defineStore('comment', { setReplyTo(comment) { this.replyTo = comment }, - async fetchComments({ cardId, offset }) { + async fetchComments({ cardId, offset, boardId }) { const comments = await apiClient.loadComments({ cardId, limit: COMMENT_FETCH_LIMIT, offset: offset || 0, + boardId, }) this.addComments({ cardId, comments }) @@ -87,8 +88,8 @@ export const useCommentStore = defineStore('comment', { await this.fetchComments({ cardId }) await this.fetchComments({ cardId, offset: this.getCommentsForCard(cardId).length }) }, - async createComment({ cardId, comment }) { - await apiClient.createComment({ cardId, comment, replyTo: this.replyTo }) + async createComment({ cardId, comment, boardId }) { + await apiClient.createComment({ cardId, comment, replyTo: this.replyTo, boardId }) await this.fetchComments({ cardId }) }, }, From b17dd13420d46135d03f531ebdb6004b7ffa4664 Mon Sep 17 00:00:00 2001 From: Luka Trovic Date: Fri, 17 Jul 2026 18:18:18 +0200 Subject: [PATCH 2/4] feat: add leaving board on remote & update users look and feel Signed-off-by: Luka Trovic --- appinfo/routes.php | 2 + lib/Controller/BoardController.php | 7 +- lib/Controller/BoardOcsController.php | 18 +++++ lib/Controller/CommentsApiController.php | 18 ++++- lib/Db/CardMapper.php | 12 +++- lib/Federation/DeckFederationProxy.php | 19 ++++- lib/Service/BoardService.php | 3 +- lib/Service/CommentService.php | 6 +- lib/Service/ExternalBoardService.php | 70 ++++++++++++++++++- src/components/board/SharingTabSidebar.vue | 10 +-- src/components/card/AssignmentSelector.vue | 10 ++- src/components/card/CardSidebar.vue | 7 +- .../card/CardSidebarTabComments.vue | 2 +- src/components/card/CommentItem.vue | 12 +++- src/components/cards/AvatarList.vue | 12 ++-- src/helpers/xml.js | 1 + src/services/BoardApi.js | 4 +- src/services/CardApi.js | 2 +- src/services/CommentApi.js | 11 +-- src/store/main.js | 7 ++ src/stores/comment.js | 12 ++-- 21 files changed, 204 insertions(+), 41 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index f93104faad..e208a347ca 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -141,7 +141,9 @@ ['name' => 'stack_ocs#index', 'url' => '/api/v{apiVersion}/stacks/{boardId}', 'verb' => 'GET'], ['name' => 'stack_ocs#getArchived', 'url' => '/api/v{apiVersion}/stacks/{boardId}/archived', 'verb' => 'GET'], ['name' => 'board_ocs#create', 'url' => '/api/v{apiVersion}/boards', 'verb' => 'POST'], + ['name' => 'board_ocs#update', 'url' => '/api/v{apiVersion}/boards/{boardId}', 'verb' => 'PUT'], ['name' => 'board_ocs#addAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl', 'verb' => 'POST'], + ['name' => 'board_ocs#leave', 'url' => '/api/v{apiVersion}/boards/{boardId}/leave', 'verb' => 'POST'], ['name' => 'card_ocs#create', 'url' => '/api/v{apiVersion}/cards', 'verb' => 'POST'], ['name' => 'card_ocs#update', 'url' => '/api/v{apiVersion}/cards/{cardId}', 'verb' => 'PUT'], diff --git a/lib/Controller/BoardController.php b/lib/Controller/BoardController.php index 5c43ab7b12..5383e0adae 100644 --- a/lib/Controller/BoardController.php +++ b/lib/Controller/BoardController.php @@ -67,7 +67,12 @@ public function deleteUndo(int $boardId): Board { #[NoAdminRequired] public function leave(int $boardId) { - return $this->boardService->leave($boardId); + $localBoard = $this->boardService->find($boardId, true, true); + $result = $this->boardService->leave($boardId); + if ($localBoard->getExternalId() !== null) { + $this->externalBoardService->leaveBoardOnRemote($localBoard); + } + return $result; } #[NoAdminRequired] diff --git a/lib/Controller/BoardOcsController.php b/lib/Controller/BoardOcsController.php index 2616194cd9..43d4d21438 100644 --- a/lib/Controller/BoardOcsController.php +++ b/lib/Controller/BoardOcsController.php @@ -49,6 +49,18 @@ public function create(string $title, string $color): DataResponse { return new DataResponse($this->boardService->create($title, $this->userId, $color)); } + #[NoAdminRequired] + #[PublicPage] + public function update(int $boardId, string $title, string $color, bool $archived): DataResponse { + $updatedBoard = $this->boardService->update($boardId, $title, $color, $archived); + + if ($updatedBoard->getExternalId()) { + return $this->externalBoardService->updateBoardOnRemote($updatedBoard, $title, $color, $archived); + } + + return new DataResponse($updatedBoard); + } + #[NoAdminRequired] public function addAcl(int $boardId, int $type, string $participant, bool $permissionEdit, bool $permissionShare, bool $permissionManage, ?string $remote = null): DataResponse { return new DataResponse($this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage)); @@ -58,4 +70,10 @@ public function addAcl(int $boardId, int $type, string $participant, bool $permi public function updateAcl(int $id, bool $permissionEdit, bool $permissionShare, bool $permissionManage): DataResponse { return new DataResponse($this->boardService->updateAcl($id, $permissionEdit, $permissionShare, $permissionManage)); } + + #[NoAdminRequired] + #[PublicPage] + public function leave(int $boardId): DataResponse { + return new DataResponse($this->boardService->leave($boardId)); + } } diff --git a/lib/Controller/CommentsApiController.php b/lib/Controller/CommentsApiController.php index db9bb8f589..8885660c2f 100644 --- a/lib/Controller/CommentsApiController.php +++ b/lib/Controller/CommentsApiController.php @@ -70,7 +70,14 @@ public function create(int $cardId, string $message, int $parentId = 0, ?int $bo * @throws StatusException */ #[NoAdminRequired] - public function update(int $cardId, int $commentId, string $message): DataResponse { + #[PublicPage] + public function update(int $cardId, int $commentId, string $message, ?int $boardId = null): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->updateCardCommentOnRemote($board, $cardId, $commentId, $message)); + } + } return $this->commentService->update($cardId, $commentId, $message); } @@ -78,7 +85,14 @@ public function update(int $cardId, int $commentId, string $message): DataRespon * @throws StatusException */ #[NoAdminRequired] - public function delete(int $cardId, int $commentId): DataResponse { + #[PublicPage] + public function delete(int $cardId, int $commentId, ?int $boardId = null): DataResponse { + if ($boardId) { + $board = $this->boardService->find($boardId, false); + if ($board->getExternalId()) { + return new DataResponse($this->externalBoardService->deleteCardCommentOnRemote($board, $cardId, $commentId)); + } + } return $this->commentService->delete($cardId, $commentId); } } diff --git a/lib/Db/CardMapper.php b/lib/Db/CardMapper.php index 47f3e58af3..f47c7a8b70 100644 --- a/lib/Db/CardMapper.php +++ b/lib/Db/CardMapper.php @@ -14,6 +14,7 @@ use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\Federation\ICloudIdManager; use OCP\ICache; use OCP\ICacheFactory; use OCP\IDBConnection; @@ -36,6 +37,8 @@ class CardMapper extends QBMapper implements IPermissionMapper { private $notificationManager; /** @var ICache */ private $cache; + /** @var ICloudIdManager */ + private $cloudIdManager; private $databaseType; private $database4ByteSupport; @@ -46,6 +49,7 @@ public function __construct( IGroupManager $groupManager, IManager $notificationManager, ICacheFactory $cacheFactory, + ICloudIdManager $cloudIdManager, $databaseType = 'sqlite3', $database4ByteSupport = true, ) { @@ -55,6 +59,7 @@ public function __construct( $this->groupManager = $groupManager; $this->notificationManager = $notificationManager; $this->cache = $cacheFactory->createDistributed('deck-cardMapper'); + $this->cloudIdManager = $cloudIdManager; $this->databaseType = $databaseType; $this->database4ByteSupport = $database4ByteSupport; } @@ -747,10 +752,15 @@ public function findBoardId(int $id): ?int { public function mapOwner(Card &$card) { $userManager = $this->userManager; - $card->resolveRelation('owner', function ($owner) use (&$userManager) { + $cloudIdManager = $this->cloudIdManager; + $card->resolveRelation('owner', function ($owner) use (&$userManager, &$cloudIdManager) { if ($userManager->userExists($owner)) { return new User($owner, $this->userManager); } + if ($cloudIdManager->isValidCloudId($owner)) { + $cloudId = $cloudIdManager->resolveCloudId($owner); + return new FederatedUser($cloudId); + } return null; }); } diff --git a/lib/Federation/DeckFederationProxy.php b/lib/Federation/DeckFederationProxy.php index 2ab77fd515..8386b918ca 100644 --- a/lib/Federation/DeckFederationProxy.php +++ b/lib/Federation/DeckFederationProxy.php @@ -10,6 +10,10 @@ use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ServerException; use OC\Http\Client\Response; +use OCA\Deck\BadRequestException; +use OCA\Deck\NoPermissionException; +use OCA\Deck\NotFoundException; +use OCA\Deck\StatusException; use OCP\AppFramework\Http; use OCP\Http\Client\IClientService; use OCP\Http\Client\IResponse; @@ -60,7 +64,7 @@ protected function prependProtocolIfNotAvailable(string $url): string { /** * @param 'get'|'post'|'put'|'delete' $verb - * @throws \Exception + * @throws \Exception|StatusException */ protected function request( string $verb, @@ -96,7 +100,18 @@ protected function request( $clientException = new \Exception($e->getMessage(), $status, $e); $this->logger->debug('Client error from remote', ['exception' => $clientException]); - return new Response($e->getResponse(), false); + + switch ($status) { + case 400: + throw new BadRequestException($data['ocs']['meta']['message'] ?? 'Bad request'); + case 401: + case 403: + throw new NoPermissionException($data['ocs']['meta']['message'] ?? 'No permission'); + case 404: + throw new NotFoundException($data['ocs']['meta']['message'] ?? 'Not found'); + default: + return new Response($e->getResponse(), false); + } } catch (ServerException|\Throwable $e) { $serverException = new \Exception($e->getMessage(), $e->getCode(), $e); $this->logger->error('Could not reach remote', ['exception' => $serverException]); diff --git a/lib/Service/BoardService.php b/lib/Service/BoardService.php index 1610352ae3..80ec4f8265 100644 --- a/lib/Service/BoardService.php +++ b/lib/Service/BoardService.php @@ -520,7 +520,8 @@ public function leave(int $boardId): ?Acl { throw new BadRequestException('Board owner cannot leave board'); } - $acl = $this->aclMapper->findParticipantFromBoard($boardId, Acl::PERMISSION_TYPE_USER, $this->userId); + $userId = $this->userId ?? $this->permissionService->getUserId(); + $acl = $this->aclMapper->findParticipantFromBoard($boardId, Acl::PERMISSION_TYPE_USER, $userId); if (!$acl) { throw new BadRequestException('Not a participant of this board'); diff --git a/lib/Service/CommentService.php b/lib/Service/CommentService.php index 66487f64f5..1cf7fdfa41 100644 --- a/lib/Service/CommentService.php +++ b/lib/Service/CommentService.php @@ -130,7 +130,8 @@ public function create(int $cardId, string $message, int $replyTo = 0): DataResp public function update(int $cardId, int $commentId, string $message): DataResponse { $comment = $this->get($cardId, $commentId); - if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) { + $userId = $this->userId ?? $this->permissionService->getUserId(); + if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $userId) { throw new NoPermissionException('Only authors are allowed to edit their comment.'); } @@ -150,7 +151,8 @@ public function delete(int $cardId, int $commentId): DataResponse { } catch (CommentNotFoundException $e) { throw new NotFoundException('No comment found.'); } - if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) { + $userId = $this->userId ?? $this->permissionService->getUserId(); + if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $userId) { throw new NoPermissionException('Only authors are allowed to edit their comment.'); } $this->commentsManager->delete((string)$commentId); diff --git a/lib/Service/ExternalBoardService.php b/lib/Service/ExternalBoardService.php index ce8b5ae239..7f3b6f9338 100644 --- a/lib/Service/ExternalBoardService.php +++ b/lib/Service/ExternalBoardService.php @@ -43,6 +43,15 @@ public function getExternalBoardFromRemote(Board $localBoard):DataResponse { $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/board/' . $localBoard->getExternalId(); $resp = $this->proxy->get($participantCloudId->getId(), $shareToken, $url); $ocs = $this->proxy->getOCSData($resp); + + // Sync local board data with remote data + if ($ocs['title'] !== $localBoard->getTitle() || $ocs['color'] !== $localBoard->getColor() || $ocs['archived'] !== $localBoard->isArchived()) { + $localBoard->setTitle($ocs['title']); + $localBoard->setColor($ocs['color']); + $localBoard->setArchived($ocs['archived']); + $this->boardMapper->update($localBoard); + } + return new DataResponse($this->LocalizeRemoteBoard($ocs, $localBoard)); } public function getExternalStacksFromRemote(Board $localBoard):DataResponse { @@ -86,6 +95,7 @@ public function LocalizeRemoteStacks(array $stacks, Board $localBoard) { $assignment['participant'] = $this->localizeRemoteUser($localBoard, $assignment['participant']); return $assignment; }, $card['assignedUsers'] ?? []); + $stack['cards'][$j]['owner'] = $this->localizeRemoteUser($localBoard, $card['owner']); } $stacks[$i] = $stack; } @@ -482,7 +492,7 @@ public function getCardCommentsFromRemote(Board $localBoard, int $cardId, int $l return $this->localizeRemoteComments($localBoard, $comments); } - public function createCardCommentOnRemote(Board $localBoard, int $cardId, string $message, int $parentId = 0): DataResponse { + public function createCardCommentOnRemote(Board $localBoard, int $cardId, string $message, int $parentId = 0): array { $this->configService->ensureFederationEnabled(); $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_READ, $this->userId, false, false); $shareToken = $localBoard->getShareToken(); @@ -496,6 +506,62 @@ public function createCardCommentOnRemote(Board $localBoard, int $cardId, string ]; $resp = $this->proxy->post($participantCloudId->getId(), $shareToken, $url, $params); $newComment = $this->proxy->getOcsData($resp); - return new DataResponse($this->localizeRemoteComments($localBoard, [$newComment])[0]); + return $this->localizeRemoteComments($localBoard, [$newComment])[0]; + } + + public function updateCardCommentOnRemote(Board $localBoard, int $cardId, int $commentId, string $message): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_READ, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/comments/' . $commentId; + $params = [ + 'boardId' => $localBoard->getExternalId(), + 'message' => $message, + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + $updatedComment = $this->proxy->getOcsData($resp); + return $this->localizeRemoteComments($localBoard, [$updatedComment])[0]; + } + + public function deleteCardCommentOnRemote(Board $localBoard, int $cardId, int $commentId): array { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_READ, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/cards/' . $cardId . '/comments/' . $commentId; + $params = [ + 'boardId' => $localBoard->getExternalId(), + ]; + $resp = $this->proxy->delete($participantCloudId->getId(), $shareToken, $url, $params); + return $this->proxy->getOcsData($resp); + } + + public function updateBoardOnRemote(Board $localBoard, string $title, string $color, bool $archived): DataResponse { + $this->configService->ensureFederationEnabled(); + $this->permissionService->checkPermission($this->boardMapper, $localBoard->getId(), Acl::PERMISSION_MANAGE, $this->userId, false, false); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/boards/' . $localBoard->getExternalId(); + $params = [ + 'title' => $title, + 'color' => $color, + 'archived' => $archived, + ]; + $resp = $this->proxy->put($participantCloudId->getId(), $shareToken, $url, $params); + $updatedBoard = $this->proxy->getOcsData($resp); + return new DataResponse($this->LocalizeRemoteBoard($updatedBoard, $localBoard)); + } + + public function leaveBoardOnRemote(Board $localBoard): void { + $this->configService->ensureFederationEnabled(); + $shareToken = $localBoard->getShareToken(); + $participantCloudId = $this->cloudIdManager->getCloudId($this->userId, null); + $ownerCloudId = $this->cloudIdManager->resolveCloudId($localBoard->getOwner()); + $url = $ownerCloudId->getRemote() . '/ocs/v2.php/apps/deck/api/v1.0/boards/' . $localBoard->getExternalId() . '/leave'; + $this->proxy->post($participantCloudId->getId(), $shareToken, $url); } } diff --git a/src/components/board/SharingTabSidebar.vue b/src/components/board/SharingTabSidebar.vue index 59d7088256..31fa3e8825 100644 --- a/src/components/board/SharingTabSidebar.vue +++ b/src/components/board/SharingTabSidebar.vue @@ -12,21 +12,20 @@
  • - + - {{ board.owner.displayname }} + {{ board.owner.type===6 ? board.owner.uid : board.owner.displayname }} {{ t('deck', 'Board owner') }}
  • - +
    -
    - {{ acl.participant.displayname || acl.participant }} + {{ acl.type===6 ? acl.participant.uid : acl.participant.displayname || acl.participant }} {{ t('deck', '(Group)') }} {{ t('deck', '(Team)') }} {{ t('deck', '(remote)') }} @@ -272,6 +271,7 @@ export default { .username { padding: 12px 9px; flex-grow: 1; + min-width: 0; } .board-owner-label { diff --git a/src/components/card/AssignmentSelector.vue b/src/components/card/AssignmentSelector.vue index 9c53eddfe3..87669ee572 100644 --- a/src/components/card/AssignmentSelector.vue +++ b/src/components/card/AssignmentSelector.vue @@ -23,7 +23,7 @@
    @@ -68,7 +68,7 @@ export default defineComponent({ const assignable = { ...item, user: item.primaryKey, - displayName: item.displayname, + displayname: item.displayname, icon: 'icon-user', isNoUser: false, multiselectKey: item.type + ':' + item.uid, @@ -82,6 +82,11 @@ export default defineComponent({ assignable.icon = 'icon-circles' assignable.isNoUser = true } + if (item.type === 6) { + assignable.isNoUser = true + assignable.icon = null + assignable.displayname = item.primaryKey + } return assignable }) @@ -107,6 +112,7 @@ export default defineComponent({ isNoUser: item.participant.type !== 0, multiselectKey: item.participant.type + ':' + item.participant.primaryKey, user: item.participant.uid, + displayname: item.participant.type === 6 ? item.participant.uid : item.participant.displayname, })) } else { this.assignedUsers = [] diff --git a/src/components/card/CardSidebar.vue b/src/components/card/CardSidebar.vue index 346aa6cb97..0b26431894 100644 --- a/src/components/card/CardSidebar.vue +++ b/src/components/card/CardSidebar.vue @@ -20,7 +20,9 @@ {{ subtitle }}