From 2269f3d61fb226dde55ce4c32bd6e83123d054ae Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 7 Mar 2024 19:27:02 +0100 Subject: [PATCH] feat(Contexts): accept nodes in context update endpoint - includes a fix to also show pages that have no content Signed-off-by: Arthur Schiwon --- lib/Controller/ContextController.php | 11 +++- lib/Db/ContextMapper.php | 14 ++-- lib/Service/ContextService.php | 96 +++++++++++++++++++++++++++- 3 files changed, 110 insertions(+), 11 deletions(-) diff --git a/lib/Controller/ContextController.php b/lib/Controller/ContextController.php index 8032c77da..bc7a31000 100644 --- a/lib/Controller/ContextController.php +++ b/lib/Controller/ContextController.php @@ -105,9 +105,16 @@ public function create(string $name, string $iconName, string $description = '', * @NoAdminRequired * @CanManageContext */ - public function update(int $contextId, ?string $name, ?string $iconName, ?string $description): DataResponse { + public function update(int $contextId, ?string $name, ?string $iconName, ?string $description, ?array $nodes): DataResponse { try { - return new DataResponse($this->contextService->update($contextId, $name, $iconName, $description)->jsonSerialize()); + return new DataResponse($this->contextService->update( + $contextId, + $this->userId, + $name, + $iconName, + $description, + $nodes, + )->jsonSerialize()); } catch (Exception|MultipleObjectsReturnedException $e) { return $this->handleError($e); } catch (DoesNotExistException $e) { diff --git a/lib/Db/ContextMapper.php b/lib/Db/ContextMapper.php index fefe48968..5b33ec10e 100644 --- a/lib/Db/ContextMapper.php +++ b/lib/Db/ContextMapper.php @@ -29,8 +29,8 @@ protected function getFindContextBaseQuery(?string $userId): IQueryBuilder { $qb->select( 'c.*', 'r.id as node_rel_id', 'r.node_id', 'r.node_type', 'r.permissions', - 'p.page_type', - 'pc.id as content_id', 'pc.page_id', 'pc.order', + 'p.id as page_id', 'p.page_type', + 'pc.id as content_id', 'pc.order', 'n.display_mode as display_mode_default', 's.id as share_id', 's.receiver', 's.receiver_type' ) @@ -119,10 +119,12 @@ protected function formatResultRows(array $rows, ?string $userId) { } $carry[$item['page_id']]['id'] = $item['page_id']; $carry[$item['page_id']]['page_type'] = $item['page_type']; - $carry[$item['page_id']]['content'][$item['content_id']] = [ - 'order' => $item['order'], - 'node_rel_id' => $item['node_rel_id'] - ]; + if ($item['node_rel_id'] !== null) { + $carry[$item['page_id']]['content'][$item['content_id']] = [ + 'order' => $item['order'], + 'node_rel_id' => $item['node_rel_id'] + ]; + } return $carry; }, []); diff --git a/lib/Service/ContextService.php b/lib/Service/ContextService.php index 2d4f7b358..bd8483cb6 100644 --- a/lib/Service/ContextService.php +++ b/lib/Service/ContextService.php @@ -120,8 +120,8 @@ public function create(string $name, string $iconName, string $description, arra * @throws DoesNotExistException * @throws MultipleObjectsReturnedException */ - public function update(int $contextId, ?string $name, ?string $iconName, ?string $description): Context { - $context = $this->contextMapper->findById($contextId); + public function update(int $contextId, string $userId, ?string $name, ?string $iconName, ?string $description, ?array $nodes): Context { + $context = $this->contextMapper->findById($contextId, $userId); if ($name !== null) { $context->setName(trim($name)); @@ -133,7 +133,75 @@ public function update(int $contextId, ?string $name, ?string $iconName, ?string $context->setDescription(trim($description)); } - return $this->contextMapper->update($context); + $hasUpdatedNodeInformation = false; + if ($nodes !== null) { + $currentNodes = $context->getNodes(); + $currentPages = $context->getPages(); + + $nodesBeingRemoved = []; + $nodesBeingAdded = []; + $nodesBeingKept = []; + + // new node relationships do not have an ID. We can recognize them + // through their nodeType and nodeIds. For this we need to transform + // the known relationships` keys to a compatible format. + $oldNodeResolvableIdMapper = []; + foreach ($currentNodes as $i => $oldNode) { + $key = sprintf('t%di%d', $oldNode['node_type'], $oldNode['node_id']); + $oldNodeResolvableIdMapper[$key] = $i; + } + + foreach ($nodes as $node) { + $key = sprintf('t%di%d', $node['type'], $node['id']); + if (isset($oldNodeResolvableIdMapper[$key])) { + unset($oldNodeResolvableIdMapper[$key]); + $nodesBeingKept[$key] = $node; + continue; + } + $nodesBeingAdded[$key] = $node; + } + + foreach (array_diff_key($oldNodeResolvableIdMapper, $nodesBeingAdded, $nodesBeingKept) as $toRemoveId) { + $nodesBeingRemoved[$toRemoveId] = $currentNodes[$toRemoveId]; + } + unset($nodesBeingKept); + + $hasUpdatedNodeInformation = !empty($nodesBeingAdded) || !empty($nodesBeingRemoved); + + foreach ($nodesBeingRemoved as $node) { + /** @var ContextNodeRelation $removedNode */ + /** @var PageContent[] $removedContents */ + [$removedNode, $removedContents] = $this->removeNodeFromContextAndPages($node['id']); + foreach ($removedContents as $removedContent) { + unset($currentPages[$removedContent->getPageId()]['content'][$removedContent->getId()]); + } + unset($currentNodes[$removedNode->getId()]); + } + unset($nodesBeingRemoved); + + foreach ($nodesBeingAdded as $node) { + /** @var ContextNodeRelation $addedNode */ + /** @var PageContent $updatedContent */ + [$addedNode, $updatedContent] = $this->addNodeToContextAndStartpage( + $contextId, + $node['id'], + $node['type'], + $node['permissions'], + $node['order'], + $userId + ); + $currentNodes[$addedNode->getId()] = $addedNode->jsonSerialize(); + $currentPages[$updatedContent->getPageId()]['content'][$updatedContent->getId()] = $updatedContent->jsonSerialize(); + } + unset($nodesBeingAdded); + } + + $context = $this->contextMapper->update($context); + if ($hasUpdatedNodeInformation && isset($currentNodes) && isset($currentPages)) { + $context->setNodes($currentNodes); + $context->setPages($currentPages); + } + return $context; } /** @@ -198,6 +266,28 @@ public function removeNodeFromContextById(int $nodeRelationId): ContextNodeRelat return $this->contextNodeRelMapper->delete($nodeRelation); } + /** + * @throws MultipleObjectsReturnedException + * @throws DoesNotExistException + * @throws Exception + */ + public function addNodeToContextAndStartpage(int $contextId, int $nodeId, int $nodeType, int $permissions, int $order, string $userId): array { + $relation = $this->addNodeToContextById($contextId, $nodeId, $nodeType, $permissions, $userId); + $pageContent = $this->addNodeRelToPage($relation, $order); + return [$relation, $pageContent]; + } + + /** + * @throws DoesNotExistException + * @throws MultipleObjectsReturnedException + * @throws Exception + */ + public function removeNodeFromContextAndPages(int $nodeRelationId): array { + $nodeRelation = $this->removeNodeFromContextById($nodeRelationId); + $contents = $this->removeNodeRelFromAllPages($nodeRelation); + return [$nodeRelation, $contents]; + } + /** * @throws Exception */