From 016fe5557d4c2168197391407d57378f1a5cce7b Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Tue, 19 Sep 2023 12:57:35 +0200 Subject: [PATCH 1/5] BUGFIX: Remove Neos `WorkspaceName` Fixes: #4533 --- .../Management/WorkspacesController.php | 48 +++++------ .../Classes/Domain/Model/WorkspaceName.php | 81 ------------------- .../Domain/Service/WorkspaceNameBuilder.php | 37 +++++++++ .../Service/EditorContentStreamZookeeper.php | 80 ++++++++---------- Neos.Neos/Classes/Utility/User.php | 4 +- 5 files changed, 96 insertions(+), 154 deletions(-) delete mode 100644 Neos.Neos/Classes/Domain/Model/WorkspaceName.php create mode 100644 Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php diff --git a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php index bf782bfc86f..8ccd85b8032 100644 --- a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php +++ b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php @@ -15,31 +15,23 @@ namespace Neos\Neos\Controller\Module\Management; use Neos\ContentRepository\Core\ContentRepository; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; -use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; -use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindAncestorNodesFilter; -use Neos\ContentRepository\Core\Projection\ContentGraph\NodeTypeConstraints; -use Neos\ContentRepository\Core\SharedModel\Node\NodeName; -use Neos\Flow\I18n\Exception\IndexOutOfBoundsException; -use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException; -use Neos\Flow\Mvc\Exception\StopActionException; -use Neos\Neos\Domain\Model\SiteNodeName; -use Neos\Neos\PendingChangesProjection\ChangeFinder; -use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace; +use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\ChangeWorkspaceOwner; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\RenameWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishIndividualNodesFromWorkspace; use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\RenameWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\ChangeWorkspaceOwner; -use Neos\Neos\FrontendRouting\NodeAddress; -use Neos\Neos\FrontendRouting\NodeAddressFactory; -use Neos\ContentRepository\Core\SharedModel\User\UserId; +use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdsToPublishOrDiscard; +use Neos\ContentRepository\Core\Feature\WorkspacePublication\Dto\NodeIdToPublishOrDiscard; +use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindAncestorNodesFilter; +use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Node\NodeName; +use Neos\ContentRepository\Core\SharedModel\User\UserId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -47,21 +39,27 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Diff\Diff; use Neos\Diff\Renderer\Html\HtmlArrayRenderer; -use Neos\Neos\Controller\Module\ModuleTranslationTrait; -use Neos\Neos\Domain\Model\WorkspaceName as NeosWorkspaceName; -use Neos\Flow\Annotations as Flow; use Neos\Error\Messages\Message; +use Neos\Flow\I18n\Exception\IndexOutOfBoundsException; +use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException; use Neos\Flow\Mvc\ActionRequest; +use Neos\Flow\Mvc\Exception\StopActionException; use Neos\Flow\Package\PackageManager; use Neos\Flow\Property\PropertyMapper; use Neos\Flow\Security\Context; use Neos\Media\Domain\Model\AssetInterface; use Neos\Media\Domain\Model\ImageInterface; use Neos\Neos\Controller\Module\AbstractModuleController; +use Neos\Neos\Controller\Module\ModuleTranslationTrait; +use Neos\Neos\Domain\Model\SiteNodeName; use Neos\Neos\Domain\Model\User; use Neos\Neos\Domain\Repository\SiteRepository; use Neos\Neos\Domain\Service\UserService; +use Neos\Neos\Domain\Service\WorkspaceNameBuilder; +use Neos\Neos\FrontendRouting\NodeAddress; +use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; +use Neos\Neos\PendingChangesProjection\ChangeFinder; /** * The Neos Workspaces module controller @@ -121,8 +119,7 @@ public function indexAction() $currentAccount = $this->securityContext->getAccount(); $userWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName( - NeosWorkspaceName::fromAccountIdentifier($currentAccount->getAccountIdentifier()) - ->toContentRepositoryWorkspaceName() + WorkspaceNameBuilder::fromAccountIdentifier($currentAccount->getAccountIdentifier()) ); if (is_null($userWorkspace)) { throw new \RuntimeException('Current user has no workspace', 1645485990); @@ -462,8 +459,7 @@ public function rebaseAndRedirectAction(Node $targetNode, Workspace $targetWorks $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); $currentAccount = $this->securityContext->getAccount(); - $personalWorkspaceName = NeosWorkspaceName::fromAccountIdentifier($currentAccount->getAccountIdentifier()) - ->toContentRepositoryWorkspaceName(); + $personalWorkspaceName = WorkspaceNameBuilder::fromAccountIdentifier($currentAccount->getAccountIdentifier()); $personalWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName($personalWorkspaceName); /** @var Workspace $personalWorkspace */ diff --git a/Neos.Neos/Classes/Domain/Model/WorkspaceName.php b/Neos.Neos/Classes/Domain/Model/WorkspaceName.php deleted file mode 100644 index c59ba79951d..00000000000 --- a/Neos.Neos/Classes/Domain/Model/WorkspaceName.php +++ /dev/null @@ -1,81 +0,0 @@ -name = $name; - } - - public static function fromAccountIdentifier(string $accountIdentifier): self - { - $name = preg_replace('/[^A-Za-z0-9\-]/', '-', self::PREFIX . $accountIdentifier); - if (is_null($name)) { - throw new \InvalidArgumentException( - 'Cannot convert account identifier ' . $accountIdentifier . ' to workspace name.', - 1645656253 - ); - } - - return new self($name); - } - - /** - * @param array $takenWorkspaceNames - */ - public function increment(array $takenWorkspaceNames): self - { - $name = $this->name; - $i = 1; - while (array_key_exists($name, $takenWorkspaceNames)) { - $name = $this->name . self::SUFFIX_DELIMITER . $i; - $i++; - } - - if ($i > 1) { - return new WorkspaceName($name); - } else { - return $this; - } - } - - public function toContentRepositoryWorkspaceName(): ContentRepositoryWorkspaceName - { - return ContentRepositoryWorkspaceName::fromString($this->name); - } - - public function jsonSerialize(): string - { - return $this->name; - } - - public function __toString(): string - { - return $this->name; - } -} diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php b/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php new file mode 100644 index 00000000000..3fde53f531f --- /dev/null +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php @@ -0,0 +1,37 @@ +partyService->getAssignedPartyOfAccount($token->getAccount()); - if ($user instanceof User) { - $workspaceName = AdjustmentsWorkspaceName::fromAccountIdentifier( - $token->getAccount()->getAccountIdentifier() - ); - $workspace = $contentRepository->getWorkspaceFinder()->findOneByName( - $workspaceName->toContentRepositoryWorkspaceName() - ); - - if (!$workspace) { - // @todo: find base workspace for user - /** @var Workspace $baseWorkspace */ - $baseWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::forLive()); - $editorsNewContentStreamId = ContentStreamId::create(); - $similarlyNamedWorkspaces = $contentRepository->getWorkspaceFinder()->findByPrefix( - $workspaceName->toContentRepositoryWorkspaceName() - ); - if (!empty($similarlyNamedWorkspaces)) { - $workspaceName = $workspaceName->increment($similarlyNamedWorkspaces); - } - - $contentRepository->handle( - CreateWorkspace::create( - $workspaceName->toContentRepositoryWorkspaceName(), - $baseWorkspace->workspaceName, - new WorkspaceTitle((string) $user->getName()), - new WorkspaceDescription(''), - $editorsNewContentStreamId, - UserId::fromString($this->persistenceManager->getIdentifierByObject($user)) - ) - )->block(); - } else { - CatchUpTriggerWithSynchronousOption::synchronously(fn() => - $contentRepository->handle( - RebaseWorkspace::create( - $workspace->workspaceName, - ) - )->block()); - } - } + if (!$isEditor) { + return; + } + $user = $this->partyService->getAssignedPartyOfAccount($token->getAccount()); + if ($user === null) { + return; } + $workspaceName = WorkspaceNameBuilder::fromAccountIdentifier( + $token->getAccount()->getAccountIdentifier() + ); + $workspace = $contentRepository->getWorkspaceFinder()->findOneByName($workspaceName); + if ($workspace !== null) { + CatchUpTriggerWithSynchronousOption::synchronously(fn() => $contentRepository->handle( + RebaseWorkspace::create( + $workspace->workspaceName, + ) + )->block()); + return; + } + + // @todo: find base workspace for user + /** @var Workspace $baseWorkspace */ + $baseWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::forLive()); + $editorsNewContentStreamId = ContentStreamId::create(); + $contentRepository->handle( + CreateWorkspace::create( + $workspaceName, + $baseWorkspace->workspaceName, + new WorkspaceTitle((string) $user->getName()), + new WorkspaceDescription(''), + $editorsNewContentStreamId, + UserId::fromString($this->persistenceManager->getIdentifierByObject($user)) + ) + )->block(); } } diff --git a/Neos.Neos/Classes/Utility/User.php b/Neos.Neos/Classes/Utility/User.php index 051eedb8fb5..1d7f0840544 100644 --- a/Neos.Neos/Classes/Utility/User.php +++ b/Neos.Neos/Classes/Utility/User.php @@ -2,7 +2,7 @@ namespace Neos\Neos\Utility; -use Neos\Neos\Domain\Model\WorkspaceName; +use Neos\Neos\Domain\Service\WorkspaceNameBuilder; /** * Utility functions for dealing with users in the Content Repository. @@ -17,7 +17,7 @@ class User */ public static function getPersonalWorkspaceNameForUsername($username): string { - return (string)WorkspaceName::fromAccountIdentifier($username); + return WorkspaceNameBuilder::fromAccountIdentifier($username)->value; } /** From cb8a22d4b1e19c7e323116e3cd973cd7baf35d80 Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Tue, 19 Sep 2023 13:22:46 +0200 Subject: [PATCH 2/5] Cosmetic tweaks to satisfy linter & phpstan --- Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php | 2 -- Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php b/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php index 3fde53f531f..44d75e72086 100644 --- a/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php +++ b/Neos.Neos/Classes/Domain/Service/WorkspaceNameBuilder.php @@ -19,7 +19,6 @@ final class WorkspaceNameBuilder { - private const PREFIX = 'user-'; public static function fromAccountIdentifier(string $accountIdentifier): WorkspaceName @@ -33,5 +32,4 @@ public static function fromAccountIdentifier(string $accountIdentifier): Workspa } return WorkspaceName::fromString($name); } - } diff --git a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php index 98106f8a2ef..588217495c4 100644 --- a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php +++ b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php @@ -105,7 +105,7 @@ public function relayEditorAuthentication(Authentication\TokenInterface $token): return; } $user = $this->partyService->getAssignedPartyOfAccount($token->getAccount()); - if ($user === null) { + if (!$user instanceof User) { return; } $workspaceName = WorkspaceNameBuilder::fromAccountIdentifier( From c73d9e8a49c82f3d8c0a855060575f8d8c93f954 Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Tue, 19 Sep 2023 13:37:04 +0200 Subject: [PATCH 3/5] Re-add accidentally removed Flow annotation ns imports --- .../Controller/Module/Management/WorkspacesController.php | 1 + Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php | 1 + 2 files changed, 2 insertions(+) diff --git a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php index 8ccd85b8032..98a1c25cb1b 100644 --- a/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php +++ b/Neos.Neos/Classes/Controller/Module/Management/WorkspacesController.php @@ -40,6 +40,7 @@ use Neos\Diff\Diff; use Neos\Diff\Renderer\Html\HtmlArrayRenderer; use Neos\Error\Messages\Message; +use Neos\Flow\Annotations as Flow; use Neos\Flow\I18n\Exception\IndexOutOfBoundsException; use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException; use Neos\Flow\Mvc\ActionRequest; diff --git a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php index 588217495c4..b48da82d59c 100644 --- a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php +++ b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php @@ -24,6 +24,7 @@ use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\ContentRepositoryRegistry\Factory\ProjectionCatchUpTrigger\CatchUpTriggerWithSynchronousOption; +use Neos\Flow\Annotations as Flow; use Neos\Flow\Core\Bootstrap; use Neos\Flow\Http\HttpRequestHandlerInterface; use Neos\Flow\Persistence\PersistenceManagerInterface; From dcb1596bc6c4c7a694499bb466253c4144761cdd Mon Sep 17 00:00:00 2001 From: bwaidelich Date: Tue, 19 Sep 2023 17:07:14 +0200 Subject: [PATCH 4/5] Remove now unused `WorkspaceFinder::findByPrefix()` --- .../Projection/Workspace/WorkspaceFinder.php | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php index db8414b0875..a5f49004cd2 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php +++ b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php @@ -91,32 +91,6 @@ public function findOneByCurrentContentStreamId( return $workspace; } - /** - * @return array - */ - public function findByPrefix(WorkspaceName $prefix): array - { - $result = []; - - $connection = $this->client->getConnection(); - $workspaceRows = $connection->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - WHERE workspaceName LIKE :workspaceNameLike - ', - [ - 'workspaceNameLike' => $prefix->value . '%' - ] - )->fetchAllAssociative(); - - foreach ($workspaceRows as $workspaceRow) { - $similarlyNamedWorkspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $result[$similarlyNamedWorkspace->workspaceName->value] = $similarlyNamedWorkspace; - } - - return $result; - } - /** * @return array * @throws \Doctrine\DBAL\DBALException From fff96373c64b05243f15d12d9dcdbd6f9b82c27e Mon Sep 17 00:00:00 2001 From: Bastian Waidelich Date: Thu, 28 Sep 2023 09:17:41 +0200 Subject: [PATCH 5/5] Remove todo comment from EditorContentStreamZookeeper.php --- Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php | 1 - 1 file changed, 1 deletion(-) diff --git a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php index b48da82d59c..0dfcea7ee4d 100644 --- a/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php +++ b/Neos.Neos/Classes/Service/EditorContentStreamZookeeper.php @@ -122,7 +122,6 @@ public function relayEditorAuthentication(Authentication\TokenInterface $token): return; } - // @todo: find base workspace for user /** @var Workspace $baseWorkspace */ $baseWorkspace = $contentRepository->getWorkspaceFinder()->findOneByName(WorkspaceName::forLive()); $editorsNewContentStreamId = ContentStreamId::create();