diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/ContentGraphFactory.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/ContentGraphFactory.php index 4aee9e1b41c..0599dba402f 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/ContentGraphFactory.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/ContentGraphFactory.php @@ -22,12 +22,19 @@ use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreams; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceStatus; +use Neos\EventStore\Model\Event\Version; /** * @internal only used inside the - * @see ContentGraphFinder + * @see ContentGraphAdapter */ final readonly class ContentGraphFactory implements ContentGraphFactoryInterface { @@ -42,32 +49,168 @@ public function __construct( public function buildForWorkspace(WorkspaceName $workspaceName): ContentGraph { - $currentContentStreamIdStatement = <<findWorkspaceByName($workspaceName); + + if ($workspace === null) { + throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName); + } + return $this->buildForWorkspaceAndContentStream($workspace->workspaceName, $workspace->currentContentStreamId); + } + + public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraph + { + return new ContentGraph($this->dbal, $this->nodeFactory, $this->contentRepositoryId, $this->nodeTypeManager, $this->tableNames, $workspaceName, $contentStreamId); + } + + public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace + { + $workspaceByNameStatement = <<tableNames->workspace()} WHERE - workspaceName = :workspaceName + name = :workspaceName LIMIT 1 SQL; try { - $currentContentStreamId = $this->dbal->fetchOne($currentContentStreamIdStatement, [ + $row = $this->dbal->fetchAssociative($workspaceByNameStatement, [ 'workspaceName' => $workspaceName->value, ]); } catch (Exception $e) { - throw new \RuntimeException(sprintf('Failed to load workspace content stream id from database: %s', $e->getMessage()), 1716486077, $e); + throw new \RuntimeException(sprintf('Failed to load workspace from database: %s', $e->getMessage()), 1716486077, $e); + } + if ($row === false) { + return null; } + return self::workspaceFromDatabaseRow($row); + } - if ($currentContentStreamId === false) { - throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName); + public function getWorkspaces(): Workspaces + { + $workspacesStatement = <<tableNames->workspace()} + SQL; + try { + $rows = $this->dbal->fetchAllAssociative($workspacesStatement); + } catch (Exception $e) { + throw new \RuntimeException(sprintf('Failed to load workspaces from database: %s', $e->getMessage()), 1716902981, $e); + } + return Workspaces::fromArray(array_map(self::workspaceFromDatabaseRow(...), $rows)); + } + + public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream + { + $contentStreamByIdStatement = <<tableNames->contentStream()} + WHERE + id = :contentStreamId + LIMIT 1 + SQL; + try { + $row = $this->dbal->fetchAssociative($contentStreamByIdStatement, [ + 'contentStreamId' => $contentStreamId->value, + ]); + } catch (Exception $e) { + throw new \RuntimeException(sprintf('Failed to load content stream from database: %s', $e->getMessage()), 1716903166, $e); } + if ($row === false) { + return null; + } + return self::contentStreamFromDatabaseRow($row); + } - return $this->buildForWorkspaceAndContentStream($workspaceName, ContentStreamId::fromString($currentContentStreamId)); + public function getContentStreams(): ContentStreams + { + $contentStreamsStatement = <<tableNames->contentStream()} + SQL; + try { + $rows = $this->dbal->fetchAllAssociative($contentStreamsStatement); + } catch (Exception $e) { + throw new \RuntimeException(sprintf('Failed to load content streams from database: %s', $e->getMessage()), 1716903042, $e); + } + return ContentStreams::fromArray(array_map(self::contentStreamFromDatabaseRow(...), $rows)); } - public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraph + public function getUnusedAndRemovedContentStreamIds(): iterable { - return new ContentGraph($this->dbal, $this->nodeFactory, $this->contentRepositoryId, $this->nodeTypeManager, $this->tableNames, $workspaceName, $contentStreamId); + $removedContentStreamIdsStatement = <<tableNames->contentStream()} + WHERE + status = :inUseStatus + AND removed = false + UNION + -- now, when a content stream is in use by a workspace, its source content stream is + -- also "transitively" in use. + SELECT + sourceContentStreamId + FROM + {$this->tableNames->contentStream()} + JOIN transitiveUsedContentStreams ON {$this->tableNames->contentStream()}.id = transitiveUsedContentStreams.id + WHERE + {$this->tableNames->contentStream()}.sourceContentStreamId IS NOT NULL + ) + -- now, we check for removed content streams which we do not need anymore transitively + SELECT + id + FROM + {$this->tableNames->contentStream()} AS cs + WHERE + removed = true + AND NOT EXISTS ( + SELECT 1 + FROM transitiveUsedContentStreams + WHERE cs.id = transitiveUsedContentStreams.id + ) + SQL; + try { + $contentStreamIds = $this->dbal->fetchFirstColumn($removedContentStreamIdsStatement, [ + 'inUseStatus' => ContentStreamStatus::IN_USE_BY_WORKSPACE->value + ]); + } catch (Exception $e) { + throw new \RuntimeException(sprintf('Failed to load unused and removed content stream ids from database: %s', $e->getMessage()), 1716914615, $e); + } + return array_map(ContentStreamId::fromString(...), $contentStreamIds); + } + + /** + * @param array $row + */ + private static function workspaceFromDatabaseRow(array $row): Workspace + { + return new Workspace( + WorkspaceName::fromString($row['name']), + isset($row['baseWorkspaceName']) ? WorkspaceName::fromString($row['baseWorkspaceName']) : null, + ContentStreamId::fromString($row['currentContentStreamId']), + WorkspaceStatus::from($row['status']), + ); + } + + /** + * @param array $row + */ + private static function contentStreamFromDatabaseRow(array $row): ContentStream + { + return new ContentStream( + ContentStreamId::fromString($row['id']), + isset($row['sourceContentStreamId']) ? ContentStreamId::fromString($row['sourceContentStreamId']) : null, + ContentStreamStatus::from($row['status']), + Version::fromInteger((int)$row['version']), + ); } } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php index 0b6225571e5..0b096bf1dce 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjection.php @@ -17,7 +17,7 @@ use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\NodeRelationAnchorPoint; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePointsRepository; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ProjectionContentGraph; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePointSet; use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint; @@ -70,12 +70,12 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\EventStore\Model\Event\SequenceNumber; use Neos\EventStore\Model\EventEnvelope; /** - * @implements ProjectionInterface + * @implements ProjectionInterface * @internal but the graph projection is api */ final class DoctrineDbalContentGraphProjection implements ProjectionInterface @@ -97,7 +97,7 @@ public function __construct( private readonly ProjectionContentGraph $projectionContentGraph, private readonly ContentGraphTableNames $tableNames, private readonly DimensionSpacePointsRepository $dimensionSpacePointsRepository, - private readonly ContentGraphFinder $contentGraphFinder + private readonly ContentGraphAdapter $contentGraphAdapter ) { $this->checkpointStorage = new DbalCheckpointStorage( $this->dbal, @@ -116,14 +116,14 @@ public function setUp(): void $this->dbal->getSchemaManager()->tablesExist([$legacyWorkspaceTableName]) && !$this->dbal->getSchemaManager()->tablesExist([$this->tableNames->workspace()]) ) { - $statements[] = 'INSERT INTO ' . $this->tableNames->workspace() . ' (workspacename, baseworkspacename, currentcontentstreamid, status) SELECT workspacename, baseworkspacename, currentcontentstreamid, status FROM ' . $legacyWorkspaceTableName; + $statements[] = 'INSERT INTO ' . $this->tableNames->workspace() . ' (name, baseWorkspaceName, currentContentStreamId, status) SELECT workspaceName AS name, baseWorkspaceName, currentContentStreamId, status FROM ' . $legacyWorkspaceTableName; } $legacyContentStreamTableName = str_replace('_p_graph_contentstream', '_p_contentstream', $this->tableNames->contentStream()); if ( $this->dbal->getSchemaManager()->tablesExist([$legacyContentStreamTableName]) && !$this->dbal->getSchemaManager()->tablesExist([$this->tableNames->contentStream()]) ) { - $statements[] = 'INSERT INTO ' . $this->tableNames->contentStream() . ' (contentStreamId, version, sourceContentStreamId, state, removed) SELECT contentStreamId, version, sourceContentStreamId, state, removed FROM ' . $legacyContentStreamTableName; + $statements[] = 'INSERT INTO ' . $this->tableNames->contentStream() . ' (id, version, sourceContentStreamId, status, removed) SELECT contentStreamId AS id, version, sourceContentStreamId, state AS status, removed FROM ' . $legacyContentStreamTableName; } // /MIGRATION @@ -175,9 +175,9 @@ public function getCheckpointStorage(): DbalCheckpointStorage return $this->checkpointStorage; } - public function getState(): ContentGraphFinder + public function getState(): ContentGraphAdapter { - return $this->contentGraphFinder; + return $this->contentGraphAdapter; } public function canHandle(EventInterface $event): bool @@ -215,6 +215,7 @@ public function canHandle(EventInterface $event): bool WorkspaceWasRebased::class, WorkspaceWasRemoved::class, ]); + // todo handle all EmbedsContentStreamId?? } public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void @@ -260,12 +261,12 @@ public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void private function whenContentStreamWasClosed(ContentStreamWasClosed $event): void { - $this->updateContentStreamState($event->contentStreamId, ContentStreamState::STATE_CLOSED); + $this->updateContentStreamStatus($event->contentStreamId, ContentStreamStatus::CLOSED); } private function whenContentStreamWasCreated(ContentStreamWasCreated $event): void { - $this->createContentStream($event->contentStreamId, ContentStreamState::STATE_CREATED); + $this->createContentStream($event->contentStreamId, ContentStreamStatus::CREATED); } private function whenContentStreamWasForked(ContentStreamWasForked $event): void @@ -304,7 +305,7 @@ private function whenContentStreamWasForked(ContentStreamWasForked $event): void // NOTE: as reference edges are attached to Relation Anchor Points (and they are lazily copy-on-written), // we do not need to copy reference edges here (but we need to do it during copy on write). - $this->createContentStream($event->newContentStreamId, ContentStreamState::STATE_FORKED); + $this->createContentStream($event->newContentStreamId, ContentStreamStatus::FORKED, $event->sourceContentStreamId); } private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event): void @@ -354,7 +355,7 @@ private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event): vo private function whenContentStreamWasReopened(ContentStreamWasReopened $event): void { - $this->updateContentStreamState($event->contentStreamId, $event->previousState); + $this->updateContentStreamStatus($event->contentStreamId, $event->previousState); } private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded $event): void @@ -711,7 +712,7 @@ private function whenRootWorkspaceWasCreated(RootWorkspaceWasCreated $event): vo $this->createWorkspace($event->workspaceName, null, $event->newContentStreamId); // the content stream is in use now - $this->updateContentStreamState($event->newContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); } private function whenSubtreeWasTagged(SubtreeWasTagged $event): void @@ -732,7 +733,7 @@ private function whenWorkspaceBaseWorkspaceWasChanged(WorkspaceBaseWorkspaceWasC private function whenWorkspaceRebaseFailed(WorkspaceRebaseFailed $event): void { $this->markWorkspaceAsOutdatedConflict($event->workspaceName); - $this->updateContentStreamState($event->candidateContentStreamId, ContentStreamState::STATE_REBASE_ERROR); + $this->updateContentStreamStatus($event->candidateContentStreamId, ContentStreamStatus::REBASE_ERROR); } private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void @@ -740,7 +741,7 @@ private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void $this->createWorkspace($event->workspaceName, $event->baseWorkspaceName, $event->newContentStreamId); // the content stream is in use now - $this->updateContentStreamState($event->newContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); } private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void @@ -750,9 +751,9 @@ private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void $this->markDependentWorkspacesAsOutdated($event->workspaceName); // the new content stream is in use now - $this->updateContentStreamState($event->newContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); // the previous content stream is no longer in use - $this->updateContentStreamState($event->previousContentStreamId, ContentStreamState::STATE_NO_LONGER_IN_USE); + $this->updateContentStreamStatus($event->previousContentStreamId, ContentStreamStatus::NO_LONGER_IN_USE); } private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscarded $event): void @@ -761,10 +762,10 @@ private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscard $this->markDependentWorkspacesAsOutdated($event->workspaceName); // the new content stream is in use now - $this->updateContentStreamState($event->newContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); // the previous content stream is no longer in use - $this->updateContentStreamState($event->previousContentStreamId, ContentStreamState::STATE_NO_LONGER_IN_USE); + $this->updateContentStreamStatus($event->previousContentStreamId, ContentStreamStatus::NO_LONGER_IN_USE); } private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void @@ -779,10 +780,10 @@ private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublish $this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName); // the new content stream is in use now - $this->updateContentStreamState($event->newSourceContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newSourceContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); // the previous content stream is no longer in use - $this->updateContentStreamState($event->previousSourceContentStreamId, ContentStreamState::STATE_NO_LONGER_IN_USE); + $this->updateContentStreamStatus($event->previousSourceContentStreamId, ContentStreamStatus::NO_LONGER_IN_USE); } private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void @@ -797,10 +798,10 @@ private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void $this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName); // the new content stream is in use now - $this->updateContentStreamState($event->newSourceContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newSourceContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); // the previous content stream is no longer in use - $this->updateContentStreamState($event->previousSourceContentStreamId, ContentStreamState::STATE_NO_LONGER_IN_USE); + $this->updateContentStreamStatus($event->previousSourceContentStreamId, ContentStreamStatus::NO_LONGER_IN_USE); } private function whenWorkspaceWasRebased(WorkspaceWasRebased $event): void @@ -812,10 +813,10 @@ private function whenWorkspaceWasRebased(WorkspaceWasRebased $event): void $this->markWorkspaceAsUpToDate($event->workspaceName); // the new content stream is in use now - $this->updateContentStreamState($event->newContentStreamId, ContentStreamState::STATE_IN_USE_BY_WORKSPACE); + $this->updateContentStreamStatus($event->newContentStreamId, ContentStreamStatus::IN_USE_BY_WORKSPACE); // the previous content stream is no longer in use - $this->updateContentStreamState($event->previousContentStreamId, ContentStreamState::STATE_NO_LONGER_IN_USE); + $this->updateContentStreamStatus($event->previousContentStreamId, ContentStreamStatus::NO_LONGER_IN_USE); } private function whenWorkspaceWasRemoved(WorkspaceWasRemoved $event): void @@ -843,6 +844,7 @@ private function truncateDatabaseTables(): void $this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->referenceRelation()); $this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->dimensionSpacePoints()); $this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->workspace()); + $this->dbal->executeQuery('TRUNCATE table ' . $this->tableNames->contentStream()); } catch (DBALException $e) { throw new \RuntimeException(sprintf('Failed to truncate database tables for projection %s: %s', self::class, $e->getMessage()), 1716478318, $e); } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjectionFactory.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjectionFactory.php index f7788df7089..4e063b7e412 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjectionFactory.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphProjectionFactory.php @@ -8,7 +8,7 @@ use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\DimensionSpacePointsRepository; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\NodeFactory; use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ProjectionContentGraph; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\Factory\ProjectionFactoryDependencies; use Neos\ContentRepository\Core\Projection\ProjectionFactoryInterface; @@ -58,7 +58,7 @@ public function build( ), $tableNames, $dimensionSpacePointsRepository, - new ContentGraphFinder($contentGraphFactory) + new ContentGraphAdapter($contentGraphFactory) ); } } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php index 98c11ddc39a..e10b4f16c62 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/DoctrineDbalContentGraphSchemaBuilder.php @@ -112,23 +112,23 @@ private function createReferenceRelationTable(): Table private function createWorkspaceTable(): Table { $workspaceTable = self::createTable($this->tableNames->workspace(), [ - DbalSchemaFactory::columnForWorkspaceName('workspacename')->setNotnull(true), - DbalSchemaFactory::columnForWorkspaceName('baseworkspacename')->setNotnull(false), - DbalSchemaFactory::columnForContentStreamId('currentcontentstreamid')->setNotNull(true), + DbalSchemaFactory::columnForWorkspaceName('name')->setNotnull(true), + DbalSchemaFactory::columnForWorkspaceName('baseWorkspaceName')->setNotnull(false), + DbalSchemaFactory::columnForContentStreamId('currentContentStreamId')->setNotNull(true), (new Column('status', self::type(Types::BINARY)))->setLength(20)->setNotnull(false), ]); - return $workspaceTable->setPrimaryKey(['workspacename']); + return $workspaceTable->setPrimaryKey(['name']); } private function createContentStreamTable(): Table { return self::createTable($this->tableNames->contentStream(), [ - DbalSchemaFactory::columnForContentStreamId('contentStreamId')->setNotnull(true), + DbalSchemaFactory::columnForContentStreamId('id')->setNotnull(true), (new Column('version', Type::getType(Types::INTEGER)))->setNotnull(true), DbalSchemaFactory::columnForContentStreamId('sourceContentStreamId')->setNotnull(false), // Should become a DB ENUM (unclear how to configure with DBAL) or int (latter needs adaption to code) - (new Column('state', Type::getType(Types::BINARY)))->setLength(20)->setNotnull(true), + (new Column('status', Type::getType(Types::BINARY)))->setLength(20)->setNotnull(true), (new Column('removed', Type::getType(Types::BOOLEAN)))->setDefault(false)->setNotnull(false) ]); } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/ContentStream.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/ContentStream.php index bc39979bf46..dfe1f7f46d5 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/ContentStream.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/ContentStream.php @@ -5,7 +5,7 @@ namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\EventStore\Model\Event\Version; /** @@ -15,22 +15,22 @@ */ trait ContentStream { - private function createContentStream(ContentStreamId $contentStreamId, ContentStreamState $state, ?ContentStreamId $sourceContentStreamId = null): void + private function createContentStream(ContentStreamId $contentStreamId, ContentStreamStatus $status, ?ContentStreamId $sourceContentStreamId = null): void { $this->dbal->insert($this->tableNames->contentStream(), [ - 'contentStreamId' => $contentStreamId->value, + 'id' => $contentStreamId->value, 'sourceContentStreamId' => $sourceContentStreamId?->value, 'version' => 0, - 'state' => $state->value, + 'status' => $status->value, ]); } - private function updateContentStreamState(ContentStreamId $contentStreamId, ContentStreamState $state): void + private function updateContentStreamStatus(ContentStreamId $contentStreamId, ContentStreamStatus $status): void { $this->dbal->update($this->tableNames->contentStream(), [ - 'state' => $state->value, + 'status' => $status->value, ], [ - 'contentStreamId' => $contentStreamId->value + 'id' => $contentStreamId->value ]); } @@ -39,7 +39,7 @@ private function removeContentStream(ContentStreamId $contentStreamId): void $this->dbal->update($this->tableNames->contentStream(), [ 'removed' => true, ], [ - 'contentStreamId' => $contentStreamId->value + 'id' => $contentStreamId->value ]); } @@ -48,7 +48,7 @@ private function updateContentStreamVersion(ContentStreamId $contentStreamId, Ve $this->dbal->update($this->tableNames->contentStream(), [ 'version' => $version->value, ], [ - 'contentStreamId' => $contentStreamId->value, + 'id' => $contentStreamId->value, ]); } } diff --git a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Workspace.php b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Workspace.php index bfe81c8dc5d..e4c28fd6cec 100644 --- a/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Workspace.php +++ b/Neos.ContentGraph.DoctrineDbalAdapter/src/Domain/Projection/Feature/Workspace.php @@ -4,7 +4,7 @@ namespace Neos\ContentGraph\DoctrineDbalAdapter\Domain\Projection\Feature; -use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceStatus; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceStatus; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; @@ -18,7 +18,7 @@ trait Workspace private function createWorkspace(WorkspaceName $workspaceName, ?WorkspaceName $baseWorkspaceName, ContentStreamId $contentStreamId): void { $this->dbal->insert($this->tableNames->workspace(), [ - 'workspaceName' => $workspaceName->value, + 'name' => $workspaceName->value, 'baseWorkspaceName' => $baseWorkspaceName?->value, 'currentContentStreamId' => $contentStreamId->value, 'status' => WorkspaceStatus::UP_TO_DATE->value @@ -29,7 +29,7 @@ private function removeWorkspace(WorkspaceName $workspaceName): void { $this->dbal->delete( $this->tableNames->workspace(), - ['workspaceName' => $workspaceName->value] + ['name' => $workspaceName->value] ); } @@ -52,7 +52,7 @@ private function updateWorkspaceContentStreamId( $this->dbal->update($this->tableNames->workspace(), [ 'currentContentStreamId' => $contentStreamId->value, ], [ - 'workspaceName' => $workspaceName->value + 'name' => $workspaceName->value ]); } @@ -62,7 +62,7 @@ private function markWorkspaceAsUpToDate(WorkspaceName $workspaceName): void UPDATE ' . $this->tableNames->workspace() . ' SET status = :upToDate WHERE - workspacename = :workspaceName + name = :workspaceName ', [ 'upToDate' => WorkspaceStatus::UP_TO_DATE->value, 'workspaceName' => $workspaceName->value @@ -89,7 +89,7 @@ private function markWorkspaceAsOutdated(WorkspaceName $workspaceName): void SET status = :outdated WHERE - workspacename = :workspaceName + name = :workspaceName ', [ 'outdated' => WorkspaceStatus::OUTDATED->value, 'workspaceName' => $workspaceName->value @@ -103,7 +103,7 @@ private function markWorkspaceAsOutdatedConflict(WorkspaceName $workspaceName): SET status = :outdatedConflict WHERE - workspacename = :workspaceName + name = :workspaceName ', [ 'outdatedConflict' => WorkspaceStatus::OUTDATED_CONFLICT->value, 'workspaceName' => $workspaceName->value diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/ContentHyperGraphFactory.php b/Neos.ContentGraph.PostgreSQLAdapter/src/ContentHyperGraphFactory.php index 974c84d17ca..f9d28d0dd68 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/ContentHyperGraphFactory.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/ContentHyperGraphFactory.php @@ -8,17 +8,21 @@ use Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\ContentHypergraph; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\NodeFactory; use Neos\ContentRepository\Core\ContentGraphFactoryInterface; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\NodeType\NodeTypeManager; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreams; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; /** * @internal only used within - * @see ContentGraphFinder + * @see ContentGraphAdapter */ final readonly class ContentHyperGraphFactory implements ContentGraphFactoryInterface { @@ -61,4 +65,34 @@ public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, { return new ContentHyperGraph($this->dbal, $this->nodeFactory, $this->contentRepositoryId, $this->nodeTypeManager, $this->tableNamePrefix, $workspaceName, $contentStreamId); } + + public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace + { + // TODO: Implement findWorkspaceByName() method. + return null; + } + + public function getWorkspaces(): Workspaces + { + // TODO: Implement getWorkspaces() method. + return Workspaces::createEmpty(); + } + + public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream + { + // TODO: Implement findContentStreamById() method. + return null; + } + + public function getContentStreams(): ContentStreams + { + // TODO: Implement getContentStreams() method. + return ContentStreams::createEmpty(); + } + + public function getUnusedAndRemovedContentStreamIds(): iterable + { + // TODO: Implement getUnusedAndRemovedContentStreamIds() method. + return []; + } } diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php index 6b683bbd08d..afc1b9c0249 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/Domain/Projection/HypergraphProjection.php @@ -26,7 +26,7 @@ use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\Feature\NodeVariation; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\Feature\SubtreeTagging; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\SchemaBuilder\HypergraphSchemaBuilder; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\ContentStreamForking\Event\ContentStreamWasForked; use Neos\ContentRepository\Core\Feature\NodeCreation\Event\NodeAggregateWithNodeWasCreated; @@ -52,7 +52,7 @@ /** * The alternate reality-aware hypergraph projector for the PostgreSQL backend via Doctrine DBAL * - * @implements ProjectionInterface + * @implements ProjectionInterface * @internal the parent Content Graph is public */ final class HypergraphProjection implements ProjectionInterface @@ -73,7 +73,7 @@ final class HypergraphProjection implements ProjectionInterface public function __construct( private readonly Connection $dbal, private readonly string $tableNamePrefix, - private readonly ContentGraphFinder $contentGraphFinder + private readonly ContentGraphAdapter $contentGraphAdapter ) { $this->projectionHypergraph = new ProjectionHypergraph($this->dbal, $this->tableNamePrefix); $this->checkpointStorage = new DbalCheckpointStorage( @@ -219,9 +219,9 @@ public function getCheckpointStorage(): DbalCheckpointStorage return $this->checkpointStorage; } - public function getState(): ContentGraphFinder + public function getState(): ContentGraphAdapter { - return $this->contentGraphFinder; + return $this->contentGraphAdapter; } protected function getProjectionHypergraph(): ProjectionHypergraph diff --git a/Neos.ContentGraph.PostgreSQLAdapter/src/HypergraphProjectionFactory.php b/Neos.ContentGraph.PostgreSQLAdapter/src/HypergraphProjectionFactory.php index edbb84fbb8e..83aab2d3553 100644 --- a/Neos.ContentGraph.PostgreSQLAdapter/src/HypergraphProjectionFactory.php +++ b/Neos.ContentGraph.PostgreSQLAdapter/src/HypergraphProjectionFactory.php @@ -7,7 +7,7 @@ use Doctrine\DBAL\Connection; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Projection\HypergraphProjection; use Neos\ContentGraph\PostgreSQLAdapter\Domain\Repository\NodeFactory; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\Factory\ProjectionFactoryDependencies; use Neos\ContentRepository\Core\Projection\ProjectionFactoryInterface; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; @@ -45,7 +45,7 @@ public function build( return new HypergraphProjection( $this->dbal, $tableNamePrefix, - new ContentGraphFinder(new ContentHyperGraphFactory($this->dbal, $nodeFactory, $projectionFactoryDependencies->contentRepositoryId, $projectionFactoryDependencies->nodeTypeManager, $tableNamePrefix)) + new ContentGraphAdapter(new ContentHyperGraphFactory($this->dbal, $nodeFactory, $projectionFactoryDependencies->contentRepositoryId, $projectionFactoryDependencies->nodeTypeManager, $tableNamePrefix)) ); } } diff --git a/Neos.ContentRepository.BehavioralTests/Classes/Command/PerformanceMeasurementService.php b/Neos.ContentRepository.BehavioralTests/Classes/Command/PerformanceMeasurementService.php index 21c344d2f9d..ff37b268bce 100644 --- a/Neos.ContentRepository.BehavioralTests/Classes/Command/PerformanceMeasurementService.php +++ b/Neos.ContentRepository.BehavioralTests/Classes/Command/PerformanceMeasurementService.php @@ -36,9 +36,7 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateClassification; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepositoryRegistry\Factory\EventStore\DoctrineEventStoreFactory; use Neos\EventStore\Model\EventStream\ExpectedVersion; @@ -81,8 +79,6 @@ public function createNodesForPerformanceTest(int $nodesPerLevel, int $levels): { $this->contentRepository->handle(CreateRootWorkspace::create( WorkspaceName::forLive(), - WorkspaceTitle::fromString('live'), - WorkspaceDescription::fromString(''), $this->contentStreamId )); diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodesFromAggregate.wip b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodesFromAggregate.wip index 3fe26bf8051..abf1480df35 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodesFromAggregate.wip +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/NodeRemoval/RemoveNodesFromAggregate.wip @@ -26,8 +26,6 @@ Feature: Remove Nodes from Aggregate And the command CreateRootWorkspace is executed with payload: | Key | Value | | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | | newContentStreamId | "live-cs-identifier" | And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/PruneContentStreams.feature b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/PruneContentStreams.feature index 4cedde28f3c..b275725c374 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/PruneContentStreams.feature +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/PruneContentStreams.feature @@ -20,8 +20,8 @@ Feature: If content streams are not in use anymore by the workspace, they can be | nodeTypeName | "Neos.ContentRepository:Root" | Scenario: content streams are marked as IN_USE_BY_WORKSPACE properly after creation - Then the content stream "cs-identifier" has state "IN_USE_BY_WORKSPACE" - Then the content stream "non-existing" has state "" + Then the content stream "cs-identifier" has status "IN_USE_BY_WORKSPACE" + Then the content stream "non-existing" does not exist Scenario: on creating a nested workspace, the new content stream is marked as IN_USE_BY_WORKSPACE. When the command CreateWorkspace is executed with payload: @@ -30,7 +30,7 @@ Feature: If content streams are not in use anymore by the workspace, they can be | baseWorkspaceName | "live" | | newContentStreamId | "user-cs-identifier" | - Then the content stream "user-cs-identifier" has state "IN_USE_BY_WORKSPACE" + Then the content stream "user-cs-identifier" has status "IN_USE_BY_WORKSPACE" Scenario: when rebasing a nested workspace, the new content stream will be marked as IN_USE_BY_WORKSPACE; and the old content stream is NO_LONGER_IN_USE. When the command CreateWorkspace is executed with payload: @@ -43,8 +43,8 @@ Feature: If content streams are not in use anymore by the workspace, they can be | workspaceName | "user-test" | When I am in workspace "user-test" and dimension space point {} - Then the current content stream has state "IN_USE_BY_WORKSPACE" - And the content stream "user-cs-identifier" has state "NO_LONGER_IN_USE" + Then the current content stream has status "IN_USE_BY_WORKSPACE" + And the content stream "user-cs-identifier" has status "NO_LONGER_IN_USE" Scenario: when pruning content streams, NO_LONGER_IN_USE content streams will be properly cleaned from the graph projection. diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodeAggregateWithDimensions.wip b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodeAggregateWithDimensions.wip index 30f13ab7578..397a2c0262c 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodeAggregateWithDimensions.wip +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodeAggregateWithDimensions.wip @@ -15,8 +15,6 @@ Feature: Remove NodeAggregate And the command CreateRootWorkspace is executed with payload: | Key | Value | | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | | newContentStreamId | "live-cs-identifier" | And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodesFromAggregate.wip b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodesFromAggregate.wip index 3fe26bf8051..abf1480df35 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodesFromAggregate.wip +++ b/Neos.ContentRepository.BehavioralTests/Tests/Behavior/Features/Workspaces/RemoveNodesFromAggregate.wip @@ -26,8 +26,6 @@ Feature: Remove Nodes from Aggregate And the command CreateRootWorkspace is executed with payload: | Key | Value | | workspaceName | "live" | - | workspaceTitle | "Live" | - | workspaceDescription | "The live workspace" | | newContentStreamId | "live-cs-identifier" | And the command CreateRootNodeAggregateWithNode is executed with payload: | Key | Value | diff --git a/Neos.ContentRepository.BehavioralTests/Tests/Functional/Feature/WorkspacePublication/WorkspaceWritingDuringPublication.php b/Neos.ContentRepository.BehavioralTests/Tests/Functional/Feature/WorkspacePublication/WorkspaceWritingDuringPublication.php index c77ce33d13a..366084abb7b 100644 --- a/Neos.ContentRepository.BehavioralTests/Tests/Functional/Feature/WorkspacePublication/WorkspaceWritingDuringPublication.php +++ b/Neos.ContentRepository.BehavioralTests/Tests/Functional/Feature/WorkspacePublication/WorkspaceWritingDuringPublication.php @@ -37,9 +37,7 @@ use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamIsClosed; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap\CRTestSuiteTrait; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\EventStore\Exception\ConcurrencyException; @@ -106,8 +104,6 @@ public function getContentDimensionsOrderedByPriority(): array $origin = OriginDimensionSpacePoint::createWithoutDimensions(); $contentRepository->handle(CreateRootWorkspace::create( WorkspaceName::forLive(), - new WorkspaceTitle('Live'), - new WorkspaceDescription('The live workspace'), ContentStreamId::fromString('live-cs-id') )); $contentRepository->handle(CreateRootNodeAggregateWithNode::create( @@ -128,8 +124,6 @@ public function getContentDimensionsOrderedByPriority(): array $contentRepository->handle(CreateWorkspace::create( WorkspaceName::fromString('user-test'), WorkspaceName::forLive(), - new WorkspaceTitle('User'), - new WorkspaceDescription('The user workspace'), ContentStreamId::fromString('user-cs-id') )); for ($i = 0; $i <= 1000; $i++) { diff --git a/Neos.ContentRepository.Core/Classes/CommandHandlingDependencies.php b/Neos.ContentRepository.Core/Classes/CommandHandlingDependencies.php index 3f3515e81f3..896fbfb938f 100644 --- a/Neos.ContentRepository.Core/Classes/CommandHandlingDependencies.php +++ b/Neos.ContentRepository.Core/Classes/CommandHandlingDependencies.php @@ -17,10 +17,10 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\CommandHandler\CommandResult; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\EventStore\Model\Event\Version; @@ -36,7 +36,7 @@ final class CommandHandlingDependencies * WorkspaceName->value to ContentGraphInterface * @var array */ - private array $overridenContentGraphInstances = []; + private array $overriddenContentGraphInstances = []; public function __construct(private readonly ContentRepository $contentRepository) { @@ -49,26 +49,30 @@ public function handle(CommandInterface $command): CommandResult public function getContentStreamVersion(ContentStreamId $contentStreamId): Version { - // TODO implement - return Version::fromInteger(1); + $contentStream = $this->contentRepository->findContentStreamById($contentStreamId); + if ($contentStream === null) { + throw new \InvalidArgumentException(sprintf('Failed to find content stream with id "%s"', $contentStreamId->value), 1716902051); + } + return $contentStream->version; } public function contentStreamExists(ContentStreamId $contentStreamId): bool { - // TODO implement - return false; + return $this->contentRepository->findContentStreamById($contentStreamId) !== null; } - public function getContentStreamState(ContentStreamId $contentStreamId): ContentStreamState + public function getContentStreamStatus(ContentStreamId $contentStreamId): ContentStreamStatus { - // TODO implement - return ContentStreamState::STATE_FORKED; + $contentStream = $this->contentRepository->findContentStreamById($contentStreamId); + if ($contentStream === null) { + throw new \InvalidArgumentException(sprintf('Failed to find content stream with id "%s"', $contentStreamId->value), 1716902219); + } + return $contentStream->status; } public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace { - // TODO implement - return null; + return $this->contentRepository->findWorkspaceByName($workspaceName); } /** @@ -76,8 +80,8 @@ public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace */ public function getContentGraph(WorkspaceName $workspaceName): ContentGraphInterface { - if (isset($this->overridenContentGraphInstances[$workspaceName->value])) { - return $this->overridenContentGraphInstances[$workspaceName->value]; + if (isset($this->overriddenContentGraphInstances[$workspaceName->value])) { + return $this->overriddenContentGraphInstances[$workspaceName->value]; } return $this->contentRepository->getContentGraph($workspaceName); @@ -94,17 +98,17 @@ public function getContentGraph(WorkspaceName $workspaceName): ContentGraphInter */ public function overrideContentStreamId(WorkspaceName $workspaceName, ContentStreamId $contentStreamId, \Closure $fn): void { - if (isset($this->overridenContentGraphInstances[$workspaceName->value])) { + if (isset($this->overriddenContentGraphInstances[$workspaceName->value])) { throw new \RuntimeException('Contentstream override for this workspace already in effect, nesting not allowed.', 1715170938); } - $contentGraph = $this->contentRepository->projectionState(ContentGraphFinder::class)->getByWorkspaceNameAndContentStreamId($workspaceName, $contentStreamId); - $this->overridenContentGraphInstances[$workspaceName->value] = $contentGraph; + $contentGraph = $this->contentRepository->projectionState(ContentGraphAdapter::class)->getContentGraphByWorkspaceNameAndContentStreamId($workspaceName, $contentStreamId); + $this->overriddenContentGraphInstances[$workspaceName->value] = $contentGraph; try { $fn(); } finally { - unset($this->overridenContentGraphInstances[$workspaceName->value]); + unset($this->overriddenContentGraphInstances[$workspaceName->value]); } } } diff --git a/Neos.ContentRepository.Core/Classes/ContentGraphAdapter.php b/Neos.ContentRepository.Core/Classes/ContentGraphAdapter.php new file mode 100644 index 00000000000..4e9f8cc27d1 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/ContentGraphAdapter.php @@ -0,0 +1,139 @@ + Runtime cache for {@see ContentGraphInterface} instances, indexed by their workspace name + */ + private array $contentGraphInstancesByWorkspaceName = []; + + /** + * @var array Runtime cache for {@see Workspace} instances, indexed by their name + */ + private array $workspaceInstancesByName = []; + + /** + * @var array Runtime cache for {@see ContentStream} instances, indexed by their name + */ + private array $contentStreamInstancesById = []; + + public function __construct( + private readonly ContentGraphFactoryInterface $contentGraphFactory + ) { + } + + /** + * To release all held instances, in case a workspace/content stream relation needs to be reset + * + * @internal Should only be needed after write operations (which should take care on their own) + */ + public function forgetInstances(): void + { + // todo do we need to reintroduce the cache??? https://github.com/neos/neos-development-collection/pull/5246 + $this->contentGraphInstancesByWorkspaceName = []; + $this->workspaceInstancesByName = []; + $this->contentStreamInstancesById = []; + } + + public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace + { + if (!array_key_exists($workspaceName->value, $this->workspaceInstancesByName)) { + $workspace = $this->contentGraphFactory->findWorkspaceByName($workspaceName); + if ($workspace === null) { + return null; + } + $this->workspaceInstancesByName[$workspaceName->value] = $workspace; + } + return $this->workspaceInstancesByName[$workspaceName->value]; + } + + public function getWorkspaces(): Workspaces + { + return $this->contentGraphFactory->getWorkspaces(); + } + + public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream + { + if (!array_key_exists($contentStreamId->value, $this->contentStreamInstancesById)) { + $contentStream = $this->contentGraphFactory->findContentStreamById($contentStreamId); + if ($contentStream === null) { + return null; + } + $this->contentStreamInstancesById[$contentStreamId->value] = $contentStream; + } + return $this->contentStreamInstancesById[$contentStreamId->value]; + } + + public function getContentStreams(): ContentStreams + { + return $this->contentGraphFactory->getContentStreams(); + } + + /** + * @return iterable + * @internal This is currently only used by the {@see ContentStreamPruner} and might be removed in the future! + */ + public function getUnusedAndRemovedContentStreamIds(): iterable + { + return $this->contentGraphFactory->getUnusedAndRemovedContentStreamIds(); + } + + /** + * The default way to get a content graph to operate on. + * The currently assigned ContentStreamId for the given Workspace is resolved internally. + * + * @throws WorkspaceDoesNotExist if the provided workspace does not resolve to an existing content stream + * @see ContentRepository::getContentGraph() + */ + public function getContentGraphByWorkspaceName(WorkspaceName $workspaceName): ContentGraphInterface + { + if (!array_key_exists($workspaceName->value, $this->contentGraphInstancesByWorkspaceName)) { + $this->contentGraphInstancesByWorkspaceName[$workspaceName->value] = $this->contentGraphFactory->buildForWorkspace($workspaceName); + } + return $this->contentGraphInstancesByWorkspaceName[$workspaceName->value]; + } + + /** + * For testing we allow getting an instance set by both parameters, effectively overriding the relationship at will + * + * @param WorkspaceName $workspaceName + * @param ContentStreamId $contentStreamId + * @internal Only for testing + */ + public function getContentGraphByWorkspaceNameAndContentStreamId(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraphInterface + { + return $this->contentGraphFactory->buildForWorkspaceAndContentStream($workspaceName, $contentStreamId); + } +} diff --git a/Neos.ContentRepository.Core/Classes/ContentGraphFactoryInterface.php b/Neos.ContentRepository.Core/Classes/ContentGraphFactoryInterface.php index 2a766579751..2bf5dab4edc 100644 --- a/Neos.ContentRepository.Core/Classes/ContentGraphFactoryInterface.php +++ b/Neos.ContentRepository.Core/Classes/ContentGraphFactoryInterface.php @@ -16,8 +16,12 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphInterface; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreams; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; /** * Create implementations of ContentGraphs bound to a specific Workspace and/or ContentStream @@ -32,4 +36,18 @@ interface ContentGraphFactoryInterface public function buildForWorkspace(WorkspaceName $workspaceName): ContentGraphInterface; public function buildForWorkspaceAndContentStream(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraphInterface; + + public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace; + + public function getWorkspaces(): Workspaces; + + public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream; + + public function getContentStreams(): ContentStreams; + + /** + * @return iterable + * @internal This is currently only used by the {@see ContentStreamPruner} and might be removed in the future! + */ + public function getUnusedAndRemovedContentStreamIds(): iterable; } diff --git a/Neos.ContentRepository.Core/Classes/ContentGraphFinder.php b/Neos.ContentRepository.Core/Classes/ContentGraphFinder.php deleted file mode 100644 index 36a7da4fea1..00000000000 --- a/Neos.ContentRepository.Core/Classes/ContentGraphFinder.php +++ /dev/null @@ -1,61 +0,0 @@ -contentGraphFactory->buildForWorkspace($workspaceName); - } - - /** - * For testing we allow getting an instance set by both parameters, effectively overriding the relationship at will - * - * @param WorkspaceName $workspaceName - * @param ContentStreamId $contentStreamId - * @internal Only for the write side during publishing {@see \Neos\ContentRepository\Core\CommandHandlingDependencies::overrideContentStreamId} - */ - public function getByWorkspaceNameAndContentStreamId(WorkspaceName $workspaceName, ContentStreamId $contentStreamId): ContentGraphInterface - { - return $this->contentGraphFactory->buildForWorkspaceAndContentStream($workspaceName, $contentStreamId); - } -} diff --git a/Neos.ContentRepository.Core/Classes/ContentRepository.php b/Neos.ContentRepository.Core/Classes/ContentRepository.php index d107f399139..12756b66312 100644 --- a/Neos.ContentRepository.Core/Classes/ContentRepository.php +++ b/Neos.ContentRepository.Core/Classes/ContentRepository.php @@ -35,13 +35,15 @@ use Neos\ContentRepository\Core\Projection\ProjectionStateInterface; use Neos\ContentRepository\Core\Projection\ProjectionStatuses; use Neos\ContentRepository\Core\Projection\WithMarkStaleInterface; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; -use Neos\ContentRepository\Core\Projection\Workspace\Workspaces; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryStatus; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\User\UserIdProviderInterface; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreams; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; use Neos\EventStore\EventStoreInterface; use Neos\EventStore\Model\Event\EventMetadata; use Neos\EventStore\Model\EventEnvelope; @@ -241,13 +243,22 @@ public function getNodeTypeManager(): NodeTypeManager public function findWorkspaceByName(WorkspaceName $workspaceName): ?Workspace { - // TODO implement - return null; + return $this->getContentGraphAdapter()->findWorkspaceByName($workspaceName); } public function getWorkspaces(): Workspaces { - // TODO implement + return $this->getContentGraphAdapter()->getWorkspaces(); + } + + public function findContentStreamById(ContentStreamId $contentStreamId): ?ContentStream + { + return $this->getContentGraphAdapter()->findContentStreamById($contentStreamId); + } + + public function getContentStreams(): ContentStreams + { + return $this->getContentGraphAdapter()->getContentStreams(); } /** @@ -255,7 +266,7 @@ public function getWorkspaces(): Workspaces */ public function getContentGraph(WorkspaceName $workspaceName): ContentGraphInterface { - return $this->projectionState(ContentGraphFinder::class)->getByWorkspaceName($workspaceName); + return $this->getContentGraphAdapter()->getContentGraphByWorkspaceName($workspaceName); } public function getVariationGraph(): InterDimensionalVariationGraph @@ -267,4 +278,9 @@ public function getContentDimensionSource(): ContentDimensionSourceInterface { return $this->contentDimensionSource; } + + private function getContentGraphAdapter(): ContentGraphAdapter + { + return $this->projectionState(ContentGraphAdapter::class); + } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php index 69259c8a5fa..428ac575ee8 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php +++ b/Neos.ContentRepository.Core/Classes/Feature/Common/ConstraintChecks.php @@ -60,7 +60,7 @@ use Neos\ContentRepository\Core\SharedModel\Node\PropertyName; use Neos\ContentRepository\Core\SharedModel\Node\ReferenceName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\EventStore\Model\EventStream\ExpectedVersion; @@ -87,8 +87,8 @@ protected function requireContentStream( 1521386692 ); } - $state = $commandHandlingDependencies->getContentStreamState($contentStreamId); - if ($state === ContentStreamState::STATE_CLOSED) { + $state = $commandHandlingDependencies->getContentStreamStatus($contentStreamId); + if ($state === ContentStreamStatus::CLOSED) { throw new ContentStreamIsClosed( 'Content stream "' . $contentStreamId->value . '" is closed.', 1710260081 diff --git a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Command/ReopenContentStream.php b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Command/ReopenContentStream.php index 41436f34d75..10280ae9890 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Command/ReopenContentStream.php +++ b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Command/ReopenContentStream.php @@ -16,7 +16,7 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; /** * @internal implementation detail. You must not use this command directly. @@ -27,21 +27,21 @@ { /** * @param ContentStreamId $contentStreamId The id of the content stream to reopen - * @param ContentStreamState $previousState The state the content stream was in before closing and is to be reset to + * @param ContentStreamStatus $previousState The state the content stream was in before closing and is to be reset to */ private function __construct( public ContentStreamId $contentStreamId, - public ContentStreamState $previousState + public ContentStreamStatus $previousState ) { } /** * @param ContentStreamId $contentStreamId The id of the content stream to reopen - * @param ContentStreamState $previousState The state the content stream was in before closing and is to be reset to + * @param ContentStreamStatus $previousState The state the content stream was in before closing and is to be reset to */ public static function create( ContentStreamId $contentStreamId, - ContentStreamState $previousState + ContentStreamStatus $previousState ): self { return new self( $contentStreamId, @@ -57,7 +57,7 @@ public static function fromArray(array $array): self { return new self( ContentStreamId::fromString($array['contentStreamId']), - ContentStreamState::from($array['previousState']), + ContentStreamStatus::from($array['previousState']), ); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Event/ContentStreamWasReopened.php b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Event/ContentStreamWasReopened.php index 7e727cf998f..4b0248803f7 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Event/ContentStreamWasReopened.php +++ b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamClosing/Event/ContentStreamWasReopened.php @@ -17,7 +17,7 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; /** * @api events are the persistence-API of the content repository @@ -26,7 +26,7 @@ { public function __construct( public ContentStreamId $contentStreamId, - public ContentStreamState $previousState, + public ContentStreamStatus $previousState, ) { } @@ -39,7 +39,7 @@ public static function fromArray(array $values): self { return new self( ContentStreamId::fromString($values['contentStreamId']), - ContentStreamState::from($values['previousState']), + ContentStreamStatus::from($values['previousState']), ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamCommandHandler.php index c1e33319d5f..76d48439e76 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamCommandHandler.php @@ -35,7 +35,7 @@ use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamIsClosed; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamIsNotClosed; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\EventStore\Model\EventStream\ExpectedVersion; /** @@ -216,7 +216,7 @@ protected function requireContentStreamToNotBeClosed( ContentStreamId $contentStreamId, CommandHandlingDependencies $commandHandlingDependencies ): void { - if ($commandHandlingDependencies->getContentStreamState($contentStreamId) === ContentStreamState::STATE_CLOSED) { + if ($commandHandlingDependencies->getContentStreamStatus($contentStreamId) === ContentStreamStatus::CLOSED) { throw new ContentStreamIsClosed( 'Content stream "' . $contentStreamId->value . '" is closed.', 1710260081 @@ -228,7 +228,7 @@ protected function requireContentStreamToBeClosed( ContentStreamId $contentStreamId, CommandHandlingDependencies $commandHandlingDependencies ): void { - if ($commandHandlingDependencies->getContentStreamState($contentStreamId) !== ContentStreamState::STATE_CLOSED) { + if ($commandHandlingDependencies->getContentStreamStatus($contentStreamId) !== ContentStreamStatus::CLOSED) { throw new ContentStreamIsNotClosed( 'Content stream "' . $contentStreamId->value . '" is not closed.', 1710405911 diff --git a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamEventStreamName.php b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamEventStreamName.php index 395d7861959..902defb7b3a 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/ContentStreamEventStreamName.php +++ b/Neos.ContentRepository.Core/Classes/Feature/ContentStreamEventStreamName.php @@ -14,6 +14,7 @@ namespace Neos\ContentRepository\Core\Feature; +use Neos\ContentRepository\Core\Feature\Common\EmbedsContentStreamId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\EventStore\Model\Event\StreamName; @@ -41,6 +42,7 @@ public static function isContentStreamStreamName(StreamName $streamName): bool return str_starts_with($streamName->value, self::EVENT_STREAM_NAME_PREFIX); } + /** @todo rather use {@see EmbedsContentStreamId} instead!? */ public static function extractContentStreamIdFromStreamName(StreamName $streamName): ContentStreamId { if (!self::isContentStreamStreamName($streamName)) { diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php index 3ca93c83168..ea4f29c6232 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php @@ -43,7 +43,6 @@ use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\ChangeBaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceBaseWorkspaceWasChanged; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceOwnerWasChanged; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Event\WorkspaceWasRemoved; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Exception\BaseWorkspaceEqualsWorkspaceException; use Neos\ContentRepository\Core\Feature\WorkspaceModification\Exception\CircularRelationBetweenWorkspacesException; @@ -64,7 +63,7 @@ use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Event\WorkspaceWasRebased; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamAlreadyExists; use Neos\ContentRepository\Core\SharedModel\Exception\ContentStreamDoesNotExistYet; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; @@ -142,10 +141,7 @@ private function handleCreateWorkspace( new WorkspaceWasCreated( $command->workspaceName, $command->baseWorkspaceName, - $command->workspaceTitle, - $command->workspaceDescription, $command->newContentStreamId, - $command->workspaceOwner ) ); @@ -178,8 +174,6 @@ private function handleCreateRootWorkspace( $events = Events::with( new RootWorkspaceWasCreated( $command->workspaceName, - $command->workspaceTitle, - $command->workspaceDescription, $newContentStreamId ) ); @@ -327,7 +321,7 @@ private function handleRebaseWorkspace( if (!$commandHandlingDependencies->contentStreamExists($oldWorkspaceContentStreamId)) { throw new \DomainException('Cannot rebase a workspace with a stateless content stream', 1711718314); } - $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamState($oldWorkspaceContentStreamId); + $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamStatus($oldWorkspaceContentStreamId); // 0) close old content stream $commandHandlingDependencies->handle( @@ -460,7 +454,7 @@ private function handlePublishIndividualNodesFromWorkspace( if (!$commandHandlingDependencies->contentStreamExists($oldWorkspaceContentStreamId)) { throw new \DomainException('Cannot publish nodes on a workspace with a stateless content stream', 1710410114); } - $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamState($oldWorkspaceContentStreamId); + $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamStatus($oldWorkspaceContentStreamId); $baseWorkspace = $this->requireBaseWorkspace($workspace, $commandHandlingDependencies); // 1) close old content stream @@ -601,7 +595,7 @@ private function handleDiscardIndividualNodesFromWorkspace( if (!$commandHandlingDependencies->contentStreamExists($contentGraph->getContentStreamId())) { throw new \DomainException('Cannot discard nodes on a workspace with a stateless content stream', 1710408112); } - $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamState($contentGraph->getContentStreamId()); + $oldWorkspaceContentStreamIdState = $commandHandlingDependencies->getContentStreamStatus($contentGraph->getContentStreamId()); $baseWorkspace = $this->requireBaseWorkspace($workspace, $commandHandlingDependencies); // 1) close old content stream diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateRootWorkspace.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateRootWorkspace.php index 748c7585684..2a36c7655cb 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateRootWorkspace.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateRootWorkspace.php @@ -16,9 +16,7 @@ use Neos\ContentRepository\Core\CommandHandler\CommandInterface; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Command to create a root workspace. @@ -31,26 +29,20 @@ { /** * @param WorkspaceName $workspaceName Unique name of the workspace to create - * @param WorkspaceTitle $workspaceTitle Human-readable title of the workspace to create (can be changed) - * @param WorkspaceDescription $workspaceDescription Description of the workspace to create (can be changed) * @param ContentStreamId $newContentStreamId The id of the content stream the new workspace is assigned to initially */ private function __construct( public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, - public WorkspaceDescription $workspaceDescription, public ContentStreamId $newContentStreamId ) { } /** * @param WorkspaceName $workspaceName Name of the workspace to create - * @param WorkspaceTitle $workspaceTitle Human-readable title of the workspace to create (can be changed) - * @param WorkspaceDescription $workspaceDescription Description of the workspace to create (can be changed) * @param ContentStreamId $newContentStreamId The id of the content stream the new workspace is assigned to initially */ - public static function create(WorkspaceName $workspaceName, WorkspaceTitle $workspaceTitle, WorkspaceDescription $workspaceDescription, ContentStreamId $newContentStreamId): self + public static function create(WorkspaceName $workspaceName, ContentStreamId $newContentStreamId): self { - return new self($workspaceName, $workspaceTitle, $workspaceDescription, $newContentStreamId); + return new self($workspaceName, $newContentStreamId); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateWorkspace.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateWorkspace.php index 74ec7248408..4add3ccf28f 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateWorkspace.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Command/CreateWorkspace.php @@ -15,11 +15,8 @@ namespace Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command; use Neos\ContentRepository\Core\CommandHandler\CommandInterface; -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; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Create a new workspace, based on an existing baseWorkspace @@ -31,31 +28,22 @@ /** * @param WorkspaceName $workspaceName Unique name of the workspace to create * @param WorkspaceName $baseWorkspaceName Name of the base workspace - * @param WorkspaceTitle $workspaceTitle Human-readable title of the workspace to create (can be changed) - * @param WorkspaceDescription $workspaceDescription Description of the workspace to create (can be changed) * @param ContentStreamId $newContentStreamId The id of the content stream the new workspace is assigned to initially - * @param UserId|null $workspaceOwner Owner of the new workspace (optional) */ private function __construct( public WorkspaceName $workspaceName, public WorkspaceName $baseWorkspaceName, - public WorkspaceTitle $workspaceTitle, - public WorkspaceDescription $workspaceDescription, public ContentStreamId $newContentStreamId, - public ?UserId $workspaceOwner, ) { } /** * @param WorkspaceName $workspaceName Unique name of the workspace to create * @param WorkspaceName $baseWorkspaceName Name of the base workspace - * @param WorkspaceTitle $workspaceTitle Human-readable title of the workspace to create (can be changed) - * @param WorkspaceDescription $workspaceDescription Description of the workspace to create (can be changed) * @param ContentStreamId $newContentStreamId The id of the content stream the new workspace is assigned to initially - * @param UserId|null $workspaceOwner Owner of the new workspace (optional) */ - public static function create(WorkspaceName $workspaceName, WorkspaceName $baseWorkspaceName, WorkspaceTitle $workspaceTitle, WorkspaceDescription $workspaceDescription, ContentStreamId $newContentStreamId, ?UserId $workspaceOwner = null): self + public static function create(WorkspaceName $workspaceName, WorkspaceName $baseWorkspaceName, ContentStreamId $newContentStreamId): self { - return new self($workspaceName, $baseWorkspaceName, $workspaceTitle, $workspaceDescription, $newContentStreamId, $workspaceOwner); + return new self($workspaceName, $baseWorkspaceName, $newContentStreamId); } } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/RootWorkspaceWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/RootWorkspaceWasCreated.php index a1a5197d265..e168401cc8d 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/RootWorkspaceWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/RootWorkspaceWasCreated.php @@ -19,9 +19,7 @@ use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; use Neos\ContentRepository\Core\Feature\ContentStreamCreation\Event\ContentStreamWasCreated; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Event triggered to indicate that a root workspace, i.e. a workspace without base workspace, was created. @@ -35,8 +33,6 @@ { public function __construct( public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, - public WorkspaceDescription $workspaceDescription, public ContentStreamId $newContentStreamId ) { } @@ -50,8 +46,6 @@ public static function fromArray(array $values): self { return new self( WorkspaceName::fromString($values['workspaceName']), - WorkspaceTitle::fromString($values['workspaceTitle']), - WorkspaceDescription::fromString($values['workspaceDescription']), ContentStreamId::fromString($values['newContentStreamId']), ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/WorkspaceWasCreated.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/WorkspaceWasCreated.php index 8cefa918108..c30a9d39796 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/WorkspaceWasCreated.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceCreation/Event/WorkspaceWasCreated.php @@ -17,11 +17,8 @@ use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; use Neos\ContentRepository\Core\Feature\ContentStreamForking\Event\ContentStreamWasForked; -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; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Event triggered to indicate that a workspace was created, based on a base workspace. @@ -36,10 +33,7 @@ public function __construct( public WorkspaceName $workspaceName, public WorkspaceName $baseWorkspaceName, - public WorkspaceTitle $workspaceTitle, - public WorkspaceDescription $workspaceDescription, public ContentStreamId $newContentStreamId, - public ?UserId $workspaceOwner = null ) { } @@ -53,10 +47,7 @@ public static function fromArray(array $values): self return new self( WorkspaceName::fromString($values['workspaceName']), WorkspaceName::fromString($values['baseWorkspaceName']), - WorkspaceTitle::fromString($values['workspaceTitle']), - WorkspaceDescription::fromString($values['workspaceDescription']), ContentStreamId::fromString($values['newContentStreamId']), - $values['workspaceOwner'] ? UserId::fromString($values['workspaceOwner']) : null ); } diff --git a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceModification/Event/WorkspaceWasRenamed.php b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceModification/Event/WorkspaceWasRenamed.php index cc90b47f89e..3d0bfe5f34e 100644 --- a/Neos.ContentRepository.Core/Classes/Feature/WorkspaceModification/Event/WorkspaceWasRenamed.php +++ b/Neos.ContentRepository.Core/Classes/Feature/WorkspaceModification/Event/WorkspaceWasRenamed.php @@ -8,7 +8,6 @@ use Neos\ContentRepository\Core\Feature\Common\EmbedsWorkspaceName; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Event triggered to indicate that a workspace title or description has changed. @@ -20,8 +19,8 @@ { public function __construct( public WorkspaceName $workspaceName, - public WorkspaceTitle $workspaceTitle, - public WorkspaceDescription $workspaceDescription, + public string $workspaceTitle, + public string $workspaceDescription, ) { } @@ -34,8 +33,8 @@ public static function fromArray(array $values): self { return new self( WorkspaceName::fromString($values['workspaceName']), - WorkspaceTitle::fromString($values['workspaceTitle']), - WorkspaceDescription::fromString($values['workspaceDescription']), + $values['workspaceTitle'], + $values['workspaceDescription'], ); } diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.monopic b/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.monopic deleted file mode 100644 index d5448e332f0..00000000000 Binary files a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.monopic and /dev/null differ diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.php b/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.php deleted file mode 100644 index 505bac18c11..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamFinder.php +++ /dev/null @@ -1,192 +0,0 @@ - - */ - public function findAllIds(): iterable - { - $contentStreamIds = $this->dbal->executeQuery('SELECT contentstreamid FROM ' . $this->tableName)->fetchFirstColumn(); - return array_map(ContentStreamId::fromString(...), $contentStreamIds); - } - - /** - * @param bool $findTemporaryContentStreams if TRUE, will find all content streams not bound to a workspace - * @return array - */ - public function findUnusedContentStreams(bool $findTemporaryContentStreams): iterable - { - $states = [ - ContentStreamState::STATE_NO_LONGER_IN_USE, - ContentStreamState::STATE_REBASE_ERROR, - ]; - - if ($findTemporaryContentStreams === true) { - $states[] = ContentStreamState::STATE_CREATED; - $states[] = ContentStreamState::STATE_FORKED; - } - - $contentStreamIds = $this->dbal->executeQuery( - ' - SELECT contentstreamid FROM ' . $this->tableName . ' - WHERE removed = FALSE - AND state IN (:states) - ', - [ - 'states' => array_map( - fn (ContentStreamState $contentStreamState): string => $contentStreamState->value, - $states - ) - ], - [ - 'states' => Connection::PARAM_STR_ARRAY - ] - )->fetchFirstColumn(); - - return array_map(ContentStreamId::fromString(...), $contentStreamIds); - } - - public function findStateForContentStream(ContentStreamId $contentStreamId): ?ContentStreamState - { - /* @var $state string|false */ - $state = $this->dbal->executeQuery( - ' - SELECT state FROM ' . $this->tableName . ' - WHERE contentstreamid = :contentStreamId - AND removed = FALSE - ', - [ - 'contentStreamId' => $contentStreamId->value, - ] - )->fetchOne(); - - return ContentStreamState::tryFrom($state ?: ''); - } - - /** - * @return array - */ - public function findUnusedAndRemovedContentStreams(): iterable - { - $contentStreamIds = $this->dbal->executeQuery( - ' - WITH RECURSIVE transitiveUsedContentStreams (contentstreamid) AS ( - -- initial case: find all content streams currently in direct use by a workspace - SELECT contentstreamid FROM ' . $this->tableName . ' - WHERE - state = :inUseState - AND removed = false - UNION - -- now, when a content stream is in use by a workspace, its source content stream is - -- also "transitively" in use. - SELECT sourceContentStreamId FROM ' . $this->tableName . ' - JOIN transitiveUsedContentStreams - ON ' . $this->tableName . '.contentStreamId - = transitiveUsedContentStreams.contentStreamId - WHERE - ' . $this->tableName . '.sourceContentStreamId IS NOT NULL - ) - - -- now, we check for removed content streams which we do not need anymore transitively - SELECT contentstreamid FROM ' . $this->tableName . ' AS cs - WHERE removed = true - AND NOT EXISTS ( - SELECT 1 - FROM transitiveUsedContentStreams - WHERE - cs.contentstreamid = transitiveUsedContentStreams.contentstreamid - ) - ', - [ - 'inUseState' => ContentStreamState::STATE_IN_USE_BY_WORKSPACE->value - ] - )->fetchFirstColumn(); - return array_map(ContentStreamId::fromString(...), $contentStreamIds); - } - - public function findVersionForContentStream(ContentStreamId $contentStreamId): MaybeVersion - { - /* @var $state string|false */ - $version = $this->dbal->executeQuery( - ' - SELECT version FROM ' . $this->tableName . ' - WHERE contentStreamId = :contentStreamId - ', - [ - 'contentStreamId' => $contentStreamId->value, - ] - )->fetchOne(); - - if ($version === false) { - return MaybeVersion::fromVersionOrNull(null); - } - - return MaybeVersion::fromVersionOrNull(Version::fromInteger($version)); - } - - public function hasContentStream(ContentStreamId $contentStreamId): bool - { - /* @var $state string|false */ - $version = $this->dbal->executeQuery( - ' - SELECT version FROM ' . $this->tableName . ' - WHERE contentStreamId = :contentStreamId - ', - [ - 'contentStreamId' => $contentStreamId->value - ] - )->fetchOne(); - - return $version !== false; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjection.php b/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjection.php deleted file mode 100644 index 96a509de9f4..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjection.php +++ /dev/null @@ -1,407 +0,0 @@ - - */ -class ContentStreamProjection implements ProjectionInterface -{ - /** - * @var ContentStreamFinder|null Cache for the content stream finder returned by {@see getState()}, - * so that always the same instance is returned - */ - private ?ContentStreamFinder $contentStreamFinder = null; - private DbalCheckpointStorage $checkpointStorage; - - public function __construct( - private readonly Connection $dbal, - private readonly string $tableName - ) { - $this->checkpointStorage = new DbalCheckpointStorage( - $this->dbal, - $this->tableName . '_checkpoint', - self::class - ); - } - - public function setUp(): void - { - $statements = $this->determineRequiredSqlStatements(); - // MIGRATIONS - if ($this->dbal->getSchemaManager()->tablesExist([$this->tableName])) { - // added 2023-04-01 - $statements[] = sprintf("UPDATE %s SET state='FORKED' WHERE state='REBASING'; ", $this->tableName); - } - foreach ($statements as $statement) { - $this->dbal->executeStatement($statement); - } - $this->checkpointStorage->setUp(); - } - - public function status(): ProjectionStatus - { - $checkpointStorageStatus = $this->checkpointStorage->status(); - if ($checkpointStorageStatus->type === CheckpointStorageStatusType::ERROR) { - return ProjectionStatus::error($checkpointStorageStatus->details); - } - if ($checkpointStorageStatus->type === CheckpointStorageStatusType::SETUP_REQUIRED) { - return ProjectionStatus::setupRequired($checkpointStorageStatus->details); - } - try { - $this->dbal->connect(); - } catch (\Throwable $e) { - return ProjectionStatus::error(sprintf('Failed to connect to database: %s', $e->getMessage())); - } - try { - $requiredSqlStatements = $this->determineRequiredSqlStatements(); - } catch (\Throwable $e) { - return ProjectionStatus::error(sprintf('Failed to determine required SQL statements: %s', $e->getMessage())); - } - if ($requiredSqlStatements !== []) { - return ProjectionStatus::setupRequired(sprintf('The following SQL statement%s required: %s', count($requiredSqlStatements) !== 1 ? 's are' : ' is', implode(chr(10), $requiredSqlStatements))); - } - return ProjectionStatus::ok(); - } - - /** - * @return array - */ - private function determineRequiredSqlStatements(): array - { - $schemaManager = $this->dbal->createSchemaManager(); - $schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [ - (new Table($this->tableName, [ - DbalSchemaFactory::columnForContentStreamId('contentStreamId')->setNotnull(true), - (new Column('version', Type::getType(Types::INTEGER)))->setNotnull(true), - DbalSchemaFactory::columnForContentStreamId('sourceContentStreamId')->setNotnull(false), - // Should become a DB ENUM (unclear how to configure with DBAL) or int (latter needs adaption to code) - (new Column('state', Type::getType(Types::BINARY)))->setLength(20)->setNotnull(true), - (new Column('removed', Type::getType(Types::BOOLEAN)))->setDefault(false)->setNotnull(false) - ])) - ]); - - return DbalSchemaDiff::determineRequiredSqlStatements($this->dbal, $schema); - } - - public function reset(): void - { - $this->dbal->executeStatement('TRUNCATE table ' . $this->tableName); - $this->checkpointStorage->acquireLock(); - $this->checkpointStorage->updateAndReleaseLock(SequenceNumber::none()); - } - - public function canHandle(EventInterface $event): bool - { - return in_array($event::class, [ - ContentStreamWasCreated::class, - RootWorkspaceWasCreated::class, - WorkspaceWasCreated::class, - ContentStreamWasForked::class, - WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class, - WorkspaceWasPartiallyPublished::class, - WorkspaceWasPublished::class, - WorkspaceWasRebased::class, - WorkspaceRebaseFailed::class, - ContentStreamWasClosed::class, - ContentStreamWasReopened::class, - ContentStreamWasRemoved::class, - DimensionShineThroughWasAdded::class, - ]) - || $event instanceof EmbedsContentStreamId; - } - - public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void - { - if ($event instanceof EmbedsContentStreamId) { - $this->updateContentStreamVersion($event, $eventEnvelope); - } - match ($event::class) { - ContentStreamWasClosed::class => $this->whenContentStreamWasClosed($event, $eventEnvelope), - ContentStreamWasCreated::class => $this->whenContentStreamWasCreated($event, $eventEnvelope), - ContentStreamWasForked::class => $this->whenContentStreamWasForked($event, $eventEnvelope), - ContentStreamWasRemoved::class => $this->whenContentStreamWasRemoved($event, $eventEnvelope), - ContentStreamWasReopened::class => $this->whenContentStreamWasReopened($event, $eventEnvelope), - DimensionShineThroughWasAdded::class => $this->whenDimensionShineThroughWasAdded($event, $eventEnvelope), - RootWorkspaceWasCreated::class => $this->whenRootWorkspaceWasCreated($event), - WorkspaceRebaseFailed::class => $this->whenWorkspaceRebaseFailed($event), - WorkspaceWasCreated::class => $this->whenWorkspaceWasCreated($event), - WorkspaceWasDiscarded::class => $this->whenWorkspaceWasDiscarded($event), - WorkspaceWasPartiallyDiscarded::class => $this->whenWorkspaceWasPartiallyDiscarded($event), - WorkspaceWasPartiallyPublished::class => $this->whenWorkspaceWasPartiallyPublished($event), - WorkspaceWasPublished::class => $this->whenWorkspaceWasPublished($event), - WorkspaceWasRebased::class => $this->whenWorkspaceWasRebased($event), - default => $event instanceof EmbedsContentStreamId || throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))), - }; - } - - public function getCheckpointStorage(): CheckpointStorageInterface - { - return $this->checkpointStorage; - } - - public function getState(): ProjectionStateInterface - { - if (!$this->contentStreamFinder) { - $this->contentStreamFinder = new ContentStreamFinder( - $this->dbal, - $this->tableName - ); - } - return $this->contentStreamFinder; - } - - private function whenContentStreamWasCreated(ContentStreamWasCreated $event, EventEnvelope $eventEnvelope): void - { - $this->dbal->insert($this->tableName, [ - 'contentStreamId' => $event->contentStreamId->value, - 'version' => self::extractVersion($eventEnvelope), - 'state' => ContentStreamState::STATE_CREATED->value, - ]); - } - - private function whenRootWorkspaceWasCreated(RootWorkspaceWasCreated $event): void - { - // the content stream is in use now - $this->updateStateForContentStream( - $event->newContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE, - ); - } - - private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void - { - // the content stream is in use now - $this->updateStateForContentStream( - $event->newContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE, - ); - } - - private function whenContentStreamWasForked(ContentStreamWasForked $event, EventEnvelope $eventEnvelope): void - { - $this->dbal->insert($this->tableName, [ - 'contentStreamId' => $event->newContentStreamId->value, - 'version' => self::extractVersion($eventEnvelope), - 'sourceContentStreamId' => $event->sourceContentStreamId->value, - 'state' => ContentStreamState::STATE_FORKED->value - ]); - } - - private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void - { - // the new content stream is in use now - $this->updateStateForContentStream( - $event->newContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE - ); - - // the previous content stream is no longer in use - $this->updateStateForContentStream( - $event->previousContentStreamId, - ContentStreamState::STATE_NO_LONGER_IN_USE - ); - } - - private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscarded $event): void - { - // the new content stream is in use now - $this->updateStateForContentStream( - $event->newContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE - ); - - // the previous content stream is no longer in use - $this->updateStateForContentStream( - $event->previousContentStreamId, - ContentStreamState::STATE_NO_LONGER_IN_USE - ); - } - - private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void - { - // the new content stream is in use now - $this->updateStateForContentStream( - $event->newSourceContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE - ); - - // the previous content stream is no longer in use - $this->updateStateForContentStream( - $event->previousSourceContentStreamId, - ContentStreamState::STATE_NO_LONGER_IN_USE - ); - } - - private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void - { - // the new content stream is in use now - $this->updateStateForContentStream( - $event->newSourceContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE - ); - - // the previous content stream is no longer in use - $this->updateStateForContentStream( - $event->previousSourceContentStreamId, - ContentStreamState::STATE_NO_LONGER_IN_USE - ); - } - - private function whenWorkspaceWasRebased(WorkspaceWasRebased $event): void - { - // the new content stream is in use now - $this->updateStateForContentStream( - $event->newContentStreamId, - ContentStreamState::STATE_IN_USE_BY_WORKSPACE - ); - - // the previous content stream is no longer in use - $this->updateStateForContentStream( - $event->previousContentStreamId, - ContentStreamState::STATE_NO_LONGER_IN_USE - ); - } - - private function whenWorkspaceRebaseFailed(WorkspaceRebaseFailed $event): void - { - $this->updateStateForContentStream( - $event->candidateContentStreamId, - ContentStreamState::STATE_REBASE_ERROR - ); - } - - private function whenContentStreamWasClosed(ContentStreamWasClosed $event, EventEnvelope $eventEnvelope): void - { - $this->updateStateForContentStream( - $event->contentStreamId, - ContentStreamState::STATE_CLOSED, - ); - $this->dbal->update($this->tableName, [ - 'version' => self::extractVersion($eventEnvelope), - ], [ - 'contentStreamId' => $event->contentStreamId->value - ]); - } - - private function whenContentStreamWasReopened(ContentStreamWasReopened $event, EventEnvelope $eventEnvelope): void - { - $this->updateStateForContentStream( - $event->contentStreamId, - $event->previousState, - ); - $this->dbal->update($this->tableName, [ - 'version' => self::extractVersion($eventEnvelope), - ], [ - 'contentStreamId' => $event->contentStreamId->value - ]); - } - - private function whenContentStreamWasRemoved(ContentStreamWasRemoved $event, EventEnvelope $eventEnvelope): void - { - $this->dbal->update($this->tableName, [ - 'removed' => true, - 'version' => self::extractVersion($eventEnvelope), - ], [ - 'contentStreamId' => $event->contentStreamId->value - ]); - } - - private function whenDimensionShineThroughWasAdded(DimensionShineThroughWasAdded $event, EventEnvelope $eventEnvelope): void - { - $this->dbal->update($this->tableName, [ - 'version' => self::extractVersion($eventEnvelope), - ], [ - 'contentStreamId' => $event->contentStreamId->value - ]); - } - - private function updateStateForContentStream(ContentStreamId $contentStreamId, ContentStreamState $state): void - { - $this->dbal->update($this->tableName, [ - 'state' => $state->value, - ], [ - 'contentStreamId' => $contentStreamId->value - ]); - } - - private function updateContentStreamVersion( - EmbedsContentStreamId $eventInstance, - EventEnvelope $eventEnvelope - ): void { - $this->dbal->update($this->tableName, [ - 'version' => self::extractVersion($eventEnvelope), - ], [ - 'contentStreamId' => $eventInstance->getContentStreamId()->value - ]); - } - - - private static function extractVersion(EventEnvelope $eventEnvelope): int - { - if ( - !str_starts_with( - $eventEnvelope->streamName->value, - 'ContentStream:' - ) - ) { - throw new \RuntimeException( - 'Cannot extract version number, as it was projected on wrong stream "' - . $eventEnvelope->streamName->value . '", but needs to start with ' - . 'ContentStream:' - ); - } - return $eventEnvelope->version->value; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjectionFactory.php b/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjectionFactory.php deleted file mode 100644 index a7fa2c6bcd5..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/ContentStream/ContentStreamProjectionFactory.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @internal - */ -class ContentStreamProjectionFactory implements ProjectionFactoryInterface -{ - public function __construct( - private readonly Connection $dbal, - ) { - } - - public function build( - ProjectionFactoryDependencies $projectionFactoryDependencies, - array $options, - ): ContentStreamProjection { - $projectionShortName = strtolower(str_replace( - 'Projection', - '', - (new \ReflectionClass(ContentStreamProjection::class))->getShortName() - )); - return new ContentStreamProjection( - $this->dbal, - sprintf( - 'cr_%s_p_%s', - $projectionFactoryDependencies->contentRepositoryId->value, - $projectionShortName - ), - ); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspace.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspace.php deleted file mode 100644 index ddb826aff52..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspace.php +++ /dev/null @@ -1,132 +0,0 @@ -workspaceName = $workspaceName; - $this->baseWorkspaceName = $baseWorkspaceName; - $this->workspaceTitle = $workspaceTitle; - $this->workspaceDescription = $workspaceDescription; - $this->currentContentStreamId = $currentContentStreamId; - $this->status = $status; - $this->workspaceOwner = $workspaceOwner; - } - - /** - * Checks if this workspace is a user's personal workspace - * @deprecated with 9.0.0-beta14 owners/collaborators should be assigned to workspaces outside the Content Repository core - */ - public function isPersonalWorkspace(): bool - { - return str_starts_with($this->workspaceName->value, static::PERSONAL_WORKSPACE_PREFIX); - } - - /** - * Checks if this workspace is shared only across users with access to internal workspaces, for example "reviewers" - * - * @return bool - * @deprecated with 9.0.0-beta14 owners/collaborators should be assigned to workspaces outside the Content Repository core - */ - public function isPrivateWorkspace(): bool - { - return $this->workspaceOwner !== null && !$this->isPersonalWorkspace(); - } - - /** - * Checks if this workspace is shared across all editors - * - * @return boolean - * @deprecated with 9.0.0-beta14 owners/collaborators should be assigned to workspaces outside the Content Repository core - */ - public function isInternalWorkspace(): bool - { - return $this->baseWorkspaceName !== null && $this->workspaceOwner === null; - } - - /** - * Checks if this workspace is public to everyone, even without authentication - * - * @return boolean - * @deprecated with 9.0.0-beta14 owners/collaborators should be assigned to workspaces outside the Content Repository core - */ - public function isPublicWorkspace(): bool - { - return $this->baseWorkspaceName === null && $this->workspaceOwner === null; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php deleted file mode 100644 index bd87127f8d7..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceFinder.php +++ /dev/null @@ -1,197 +0,0 @@ -workspaceRuntimeCache->getWorkspaceByName($name); - if ($workspace !== null) { - return $workspace; - } - - $workspaceRow = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - WHERE workspaceName = :workspaceName - ', - [ - 'workspaceName' => $name->value, - ] - )->fetchAssociative(); - - if ($workspaceRow === false) { - return null; - } - - $workspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $this->workspaceRuntimeCache->setWorkspace($workspace); - return $workspace; - } - - public function findOneByCurrentContentStreamId( - ContentStreamId $contentStreamId - ): ?Workspace { - $workspace = $this->workspaceRuntimeCache->getByCurrentContentStreamId($contentStreamId); - if ($workspace !== null) { - return $workspace; - } - - $workspaceRow = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - WHERE currentContentStreamId = :currentContentStreamId - ', - [ - 'currentContentStreamId' => $contentStreamId->value - ] - )->fetchAssociative(); - - if ($workspaceRow === false) { - return null; - } - - $workspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $this->workspaceRuntimeCache->setWorkspace($workspace); - return $workspace; - } - - /** - * @return array - * @throws DBALException - */ - public function findByBaseWorkspace(WorkspaceName $baseWorkspace): array - { - $result = []; - - $workspaceRows = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - WHERE baseWorkspaceName = :workspaceName - ', - [ - 'workspaceName' => $baseWorkspace->value, - ] - )->fetchAllAssociative(); - - foreach ($workspaceRows as $workspaceRow) { - $similarlyNamedWorkspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $result[$similarlyNamedWorkspace->workspaceName->value] = $similarlyNamedWorkspace; - } - - return $result; - } - - public function findOneByWorkspaceOwner(string $owner): ?Workspace - { - $workspaceRow = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - WHERE workspaceOwner = :workspaceOwner - ', - [ - 'workspaceOwner' => $owner - ] - )->fetchAssociative(); - - if ($workspaceRow === false) { - return null; - } - - return $this->createWorkspaceFromDatabaseRow($workspaceRow); - } - - public function findAll(): Workspaces - { - $result = []; - - $workspaceRows = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' - ' - )->fetchAllAssociative(); - - foreach ($workspaceRows as $workspaceRow) { - $similarlyNamedWorkspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $result[$similarlyNamedWorkspace->workspaceName->value] = $similarlyNamedWorkspace; - } - - return Workspaces::fromArray($result); - } - - /** - * @return array - * @throws \Doctrine\DBAL\Driver\Exception - * @throws DBALException - */ - public function findOutdated(): array - { - $result = []; - - $workspaceRows = $this->dbal->executeQuery( - ' - SELECT * FROM ' . $this->tableName . ' WHERE status = :outdated - ', - [ - 'outdated' => WorkspaceStatus::OUTDATED->value - ] - )->fetchAllAssociative(); - - foreach ($workspaceRows as $workspaceRow) { - $similarlyNamedWorkspace = $this->createWorkspaceFromDatabaseRow($workspaceRow); - $result[$similarlyNamedWorkspace->workspaceName->value] = $similarlyNamedWorkspace; - } - - return $result; - } - - /** - * @param array $row - */ - private function createWorkspaceFromDatabaseRow(array $row): Workspace - { - return new Workspace( - WorkspaceName::fromString($row['workspacename']), - !empty($row['baseworkspacename']) ? WorkspaceName::fromString($row['baseworkspacename']) : null, - WorkspaceTitle::fromString($row['workspacetitle']), - WorkspaceDescription::fromString($row['workspacedescription']), - ContentStreamId::fromString($row['currentcontentstreamid']), - WorkspaceStatus::from($row['status']), - $row['workspaceowner'] - ); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjection.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjection.php deleted file mode 100644 index 04e58933399..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjection.php +++ /dev/null @@ -1,386 +0,0 @@ - - */ -class WorkspaceProjection implements ProjectionInterface, WithMarkStaleInterface -{ - private const DEFAULT_TEXT_COLLATION = 'utf8mb4_unicode_520_ci'; - - /** - * @var WorkspaceFinder|null Cache for the workspace finder returned by {@see getState()}, - * so that always the same instance is returned - */ - private ?WorkspaceFinder $workspaceFinder = null; - private DbalCheckpointStorage $checkpointStorage; - private WorkspaceRuntimeCache $workspaceRuntimeCache; - - public function __construct( - private readonly Connection $dbal, - private readonly string $tableName, - ) { - $this->checkpointStorage = new DbalCheckpointStorage( - $this->dbal, - $this->tableName . '_checkpoint', - self::class - ); - $this->workspaceRuntimeCache = new WorkspaceRuntimeCache(); - } - - public function setUp(): void - { - foreach ($this->determineRequiredSqlStatements() as $statement) { - $this->dbal->executeStatement($statement); - } - $this->checkpointStorage->setUp(); - } - - public function status(): ProjectionStatus - { - $checkpointStorageStatus = $this->checkpointStorage->status(); - if ($checkpointStorageStatus->type === CheckpointStorageStatusType::ERROR) { - return ProjectionStatus::error($checkpointStorageStatus->details); - } - if ($checkpointStorageStatus->type === CheckpointStorageStatusType::SETUP_REQUIRED) { - return ProjectionStatus::setupRequired($checkpointStorageStatus->details); - } - try { - $this->dbal->connect(); - } catch (\Throwable $e) { - return ProjectionStatus::error(sprintf('Failed to connect to database: %s', $e->getMessage())); - } - try { - $requiredSqlStatements = $this->determineRequiredSqlStatements(); - } catch (\Throwable $e) { - return ProjectionStatus::error(sprintf('Failed to determine required SQL statements: %s', $e->getMessage())); - } - if ($requiredSqlStatements !== []) { - return ProjectionStatus::setupRequired(sprintf('The following SQL statement%s required: %s', count($requiredSqlStatements) !== 1 ? 's are' : ' is', implode(chr(10), $requiredSqlStatements))); - } - return ProjectionStatus::ok(); - } - - /** - * @return array - */ - private function determineRequiredSqlStatements(): array - { - $schemaManager = $this->dbal->createSchemaManager(); - $workspaceTable = new Table($this->tableName, [ - DbalSchemaFactory::columnForWorkspaceName('workspacename'), - DbalSchemaFactory::columnForWorkspaceName('baseworkspacename')->setNotNull(false), - (new Column('workspacetitle', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - (new Column('workspacedescription', Type::getType(Types::STRING)))->setLength(255)->setNotnull(true)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - (new Column('workspaceowner', Type::getType(Types::STRING)))->setLength(255)->setNotnull(false)->setPlatformOption('collation', self::DEFAULT_TEXT_COLLATION), - DbalSchemaFactory::columnForContentStreamId('currentcontentstreamid')->setNotNull(true), - (new Column('status', Type::getType(Types::BINARY)))->setLength(20)->setNotnull(false) - ]); - $workspaceTable->setPrimaryKey(['workspacename']); - - $schema = DbalSchemaFactory::createSchemaWithTables($schemaManager, [$workspaceTable]); - return DbalSchemaDiff::determineRequiredSqlStatements($this->dbal, $schema); - } - - public function reset(): void - { - $this->dbal->exec('TRUNCATE ' . $this->tableName); - $this->checkpointStorage->acquireLock(); - $this->checkpointStorage->updateAndReleaseLock(SequenceNumber::none()); - } - - public function canHandle(EventInterface $event): bool - { - return in_array($event::class, [ - WorkspaceWasCreated::class, - WorkspaceWasRenamed::class, - RootWorkspaceWasCreated::class, - WorkspaceWasDiscarded::class, - WorkspaceWasPartiallyDiscarded::class, - WorkspaceWasPartiallyPublished::class, - WorkspaceWasPublished::class, - WorkspaceWasRebased::class, - WorkspaceRebaseFailed::class, - WorkspaceWasRemoved::class, - WorkspaceOwnerWasChanged::class, - WorkspaceBaseWorkspaceWasChanged::class, - ]); - } - - public function apply(EventInterface $event, EventEnvelope $eventEnvelope): void - { - match ($event::class) { - WorkspaceWasCreated::class => $this->whenWorkspaceWasCreated($event), - WorkspaceWasRenamed::class => $this->whenWorkspaceWasRenamed($event), - RootWorkspaceWasCreated::class => $this->whenRootWorkspaceWasCreated($event), - WorkspaceWasDiscarded::class => $this->whenWorkspaceWasDiscarded($event), - WorkspaceWasPartiallyDiscarded::class => $this->whenWorkspaceWasPartiallyDiscarded($event), - WorkspaceWasPartiallyPublished::class => $this->whenWorkspaceWasPartiallyPublished($event), - WorkspaceWasPublished::class => $this->whenWorkspaceWasPublished($event), - WorkspaceWasRebased::class => $this->whenWorkspaceWasRebased($event), - WorkspaceRebaseFailed::class => $this->whenWorkspaceRebaseFailed($event), - WorkspaceWasRemoved::class => $this->whenWorkspaceWasRemoved($event), - WorkspaceOwnerWasChanged::class => $this->whenWorkspaceOwnerWasChanged($event), - WorkspaceBaseWorkspaceWasChanged::class => $this->whenWorkspaceBaseWorkspaceWasChanged($event), - default => throw new \InvalidArgumentException(sprintf('Unsupported event %s', get_debug_type($event))), - }; - } - - public function getCheckpointStorage(): DbalCheckpointStorage - { - return $this->checkpointStorage; - } - - public function getState(): WorkspaceFinder - { - if (!$this->workspaceFinder) { - $this->workspaceFinder = new WorkspaceFinder( - $this->dbal, - $this->workspaceRuntimeCache, - $this->tableName - ); - } - return $this->workspaceFinder; - } - - public function markStale(): void - { - $this->workspaceRuntimeCache->disableCache(); - } - - private function whenWorkspaceWasCreated(WorkspaceWasCreated $event): void - { - $this->dbal->insert($this->tableName, [ - 'workspaceName' => $event->workspaceName->value, - 'baseWorkspaceName' => $event->baseWorkspaceName->value, - 'workspaceTitle' => $event->workspaceTitle->value, - 'workspaceDescription' => $event->workspaceDescription->value, - 'workspaceOwner' => $event->workspaceOwner?->value, - 'currentContentStreamId' => $event->newContentStreamId->value, - 'status' => WorkspaceStatus::UP_TO_DATE->value - ]); - } - - private function whenWorkspaceWasRenamed(WorkspaceWasRenamed $event): void - { - $this->dbal->update( - $this->tableName, - [ - 'workspaceTitle' => $event->workspaceTitle->value, - 'workspaceDescription' => $event->workspaceDescription->value, - ], - ['workspaceName' => $event->workspaceName->value] - ); - } - - private function whenRootWorkspaceWasCreated(RootWorkspaceWasCreated $event): void - { - $this->dbal->insert($this->tableName, [ - 'workspaceName' => $event->workspaceName->value, - 'workspaceTitle' => $event->workspaceTitle->value, - 'workspaceDescription' => $event->workspaceDescription->value, - 'currentContentStreamId' => $event->newContentStreamId->value, - 'status' => WorkspaceStatus::UP_TO_DATE->value - ]); - } - - private function whenWorkspaceWasDiscarded(WorkspaceWasDiscarded $event): void - { - $this->updateContentStreamId($event->newContentStreamId, $event->workspaceName); - $this->markWorkspaceAsOutdated($event->workspaceName); - $this->markDependentWorkspacesAsOutdated($event->workspaceName); - } - - private function whenWorkspaceWasPartiallyDiscarded(WorkspaceWasPartiallyDiscarded $event): void - { - $this->updateContentStreamId($event->newContentStreamId, $event->workspaceName); - $this->markDependentWorkspacesAsOutdated($event->workspaceName); - } - - private function whenWorkspaceWasPartiallyPublished(WorkspaceWasPartiallyPublished $event): void - { - // TODO: How do we test this method? - // It's hard to design a BDD testcase failing if this method is commented out... - $this->updateContentStreamId( - $event->newSourceContentStreamId, - $event->sourceWorkspaceName - ); - - $this->markDependentWorkspacesAsOutdated($event->targetWorkspaceName); - - // NASTY: we need to set the source workspace name as non-outdated; as it has been made up-to-date again. - $this->markWorkspaceAsUpToDate($event->sourceWorkspaceName); - - $this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName); - } - - private function whenWorkspaceWasPublished(WorkspaceWasPublished $event): void - { - // TODO: How do we test this method? - // It's hard to design a BDD testcase failing if this method is commented out... - $this->updateContentStreamId( - $event->newSourceContentStreamId, - $event->sourceWorkspaceName - ); - - $this->markDependentWorkspacesAsOutdated($event->targetWorkspaceName); - - // NASTY: we need to set the source workspace name as non-outdated; as it has been made up-to-date again. - $this->markWorkspaceAsUpToDate($event->sourceWorkspaceName); - - $this->markDependentWorkspacesAsOutdated($event->sourceWorkspaceName); - } - - private function whenWorkspaceWasRebased(WorkspaceWasRebased $event): void - { - $this->updateContentStreamId($event->newContentStreamId, $event->workspaceName); - $this->markDependentWorkspacesAsOutdated($event->workspaceName); - - // When the rebase is successful, we can set the status of the workspace back to UP_TO_DATE. - $this->markWorkspaceAsUpToDate($event->workspaceName); - } - - private function whenWorkspaceRebaseFailed(WorkspaceRebaseFailed $event): void - { - $this->markWorkspaceAsOutdatedConflict($event->workspaceName); - } - - private function whenWorkspaceWasRemoved(WorkspaceWasRemoved $event): void - { - $this->dbal->delete( - $this->tableName, - ['workspaceName' => $event->workspaceName->value] - ); - } - - private function whenWorkspaceOwnerWasChanged(WorkspaceOwnerWasChanged $event): void - { - $this->dbal->update( - $this->tableName, - ['workspaceOwner' => $event->newWorkspaceOwner], - ['workspaceName' => $event->workspaceName->value] - ); - } - - private function whenWorkspaceBaseWorkspaceWasChanged(WorkspaceBaseWorkspaceWasChanged $event): void - { - $this->dbal->update( - $this->tableName, - [ - 'baseWorkspaceName' => $event->baseWorkspaceName->value, - 'currentContentStreamId' => $event->newContentStreamId->value, - ], - ['workspaceName' => $event->workspaceName->value] - ); - } - - private function updateContentStreamId( - ContentStreamId $contentStreamId, - WorkspaceName $workspaceName, - ): void { - $this->dbal->update($this->tableName, [ - 'currentContentStreamId' => $contentStreamId->value, - ], [ - 'workspaceName' => $workspaceName->value - ]); - } - - private function markWorkspaceAsUpToDate(WorkspaceName $workspaceName): void - { - $this->dbal->executeUpdate(' - UPDATE ' . $this->tableName . ' - SET status = :upToDate - WHERE - workspacename = :workspaceName - ', [ - 'upToDate' => WorkspaceStatus::UP_TO_DATE->value, - 'workspaceName' => $workspaceName->value - ]); - } - - private function markDependentWorkspacesAsOutdated(WorkspaceName $baseWorkspaceName): void - { - $this->dbal->executeUpdate(' - UPDATE ' . $this->tableName . ' - SET status = :outdated - WHERE - baseworkspacename = :baseWorkspaceName - ', [ - 'outdated' => WorkspaceStatus::OUTDATED->value, - 'baseWorkspaceName' => $baseWorkspaceName->value - ]); - } - - private function markWorkspaceAsOutdated(WorkspaceName $workspaceName): void - { - $this->dbal->executeUpdate(' - UPDATE ' . $this->tableName . ' - SET - status = :outdated - WHERE - workspacename = :workspaceName - ', [ - 'outdated' => WorkspaceStatus::OUTDATED->value, - 'workspaceName' => $workspaceName->value - ]); - } - - private function markWorkspaceAsOutdatedConflict(WorkspaceName $workspaceName): void - { - $this->dbal->executeUpdate(' - UPDATE ' . $this->tableName . ' - SET - status = :outdatedConflict - WHERE - workspacename = :workspaceName - ', [ - 'outdatedConflict' => WorkspaceStatus::OUTDATED_CONFLICT->value, - 'workspaceName' => $workspaceName->value - ]); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjectionFactory.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjectionFactory.php deleted file mode 100644 index 094ee2e4e56..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceProjectionFactory.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @internal - */ -class WorkspaceProjectionFactory implements ProjectionFactoryInterface -{ - public function __construct( - private readonly Connection $dbal, - ) { - } - - public function build( - ProjectionFactoryDependencies $projectionFactoryDependencies, - array $options, - ): WorkspaceProjection { - $projectionShortName = strtolower(str_replace( - 'Projection', - '', - (new \ReflectionClass(WorkspaceProjection::class))->getShortName() - )); - return new WorkspaceProjection( - $this->dbal, - sprintf( - 'cr_%s_p_%s', - $projectionFactoryDependencies->contentRepositoryId->value, - $projectionShortName - ), - ); - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceRuntimeCache.php b/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceRuntimeCache.php deleted file mode 100644 index 5db459a2fff..00000000000 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/WorkspaceRuntimeCache.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ - private array $cachedWorkspacesByName = []; - - /** - * @var array - */ - private array $cachedWorkspacesByContentStreamId = []; - - /** - * @return void - */ - public function disableCache(): void - { - $this->cacheEnabled = false; - $this->cachedWorkspacesByName = []; - $this->cachedWorkspacesByContentStreamId = []; - } - - public function getWorkspaceByName(WorkspaceName $name): ?Workspace - { - if ($this->cacheEnabled === true && isset($this->cachedWorkspacesByName[$name->value])) { - return $this->cachedWorkspacesByName[$name->value]; - } - return null; - } - - public function setWorkspace(Workspace $workspace): void - { - if ($this->cacheEnabled === true) { - $this->cachedWorkspacesByName[$workspace->workspaceName->value] = $workspace; - $this->cachedWorkspacesByContentStreamId[ - $workspace->currentContentStreamId->value - ] = $workspace; - } - } - - public function getByCurrentContentStreamId(ContentStreamId $contentStreamId): ?Workspace - { - if ( - $this->cacheEnabled === true - && isset($this->cachedWorkspacesByContentStreamId[$contentStreamId->value]) - ) { - return $this->cachedWorkspacesByContentStreamId[$contentStreamId->value]; - } - return null; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Service/ContentRepositoryBootstrapper.php b/Neos.ContentRepository.Core/Classes/Service/ContentRepositoryBootstrapper.php index 6dd837c9a81..bcd01b31acd 100644 --- a/Neos.ContentRepository.Core/Classes/Service/ContentRepositoryBootstrapper.php +++ b/Neos.ContentRepository.Core/Classes/Service/ContentRepositoryBootstrapper.php @@ -8,12 +8,10 @@ use Neos\ContentRepository\Core\Feature\RootNodeCreation\Command\CreateRootNodeAggregateWithNode; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateRootWorkspace; use Neos\ContentRepository\Core\NodeType\NodeTypeName; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; /** * Utility class that provides functionality to initialize a Content Repository instance (i.e. create the first @@ -48,8 +46,6 @@ public function getOrCreateLiveWorkspace(): Workspace $this->contentRepository->handle( CreateRootWorkspace::create( $liveWorkspaceName, - WorkspaceTitle::fromString('Live'), - WorkspaceDescription::fromString('Public live workspace'), ContentStreamId::create() ) ); diff --git a/Neos.ContentRepository.Core/Classes/Service/ContentStreamPruner.php b/Neos.ContentRepository.Core/Classes/Service/ContentStreamPruner.php index eb5f5540917..cd151bf631d 100644 --- a/Neos.ContentRepository.Core/Classes/Service/ContentStreamPruner.php +++ b/Neos.ContentRepository.Core/Classes/Service/ContentStreamPruner.php @@ -4,27 +4,27 @@ namespace Neos\ContentRepository\Core\Service; -use Neos\ContentRepository\Core\CommandHandler\CommandResult; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; use Neos\ContentRepository\Core\Feature\ContentStreamRemoval\Command\RemoveContentStream; -use Neos\ContentRepository\Core\Projection\ContentStream\ContentStreamFinder; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStream; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\EventStore\EventStoreInterface; /** - * For internal implementation details, see {@see ContentStreamFinder}. + * For internal implementation details, see {@see ContentGraphAdapter}. * * @api */ class ContentStreamPruner implements ContentRepositoryServiceInterface { - private ?CommandResult $lastCommandResult; - public function __construct( private readonly ContentRepository $contentRepository, private readonly EventStoreInterface $eventStore, + private readonly ContentGraphAdapter $contentGraphAdapter, ) { } @@ -46,17 +46,23 @@ public function __construct( */ public function prune(bool $removeTemporary = false): iterable { - $unusedContentStreams = $this->contentRepository->getContentStreamFinder()->findUnusedContentStreams( - $removeTemporary + $status = [ContentStreamStatus::NO_LONGER_IN_USE, ContentStreamStatus::REBASE_ERROR]; + if ($removeTemporary) { + $status[] = ContentStreamStatus::CREATED; + $status[] = ContentStreamStatus::FORKED; + } + $unusedContentStreams = $this->contentRepository->getContentStreams()->filter( + static fn (ContentStream $contentStream) => in_array($contentStream->status, $status, true), ); - + $unusedContentStreamIds = []; foreach ($unusedContentStreams as $contentStream) { - $this->lastCommandResult = $this->contentRepository->handle( - RemoveContentStream::create($contentStream) + $this->contentRepository->handle( + RemoveContentStream::create($contentStream->id) ); + $unusedContentStreamIds[] = $contentStream->id; } - return $unusedContentStreams; + return $unusedContentStreamIds; } /** @@ -68,34 +74,24 @@ public function prune(bool $removeTemporary = false): iterable * * - Otherwise, we cannot replay the other content streams correctly (if the base content streams are missing). * - * @return iterable the identifiers of the removed content streams + * @return iterable the identifiers of the removed content streams */ public function pruneRemovedFromEventStream(): iterable { - $removedContentStreams = $this->contentRepository->getContentStreamFinder() - ->findUnusedAndRemovedContentStreams(); - - foreach ($removedContentStreams as $removedContentStream) { + $removedContentStreamIds = $this->contentGraphAdapter->getUnusedAndRemovedContentStreamIds(); + foreach ($removedContentStreamIds as $removedContentStream) { $streamName = ContentStreamEventStreamName::fromContentStreamId($removedContentStream) ->getEventStreamName(); $this->eventStore->deleteStream($streamName); } - - return $removedContentStreams; + return $removedContentStreamIds; } public function pruneAll(): void { - $contentStreamIds = $this->contentRepository->getContentStreamFinder()->findAllIds(); - - foreach ($contentStreamIds as $contentStreamId) { - $streamName = ContentStreamEventStreamName::fromContentStreamId($contentStreamId)->getEventStreamName(); + foreach ($this->contentRepository->getContentStreams() as $contentStream) { + $streamName = ContentStreamEventStreamName::fromContentStreamId($contentStream->id)->getEventStreamName(); $this->eventStore->deleteStream($streamName); } } - - public function getLastCommandResult(): ?CommandResult - { - return $this->lastCommandResult; - } } diff --git a/Neos.ContentRepository.Core/Classes/Service/ContentStreamPrunerFactory.php b/Neos.ContentRepository.Core/Classes/Service/ContentStreamPrunerFactory.php index 42c4b2c48a7..4f72263e21f 100644 --- a/Neos.ContentRepository.Core/Classes/Service/ContentStreamPrunerFactory.php +++ b/Neos.ContentRepository.Core/Classes/Service/ContentStreamPrunerFactory.php @@ -4,6 +4,7 @@ namespace Neos\ContentRepository\Core\Service; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryDependencies; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryInterface; @@ -15,9 +16,11 @@ class ContentStreamPrunerFactory implements ContentRepositoryServiceFactoryInter { public function build(ContentRepositoryServiceFactoryDependencies $serviceFactoryDependencies): ContentStreamPruner { + $contentGraphAdapter = $serviceFactoryDependencies->contentRepository->projectionState(ContentGraphAdapter::class); return new ContentStreamPruner( $serviceFactoryDependencies->contentRepository, - $serviceFactoryDependencies->eventStore + $serviceFactoryDependencies->eventStore, + $contentGraphAdapter, ); } } diff --git a/Neos.ContentRepository.Core/Classes/Service/WorkspaceMaintenanceService.php b/Neos.ContentRepository.Core/Classes/Service/WorkspaceMaintenanceService.php index 521af3a1a40..3dd792237aa 100644 --- a/Neos.ContentRepository.Core/Classes/Service/WorkspaceMaintenanceService.php +++ b/Neos.ContentRepository.Core/Classes/Service/WorkspaceMaintenanceService.php @@ -9,9 +9,9 @@ use Neos\ContentRepository\Core\Feature\WorkspaceEventStreamName; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Command\RebaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; -use Neos\ContentRepository\Core\Projection\Workspace\Workspaces; -use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceStatus; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceStatus; use Neos\EventStore\EventStoreInterface; /** diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/ContentStream.php b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/ContentStream.php new file mode 100644 index 00000000000..1623280473a --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/ContentStream.php @@ -0,0 +1,36 @@ + + * + * @api + */ + +final class ContentStreams implements \IteratorAggregate, \Countable +{ + /** + * @var array + */ + private array $contentStreams; + + /** + * @param iterable $collection + */ + private function __construct(iterable $collection) + { + $contentStreams = []; + foreach ($collection as $item) { + if (!$item instanceof ContentStream) { + throw new \InvalidArgumentException(sprintf('ContentStreams must only consist of %s objects, got: %s', ContentStream::class, get_debug_type($item)), 1716900709); + } + $contentStreams[$item->id->value] = $item; + } + + $this->contentStreams = $contentStreams; + } + + /** + * @param array $contentStreams + */ + public static function fromArray(array $contentStreams): self + { + return new self($contentStreams); + } + + public static function createEmpty(): self + { + return new self([]); + } + + /** + * @return \Traversable + */ + public function getIterator(): \Traversable + { + yield from array_values($this->contentStreams); + } + + /** + * @param \Closure(ContentStream): bool $callback + */ + public function filter(\Closure $callback): self + { + return new self(array_filter($this->contentStreams, $callback)); + } + + /** + * @param \Closure(ContentStream): bool $callback + */ + public function find(\Closure $callback): ?ContentStream + { + foreach ($this->contentStreams as $contentStream) { + if ($callback($contentStream)) { + return $contentStream; + } + } + return null; + } + + public function count(): int + { + return count($this->contentStreams); + } + + public function isEmpty(): bool + { + return $this->contentStreams === []; + } +} diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspace.php b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspace.php new file mode 100644 index 00000000000..79240373951 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspace.php @@ -0,0 +1,63 @@ +workspaceName = $workspaceName; + $this->baseWorkspaceName = $baseWorkspaceName; + $this->currentContentStreamId = $currentContentStreamId; + $this->status = $status; + } + + public function isBaseWorkspace(): bool + { + return $this->baseWorkspaceName === null; + } +} diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceDescription.php b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceDescription.php deleted file mode 100644 index 3181b351960..00000000000 --- a/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceDescription.php +++ /dev/null @@ -1,52 +0,0 @@ -value) !== 1) { - throw new \InvalidArgumentException('Invalid workspace description given.', 1505831660363); - } - } - - public static function fromString(string $value): self - { - return new self($value); - } - - public function jsonSerialize(): string - { - return $this->value; - } - - public function equals(self $other): bool - { - return $this->value === $other->value; - } - - public function __toString(): string - { - return $this->value; - } -} diff --git a/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceFilter.php b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceFilter.php new file mode 100644 index 00000000000..70e6a1e55b6 --- /dev/null +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/WorkspaceFilter.php @@ -0,0 +1,37 @@ +value) !== 1) { - throw new \InvalidArgumentException('Invalid workspace title given.', 1505827170288); - } - } - - public static function fromString(string $value): self - { - return new self($value); - } - - public function jsonSerialize(): string - { - return $this->value; - } - - public function equals(self $other): bool - { - return $this->value === $other->value; - } - - public function __toString(): string - { - return $this->value; - } -} diff --git a/Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspaces.php b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspaces.php similarity index 78% rename from Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspaces.php rename to Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspaces.php index 82a9399355c..671f8ab1ed4 100644 --- a/Neos.ContentRepository.Core/Classes/Projection/Workspace/Workspaces.php +++ b/Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspaces.php @@ -12,14 +12,12 @@ declare(strict_types=1); -namespace Neos\ContentRepository\Core\Projection\Workspace; - -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +namespace Neos\ContentRepository\Core\SharedModel\Workspace; /** * An immutable, type-safe collection of Workspace objects * - * @implements \IteratorAggregate + * @implements \IteratorAggregate * * @api */ @@ -32,17 +30,14 @@ final class Workspaces implements \IteratorAggregate, \Countable private array $workspaces; /** - * @param iterable $collection + * @param iterable $collection */ private function __construct(iterable $collection) { $workspaces = []; foreach ($collection as $item) { if (!$item instanceof Workspace) { - throw new \InvalidArgumentException( - 'Workspaces can only consist of ' . Workspace::class . ' objects.', - 1677833509 - ); + throw new \InvalidArgumentException(sprintf('Workspaces must only consist of %s objects, got: %s', Workspace::class, get_debug_type($item)), 1677833509); } $workspaces[$item->workspaceName->value] = $item; } @@ -51,7 +46,7 @@ private function __construct(iterable $collection) } /** - * @param array $workspaces + * @param array $workspaces */ public static function fromArray(array $workspaces): self { @@ -77,10 +72,10 @@ public function getBaseWorkspaces(WorkspaceName $workspaceName): Workspaces $workspace = $this->get($workspaceName); if (!$workspace) { - return Workspaces::createEmpty(); + return self::createEmpty(); } $baseWorkspaceName = $workspace->baseWorkspaceName; - while ($baseWorkspaceName != null) { + while ($baseWorkspaceName !== null) { $baseWorkspace = $this->get($baseWorkspaceName); if ($baseWorkspace) { $baseWorkspaces[] = $baseWorkspace; @@ -89,11 +84,11 @@ public function getBaseWorkspaces(WorkspaceName $workspaceName): Workspaces $baseWorkspaceName = null; } } - return Workspaces::fromArray($baseWorkspaces); + return self::fromArray($baseWorkspaces); } /** - * @return \Traversable + * @return \Traversable */ public function getIterator(): \Traversable { @@ -105,7 +100,7 @@ public function getIterator(): \Traversable */ public function filter(\Closure $callback): self { - return new self(array_Filter($this->workspaces, $callback)); + return new self(array_filter($this->workspaces, $callback)); } /** diff --git a/Neos.ContentRepository.Export/src/ExportServiceFactory.php b/Neos.ContentRepository.Export/src/ExportServiceFactory.php index 4e2dadd59a5..798c8752430 100644 --- a/Neos.ContentRepository.Export/src/ExportServiceFactory.php +++ b/Neos.ContentRepository.Export/src/ExportServiceFactory.php @@ -6,7 +6,7 @@ use League\Flysystem\Filesystem; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryDependencies; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceFactoryInterface; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; use Neos\Media\Domain\Repository\AssetRepository; use Neos\Neos\AssetUsage\Projection\AssetUsageFinder; diff --git a/Neos.ContentRepository.Export/src/Processors/EventStoreImportProcessor.php b/Neos.ContentRepository.Export/src/Processors/EventStoreImportProcessor.php index df2101f2dc5..50f4474f485 100644 --- a/Neos.ContentRepository.Export/src/Processors/EventStoreImportProcessor.php +++ b/Neos.ContentRepository.Export/src/Processors/EventStoreImportProcessor.php @@ -5,9 +5,7 @@ use League\Flysystem\Filesystem; use Neos\ContentRepository\Core\EventStore\DecoratedEvent; -use Neos\ContentRepository\Core\EventStore\EventInterface; use Neos\ContentRepository\Core\EventStore\EventNormalizer; -use Neos\ContentRepository\Core\EventStore\EventPersister; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\Feature\ContentStreamCreation\Event\ContentStreamWasCreated; use Neos\ContentRepository\Core\Feature\ContentStreamEventStreamName; @@ -16,9 +14,7 @@ use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Event\RootWorkspaceWasCreated; use Neos\ContentRepository\Core\Feature\WorkspaceEventStreamName; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepository\Export\Event\ValueObject\ExportedEvent; use Neos\ContentRepository\Export\ProcessorInterface; use Neos\ContentRepository\Export\ProcessorResult; @@ -138,8 +134,6 @@ public function run(): ProcessorResult $this->eventNormalizer->normalize( new RootWorkspaceWasCreated( $workspaceName, - WorkspaceTitle::fromString('live workspace'), - WorkspaceDescription::fromString('live workspace'), $this->contentStreamId ) ) diff --git a/Neos.ContentRepository.LegacyNodeMigration/Tests/Behavior/Bootstrap/FeatureContext.php b/Neos.ContentRepository.LegacyNodeMigration/Tests/Behavior/Bootstrap/FeatureContext.php index 0c9d8dd9770..3f575418453 100644 --- a/Neos.ContentRepository.LegacyNodeMigration/Tests/Behavior/Bootstrap/FeatureContext.php +++ b/Neos.ContentRepository.LegacyNodeMigration/Tests/Behavior/Bootstrap/FeatureContext.php @@ -123,11 +123,11 @@ public function iRunTheEventMigration(string $contentStream = null): void { $nodeTypeManager = $this->currentContentRepository->getNodeTypeManager(); $propertyMapper = $this->getObject(PropertyMapper::class); - $contentGraphFinder = $this->currentContentRepository->projectionState(\Neos\ContentRepository\Core\ContentGraphFinder::class); + $contentGraphAdapter = $this->currentContentRepository->projectionState(\Neos\ContentRepository\Core\ContentGraphAdapter::class); // FIXME: Dirty - $contentGraphFactory = (new \ReflectionClass($contentGraphFinder)) + $contentGraphFactory = (new \ReflectionClass($contentGraphAdapter)) ->getProperty('contentGraphFactory') - ->getValue($contentGraphFinder); + ->getValue($contentGraphAdapter); $nodeFactory = (new \ReflectionClass($contentGraphFactory)) ->getProperty('nodeFactory') ->getValue($contentGraphFactory); diff --git a/Neos.ContentRepository.NodeMigration/src/NodeMigrationService.php b/Neos.ContentRepository.NodeMigration/src/NodeMigrationService.php index 69ba9a5bf7c..5fb2eaa06ab 100644 --- a/Neos.ContentRepository.NodeMigration/src/NodeMigrationService.php +++ b/Neos.ContentRepository.NodeMigration/src/NodeMigrationService.php @@ -7,14 +7,11 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace; +use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; +use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishWorkspace; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; -use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\PublishWorkspace; -use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\NodeMigration\Command\ExecuteMigration; use Neos\ContentRepository\NodeMigration\Filter\FiltersFactory; use Neos\ContentRepository\NodeMigration\Filter\InvalidMigrationFilterSpecified; @@ -80,8 +77,6 @@ public function executeMigration(ExecuteMigration $command): void CreateWorkspace::create( $command->targetWorkspaceName, $sourceWorkspace->workspaceName, - WorkspaceTitle::fromString($command->targetWorkspaceName->value), - WorkspaceDescription::fromString(''), $command->contentStreamId, ) ); diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteRuntimeVariables.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteRuntimeVariables.php index a9520df0c93..4c6e2877555 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteRuntimeVariables.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteRuntimeVariables.php @@ -14,7 +14,7 @@ namespace Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; @@ -125,9 +125,9 @@ public function visibilityConstraintsAreSetTo(string $restrictionType): void public function getCurrentSubgraph(): ContentSubgraphInterface { - $contentGraphFinder = $this->currentContentRepository->projectionState(ContentGraphFinder::class); + $contentGraphAdapter = $this->currentContentRepository->projectionState(ContentGraphAdapter::class); - return $contentGraphFinder->getByWorkspaceName($this->currentWorkspaceName)->getSubgraph( + return $contentGraphAdapter->getContentGraphByWorkspaceName($this->currentWorkspaceName)->getSubgraph( $this->currentDimensionSpacePoint, $this->currentVisibilityConstraints ); diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteTrait.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteTrait.php index 06a5fd3fd13..ded9ac804fa 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteTrait.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/CRTestSuiteTrait.php @@ -25,11 +25,12 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\NodeType\NodeTypeCriteria; use Neos\ContentRepository\Core\Projection\ContentGraph\Subtree; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\Service\ContentStreamPruner; use Neos\ContentRepository\Core\Service\ContentStreamPrunerFactory; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamState; +use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamStatus; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap\Features\ContentStreamClosing; use Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap\Features\ContentStreamForking; @@ -133,7 +134,7 @@ protected function readPayloadTable(TableNode $payloadTable): array */ public function iExpectTheContentStreamToNotExist(string $rawContentStreamId): void { - // todo use, but also it should be assertFAlse?!!! + // todo use, but also it should be assertFAlse?!!! And what about theContentStreamDoesNotExist?? // $this->contentRepository->getContentStreams() // ->find(fn (ContentStream $contentStream) => $contentStream->id->equals(ContentStreamId::fromString($rawContentStreamId))), Assert::assertTrue( @@ -142,6 +143,15 @@ public function iExpectTheContentStreamToNotExist(string $rawContentStreamId): v ); } + /** + * @Then the content stream :contentStreamId does not exist + */ + public function theContentStreamDoesNotExist(string $contentStreamId): void + { + $contentStream = $this->currentContentRepository->findContentStreamById(ContentStreamId::fromString($contentStreamId)); + Assert::assertNull($contentStream, sprintf('Content stream "%s" was not expected to exist, but it does', $contentStreamId)); + } + /** * @Then /^workspace ([^"]*) has status ([^"]*)$/ */ @@ -230,27 +240,27 @@ protected function getRootNodeAggregateId(): ?NodeAggregateId } /** - * @Then the content stream :contentStreamId has state :expectedState + * @Then the content stream :contentStreamId has status :expectedState */ - public function theContentStreamHasState(string $contentStreamId, string $expectedState): void + public function theContentStreamHasStatus(string $contentStreamId, string $expectedStatus): void { - $contentStreamId = ContentStreamId::fromString($contentStreamId); - $contentStreamFinder = $this->currentContentRepository->getContentStreamFinder(); - - $actual = $contentStreamFinder->findStateForContentStream($contentStreamId); - Assert::assertSame(ContentStreamState::tryFrom($expectedState), $actual); + $contentStream = $this->currentContentRepository->findContentStreamById(ContentStreamId::fromString($contentStreamId)); + if ($contentStream === null) { + Assert::fail(sprintf('Expected content stream "%s" to have status "%s" but it does not exist', $contentStreamId, $expectedStatus)); + } + Assert::assertSame(ContentStreamStatus::tryFrom($expectedStatus), $contentStream->status); } /** - * @Then the current content stream has state :expectedState + * @Then the current content stream has status :expectedStatus */ - public function theCurrentContentStreamHasState(string $expectedState): void + public function theCurrentContentStreamHasStatus(string $expectedStatus): void { - $this->theContentStreamHasState( + $this->theContentStreamHasStatus( $this->currentContentRepository ->findWorkspaceByName($this->currentWorkspaceName) ->currentContentStreamId->value, - $expectedState + $expectedStatus ); } diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/WorkspaceCreation.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/WorkspaceCreation.php index bdfcd1dc5e4..b876e98a7b4 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/WorkspaceCreation.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/Features/WorkspaceCreation.php @@ -21,11 +21,8 @@ use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Command\CreateWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Command\RebaseWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; -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; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepository\TestSuite\Behavior\Features\Bootstrap\CRTestSuiteRuntimeVariables; use Neos\EventStore\Model\Event\StreamName; @@ -67,8 +64,6 @@ public function theCommandCreateRootWorkspaceIsExecutedWithPayload(TableNode $pa $command = CreateRootWorkspace::create( WorkspaceName::fromString($commandArguments['workspaceName']), - new WorkspaceTitle($commandArguments['workspaceTitle'] ?? ucfirst($commandArguments['workspaceName'])), - new WorkspaceDescription($commandArguments['workspaceDescription'] ?? ('The workspace "' . $commandArguments['workspaceName'] . '"')), ContentStreamId::fromString($commandArguments['newContentStreamId']) ); @@ -99,10 +94,7 @@ public function theCommandCreateWorkspaceIsExecutedWithPayload(TableNode $payloa $command = CreateWorkspace::create( WorkspaceName::fromString($commandArguments['workspaceName']), WorkspaceName::fromString($commandArguments['baseWorkspaceName']), - new WorkspaceTitle($commandArguments['workspaceTitle'] ?? ucfirst($commandArguments['workspaceName'])), - new WorkspaceDescription($commandArguments['workspaceDescription'] ?? 'The workspace "' . $commandArguments['workspaceName'] . '"'), ContentStreamId::fromString($commandArguments['newContentStreamId']), - isset($commandArguments['workspaceOwner']) ? UserId::fromString($commandArguments['workspaceOwner']) : null ); $this->currentContentRepository->handle($command); diff --git a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/ProjectedNodeTrait.php b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/ProjectedNodeTrait.php index 017327f22ed..fcc37c84219 100644 --- a/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/ProjectedNodeTrait.php +++ b/Neos.ContentRepository.TestSuite/Classes/Behavior/Features/Bootstrap/ProjectedNodeTrait.php @@ -16,7 +16,7 @@ use Behat\Gherkin\Node\TableNode; use GuzzleHttp\Psr7\Uri; -use Neos\ContentRepository\Core\ContentGraphFinder; +use Neos\ContentRepository\Core\ContentGraphAdapter; use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag; use Neos\ContentRepository\Core\NodeType\NodeTypeName; use Neos\ContentRepository\Core\Projection\ContentGraph\ContentSubgraphInterface; @@ -32,6 +32,8 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; use Neos\ContentRepository\Core\SharedModel\Node\PropertyName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceFilter; use Neos\ContentRepository\Core\Tests\Behavior\Fixtures\DayOfWeek; use Neos\ContentRepository\Core\Tests\Behavior\Fixtures\PostalAddress; use Neos\ContentRepository\Core\Tests\Behavior\Fixtures\PriceSpecification; @@ -80,12 +82,16 @@ public function iGetTheNodeAtPath(string $serializedNodePath): void public function iExpectANodeIdentifiedByXToExistInTheContentGraph(string $serializedNodeDiscriminator): void { $nodeDiscriminator = NodeDiscriminator::fromShorthand($serializedNodeDiscriminator); - // TODO Fix - $workspaceName = $this->currentContentRepository->getWorkspaceFinder()->findOneByCurrentContentStreamId( - $nodeDiscriminator->contentStreamId - )->workspaceName; - $contentGraph = $this->currentContentRepository->getContentGraph($workspaceName); - $currentNodeAggregate = $contentGraph->findNodeAggregateById( + $contentGraphAdapter = $this->currentContentRepository->projectionState(ContentGraphAdapter::class); + $contentGraphAdapter->forgetInstances(); + $matchingWorkspace = $this->currentContentRepository->getWorkspaces()->find( + static fn (Workspace $workspace) => $workspace->currentContentStreamId->equals($nodeDiscriminator->contentStreamId) + ); + if ($matchingWorkspace === null) { + Assert::fail(sprintf('Failed to find workspace for content stream id "%s"', $nodeDiscriminator->contentStreamId->value)); + } + $workspaceName = $matchingWorkspace->workspaceName; + $currentNodeAggregate = $this->currentContentRepository->getContentGraph($workspaceName)->findNodeAggregateById( $nodeDiscriminator->nodeAggregateId ); Assert::assertTrue( diff --git a/Neos.ContentRepositoryRegistry/Configuration/Objects.yaml b/Neos.ContentRepositoryRegistry/Configuration/Objects.yaml index 113f828b6d6..df582d41eba 100644 --- a/Neos.ContentRepositoryRegistry/Configuration/Objects.yaml +++ b/Neos.ContentRepositoryRegistry/Configuration/Objects.yaml @@ -4,23 +4,6 @@ Neos\ContentRepositoryRegistry\ContentRepositoryRegistry: setting: Neos.ContentRepositoryRegistry # !!! UGLY WORKAROUNDS, because we cannot wire non-Flow class constructor arguments here. -Neos\ContentRepository\Core\Projection\ContentStream\ContentStreamProjectionFactory: - scope: singleton - factoryObjectName: Neos\ContentRepositoryRegistry\Infrastructure\GenericObjectFactory - arguments: - 1: - value: Neos\ContentRepository\Core\Projection\ContentStream\ContentStreamProjectionFactory - 2: - object: 'Doctrine\DBAL\Connection' - -Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjectionFactory: - scope: singleton - factoryObjectName: Neos\ContentRepositoryRegistry\Infrastructure\GenericObjectFactory - arguments: - 1: - value: Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjectionFactory - 2: - object: 'Doctrine\DBAL\Connection' # This adds a soft-dependency to the neos/contentgraph-doctrinedbaladapter package Neos\ContentGraph\DoctrineDbalAdapter\DoctrineDbalContentGraphProjectionFactory: diff --git a/Neos.ContentRepositoryRegistry/Configuration/Settings.yaml b/Neos.ContentRepositoryRegistry/Configuration/Settings.yaml index 70b0fe0c5d8..44e28699641 100644 --- a/Neos.ContentRepositoryRegistry/Configuration/Settings.yaml +++ b/Neos.ContentRepositoryRegistry/Configuration/Settings.yaml @@ -69,10 +69,6 @@ Neos: className: Neos\ContentRepositoryRegistry\Infrastructure\Property\Normalizer\ProxyAwareObjectNormalizer projections: - 'Neos.ContentRepository:ContentStream': - factoryObjectName: Neos\ContentRepository\Core\Projection\ContentStream\ContentStreamProjectionFactory - 'Neos.ContentRepository:Workspace': - factoryObjectName: Neos\ContentRepository\Core\Projection\Workspace\WorkspaceProjectionFactory # NOTE: the following name must be stable, because we use it f.e. in Neos UI to register catchUpHooks for content cache flushing 'Neos.ContentRepository:ContentGraph': # NOTE: This introduces a soft-dependency to the neos/contentgraph-doctrinedbaladapter package, but it can be overridden when a different adapter is used diff --git a/Neos.Media.Browser/Classes/Controller/UsageController.php b/Neos.Media.Browser/Classes/Controller/UsageController.php index c0fe9f11ae3..ef9cc14553f 100644 --- a/Neos.Media.Browser/Classes/Controller/UsageController.php +++ b/Neos.Media.Browser/Classes/Controller/UsageController.php @@ -14,7 +14,7 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindClosestNodeFilter; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFound; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; diff --git a/Neos.Media.Browser/Resources/Private/Templates/Usage/RelatedNodes.html b/Neos.Media.Browser/Resources/Private/Templates/Usage/RelatedNodes.html index 921537fb5c8..50396e7223a 100644 --- a/Neos.Media.Browser/Resources/Private/Templates/Usage/RelatedNodes.html +++ b/Neos.Media.Browser/Resources/Private/Templates/Usage/RelatedNodes.html @@ -142,7 +142,7 @@ - {nodeInformation.workspace.workspaceTitle.value -> f:format.crop(maxCharacters: 25, append: '…')} + {nodeInformation.workspace.name.value -> f:format.crop(maxCharacters: 25, append: '…')} diff --git a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageSyncService.php b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageSyncService.php index cb4c17337ef..34abd7997a8 100644 --- a/Neos.Neos/Classes/AssetUsage/Service/AssetUsageSyncService.php +++ b/Neos.Neos/Classes/AssetUsage/Service/AssetUsageSyncService.php @@ -7,7 +7,7 @@ use Neos\ContentRepository\Core\ContentRepository; use Neos\ContentRepository\Core\Factory\ContentRepositoryServiceInterface; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Media\Domain\Model\AssetInterface; use Neos\Media\Domain\Repository\AssetRepository; use Neos\Neos\AssetUsage\Dto\AssetUsage; diff --git a/Neos.Neos/Classes/Command/WorkspaceCommandController.php b/Neos.Neos/Classes/Command/WorkspaceCommandController.php index eab83907d49..426bc07d670 100644 --- a/Neos.Neos/Classes/Command/WorkspaceCommandController.php +++ b/Neos.Neos/Classes/Command/WorkspaceCommandController.php @@ -20,16 +20,12 @@ use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy; use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; -use Neos\ContentRepository\Core\Projection\Workspace\Workspaces; -use Neos\ContentRepository\Core\Projection\Workspace\WorkspaceStatus; use Neos\ContentRepository\Core\Service\WorkspaceMaintenanceServiceFactory; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceDoesNotExist; use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceDescription; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; -use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceTitle; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Cli\CommandController; @@ -203,10 +199,7 @@ public function createCommand( $contentRepositoryInstance->handle(CreateWorkspace::create( WorkspaceName::fromString($workspace), WorkspaceName::fromString($baseWorkspace), - WorkspaceTitle::fromString($title ?: $workspace), - WorkspaceDescription::fromString($description ?: $workspace), ContentStreamId::create(), - $workspaceOwnerUserId !== null ? \Neos\ContentRepository\Core\SharedModel\User\UserId::fromString($workspaceOwnerUserId->value) : null, )); } catch (WorkspaceAlreadyExists $workspaceAlreadyExists) { $this->outputLine('Workspace "%s" already exists', [$workspace]); diff --git a/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php b/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php index 5afd08ec56e..7dabc86f97d 100755 --- a/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php +++ b/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php @@ -16,7 +16,7 @@ use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Exception\NodeNameIsAlreadyCovered; use Neos\ContentRepository\Core\SharedModel\Exception\NodeTypeNotFound; diff --git a/Neos.Neos/Classes/Domain/Service/UserService.php b/Neos.Neos/Classes/Domain/Service/UserService.php index 8ad74db35ec..ea3f627c86d 100644 --- a/Neos.Neos/Classes/Domain/Service/UserService.php +++ b/Neos.Neos/Classes/Domain/Service/UserService.php @@ -14,7 +14,7 @@ namespace Neos\Neos\Domain\Service; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\Flow\Annotations as Flow; use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Flow\Persistence\PersistenceManagerInterface; diff --git a/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php b/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php index b5169ae51c1..bb0087d66e3 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeAddressFactory.php @@ -18,6 +18,7 @@ use Neos\Neos\FrontendRouting\NodeAddress as LegacyNodeAddress; use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeAddress; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; diff --git a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php index c1a9f11c6ec..b5d720bc013 100644 --- a/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/CachingHelper.php @@ -17,7 +17,7 @@ use Neos\ContentRepository\Core\NodeType\NodeTypeNames; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\Nodes; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Eel\ProtectedContextAwareInterface; diff --git a/Neos.Neos/Classes/Routing/Cache/RouteCacheFlusher.php b/Neos.Neos/Classes/Routing/Cache/RouteCacheFlusher.php index aa6e4e5b20e..fe347de81b9 100644 --- a/Neos.Neos/Classes/Routing/Cache/RouteCacheFlusher.php +++ b/Neos.Neos/Classes/Routing/Cache/RouteCacheFlusher.php @@ -15,7 +15,7 @@ namespace Neos\Neos\Routing\Cache; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; -use Neos\ContentRepository\Core\Projection\Workspace\Workspace; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Mvc\Routing\RouterCachingService; diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index f112a02e641..dbfa0c9ad49 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -312,7 +312,7 @@ public function createNodeUri( $workspace = $contentRepository->findWorkspaceByName($nodeAddress->workspaceName); $subgraph = $contentRepository->getContentGraph($nodeAddress->workspaceName)->getSubgraph( $nodeAddress->dimensionSpacePoint, - $workspace && !$workspace->isPublicWorkspace() + $workspace && !$workspace->workspaceName->isLive() ? VisibilityConstraints::withoutRestrictions() : VisibilityConstraints::frontend() ); @@ -354,7 +354,7 @@ public function createNodeUri( $mainRequest = $controllerContext->getRequest()->getMainRequest(); $uriBuilder = clone $controllerContext->getUriBuilder(); $uriBuilder->setRequest($mainRequest); - $createLiveUri = $workspace && $workspace->isPublicWorkspace() && $node->tags->contain(SubtreeTag::disabled()); + $createLiveUri = $workspace && $workspace->workspaceName->isLive() && $node->tags->contain(SubtreeTag::disabled()); if ($addQueryString === true) { // legacy feature see https://github.com/neos/neos-development-collection/issues/5076 diff --git a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php index 47a40a73100..92561a17464 100644 --- a/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php +++ b/Neos.TimeableNodeVisibility/Classes/Service/TimeableNodeVisibilityService.php @@ -16,6 +16,7 @@ use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\PropertyValue\Criteria\PropertyValueLessThanOrEqual; use Neos\ContentRepository\Core\Projection\ContentGraph\Node; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Node\PropertyName;