Skip to content

Commit b4c70c9

Browse files
committed
TASK: Make existing code aware of possibly deactivated workspaces
1 parent 8d464c8 commit b4c70c9

File tree

7 files changed

+98
-25
lines changed

7 files changed

+98
-25
lines changed

Neos.ContentRepository.Core/Classes/Feature/Common/WorkspaceConstraintChecks.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceHasNoBaseWorkspaceName;
1717
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceHasWorkspacesDependingOnIt;
1818
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceIsDeactivated;
19+
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
1920
use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace;
2021
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
2122
use Neos\ContentRepository\Core\SharedModel\Workspace\Workspaces;
@@ -25,13 +26,25 @@ trait WorkspaceConstraintChecks
2526
{
2627
/**
2728
* @throws WorkspaceDoesNotExist
29+
* @phpstan-return Workspace
2830
*/
29-
private function requireActiveWorkspace(WorkspaceName $workspaceName, CommandHandlingDependencies $commandHandlingDependencies): Workspace
31+
private function requireWorkspace(WorkspaceName $workspaceName, CommandHandlingDependencies $commandHandlingDependencies): Workspace
3032
{
3133
$workspace = $commandHandlingDependencies->findWorkspaceByName($workspaceName);
3234
if (is_null($workspace)) {
3335
throw WorkspaceDoesNotExist::butWasSupposedTo($workspaceName);
3436
}
37+
38+
return $workspace;
39+
}
40+
41+
/**
42+
* @throws WorkspaceDoesNotExist|WorkspaceIsDeactivated
43+
* @phpstan-return object{ currentContentStreamId: ContentStreamId, status: WorkspaceStatus::UP_TO_DATE|WorkspaceStatus::OUTDATED } & Workspace
44+
*/
45+
private function requireActiveWorkspace(WorkspaceName $workspaceName, CommandHandlingDependencies $commandHandlingDependencies): Workspace
46+
{
47+
$workspace = $this->requireWorkspace($workspaceName, $commandHandlingDependencies);
3548
if (!$workspace->isActive()) {
3649
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($workspaceName);
3750
}
@@ -42,6 +55,7 @@ private function requireActiveWorkspace(WorkspaceName $workspaceName, CommandHan
4255
/**
4356
* @throws WorkspaceHasNoBaseWorkspaceName
4457
* @throws BaseWorkspaceDoesNotExist
58+
* @phpstan-return object{ currentContentStreamId: ContentStreamId, status: WorkspaceStatus::UP_TO_DATE|WorkspaceStatus::OUTDATED } & Workspace
4559
*/
4660
private function requireBaseWorkspace(Workspace $workspace, CommandHandlingDependencies $commandHandlingDependencies): Workspace
4761
{
@@ -51,6 +65,10 @@ private function requireBaseWorkspace(Workspace $workspace, CommandHandlingDepen
5165
$baseWorkspace = $commandHandlingDependencies->findWorkspaceByName($workspace->baseWorkspaceName);
5266
if (is_null($baseWorkspace)) {
5367
throw BaseWorkspaceDoesNotExist::butWasSupposedTo($workspace->workspaceName);
68+
} elseif (!$baseWorkspace->isActive()) {
69+
// should never happen!
70+
// TODO: if this happens, something is seriously wrong in the database, handle differently?
71+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($workspace->baseWorkspaceName);
5472
}
5573
return $baseWorkspace;
5674
}

Neos.ContentRepository.Core/Classes/Feature/WorkspaceCommandHandler.php

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceHasNoBaseWorkspaceName;
6666
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceContainsPublishableChanges;
6767
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceIsActivated;
68+
use Neos\ContentRepository\Core\SharedModel\Exception\WorkspaceIsDeactivated;
6869
use Neos\ContentRepository\Core\SharedModel\Workspace\ContentStreamId;
6970
use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace;
7071
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
@@ -136,6 +137,9 @@ private function handleCreateWorkspace(
136137
$command->workspaceName->value
137138
), 1513890708);
138139
}
140+
if (!$baseWorkspace->isActive()) {
141+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($command->baseWorkspaceName);
142+
}
139143
$sourceContentStreamVersion = $commandHandlingDependencies->getContentStreamVersion($baseWorkspace->currentContentStreamId);
140144
$this->requireContentStreamToNotBeClosed($baseWorkspace->currentContentStreamId, $commandHandlingDependencies);
141145
$this->requireContentStreamToNotExistYet($command->newContentStreamId, $commandHandlingDependencies);
@@ -291,6 +295,13 @@ private function rebaseWorkspaceWithoutChanges(
291295
Version $baseWorkspaceContentStreamVersion,
292296
ContentStreamId $newContentStreamId
293297
): \Generator {
298+
if (!$baseWorkspace->isActive()) {
299+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($baseWorkspace->workspaceName);
300+
}
301+
if (!$workspace->isActive()) {
302+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($workspace->workspaceName);
303+
}
304+
294305
yield $this->forkContentStream(
295306
$newContentStreamId,
296307
$baseWorkspace->currentContentStreamId,
@@ -703,6 +714,13 @@ private function discardWorkspace(
703714
Version $baseWorkspaceContentStreamVersion,
704715
ContentStreamId $newContentStream
705716
): \Generator {
717+
if (!$baseWorkspace->isActive()) {
718+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($baseWorkspace->workspaceName);
719+
}
720+
if (!$workspace->isActive()) {
721+
throw WorkspaceIsDeactivated::butWasSupposedToBeActivated($workspace->workspaceName);
722+
}
723+
706724
yield $this->forkContentStream(
707725
$newContentStream,
708726
$baseWorkspace->currentContentStreamId,
@@ -785,18 +803,21 @@ private function handleDeleteWorkspace(
785803
DeleteWorkspace $command,
786804
CommandHandlingDependencies $commandHandlingDependencies,
787805
): \Generator {
788-
$workspace = $this->requireActiveWorkspace($command->workspaceName, $commandHandlingDependencies);
789-
$contentStreamVersion = $commandHandlingDependencies->getContentStreamVersion($workspace->currentContentStreamId);
806+
$workspace = $this->requireWorkspace($command->workspaceName, $commandHandlingDependencies);
790807

791-
yield new EventsToPublish(
792-
ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId)->getEventStreamName(),
793-
Events::with(
794-
new ContentStreamWasRemoved(
795-
$workspace->currentContentStreamId,
808+
if ($workspace->isActive()) {
809+
$contentStreamVersion = $commandHandlingDependencies->getContentStreamVersion($workspace->currentContentStreamId);
810+
811+
yield new EventsToPublish(
812+
ContentStreamEventStreamName::fromContentStreamId($workspace->currentContentStreamId)->getEventStreamName(),
813+
Events::with(
814+
new ContentStreamWasRemoved(
815+
$workspace->currentContentStreamId,
816+
),
796817
),
797-
),
798-
ExpectedVersion::fromVersion($contentStreamVersion)
799-
);
818+
ExpectedVersion::fromVersion($contentStreamVersion)
819+
);
820+
}
800821

801822
yield new EventsToPublish(
802823
WorkspaceEventStreamName::fromWorkspaceName($command->workspaceName)->getEventStreamName(),
@@ -932,6 +953,11 @@ private function requireWorkspaceToNotExist(WorkspaceName $workspaceName, Comman
932953
), 1715341085);
933954
}
934955

956+
/**
957+
* @param Workspace&object{ currentContentStreamId: ContentStreamId } $workspace
958+
* @param CommandHandlingDependencies $commandHandlingDependencies
959+
* @return Version
960+
*/
935961
private function requireOpenContentStreamAndVersion(Workspace $workspace, CommandHandlingDependencies $commandHandlingDependencies): Version
936962
{
937963
if ($commandHandlingDependencies->isContentStreamClosed($workspace->currentContentStreamId)) {

Neos.ContentRepository.Core/Classes/SharedModel/Workspace/Workspace.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,12 @@ private function __construct(
3838
if ($this->isRootWorkspace() && $this->hasPublishableChanges) {
3939
throw new \InvalidArgumentException('Root workspaces cannot have changes', 1730371566);
4040
}
41-
if ($this->isActive() && $this->currentContentStreamId === null) {
41+
if ($this->currentContentStreamId === null && $this->isActive()) {
4242
throw new \InvalidArgumentException('Active workspaces must have a non null content stream ID', 1730371566);
4343
}
44+
if ($this->isRootWorkspace() && !$this->isActive()) {
45+
throw new \InvalidArgumentException('Root Workspaces cannot be deactivated', 1768571925);
46+
}
4447
}
4548

4649
/**
@@ -66,6 +69,10 @@ public function hasPublishableChanges(): bool
6669

6770
/**
6871
* Indicates if the workspace is active.
72+
*
73+
* @phpstan-assert-if-true ContentStreamId $this->currentContentStreamId
74+
* @phpstan-assert-if-true WorkspaceStatus::UP_TO_DATE|WorkspaceStatus::OUTDATED $this->status
75+
* @phpstan-assert-if-false WorkspaceName $this->baseWorkspaceName
6976
*/
7077
public function isActive(): bool
7178
{

Neos.Neos/Classes/Command/WorkspaceCommandController.php

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ public function listCommand(string $contentRepository = 'default'): void
478478
$workspaceMetadata->title->value,
479479
$workspaceMetadata->description->value,
480480
$workspace->status->value,
481-
$workspace->currentContentStreamId->value,
481+
$workspace->currentContentStreamId?->value,
482482
];
483483
}
484484
$this->output->outputTable($tableRows, $headerRow);
@@ -515,7 +515,7 @@ public function showCommand(string $workspace, string $contentRepository = 'defa
515515
$this->outputFormatted('Title: <b>%s</b>', [$workspaceMetadata->title->value]);
516516
$this->outputFormatted('Description: <b>%s</b>', [$workspaceMetadata->description->value]);
517517
$this->outputFormatted('Status: <b>%s</b>', [$workspacesInstance->status->value]);
518-
$this->outputFormatted('Content Stream: <b>%s</b>', [$workspacesInstance->currentContentStreamId->value]);
518+
$this->outputFormatted('Content Stream: <b>%s</b>', [$workspacesInstance->currentContentStreamId?->value]);
519519

520520
$workspaceRoleAssignments = $this->workspaceService->getWorkspaceRoleAssignments($contentRepositoryId, $workspaceName);
521521
$this->outputLine();
@@ -582,15 +582,18 @@ public function deactivateCommand(string $workspace, string $contentRepository =
582582
*
583583
* @param string $contentRepository The name of the content repository. (Default: 'default')
584584
* @param string $dateInterval The time interval a user had to be inactive for its workspaces to be considered stale. (Default: '7 days')
585-
* @throws AccessDenied
586-
* @throws DateInvalidOperationException
585+
* @throws AccessDenied|DateInvalidOperationException
587586
*/
588587
public function deactivateStaleCommand(string $contentRepository = 'default', string $dateInterval = '7 days'): void
589588
{
590589
$contentRepositoryId = ContentRepositoryId::fromString($contentRepository);
591590
$contentRepositoryInstance = $this->contentRepositoryRegistry->get($contentRepositoryId);
592591

593592
$interval = DateInterval::createFromDateString($dateInterval);
593+
if ($interval === false) {
594+
$this->outputLine('Invalid date interval "%s".', [$dateInterval]);
595+
return;
596+
}
594597

595598
$workspaces = $contentRepositoryInstance->findWorkspaces();
596599
$baseWorkspaces = $this->splObjectStoreFromIterable($workspaces->map(fn($workspace) => $workspace->baseWorkspaceName));
@@ -638,11 +641,19 @@ private function buildWorkspaceRoleSubject(WorkspaceRoleSubjectType $subjectType
638641
return $roleSubject;
639642
}
640643

644+
/**
645+
* @template TObject of object
646+
* @param iterable<TObject|null> $iterable
647+
* @return \SplObjectStorage<TObject,mixed>
648+
*/
641649
private function splObjectStoreFromIterable(iterable $iterable): \SplObjectStorage
642650
{
651+
/** @var \SplObjectStorage<TObject,mixed> $result */
643652
$result = new \SplObjectStorage();
644-
foreach ($iterable as $workspace) {
645-
$result->attach($workspace);
653+
foreach ($iterable as $value) {
654+
if ($value !== null) {
655+
$result->attach($value);
656+
}
646657
}
647658
return $result;
648659
}

Neos.Neos/Classes/Domain/Service/SiteExportService.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ public function exportToPath(ContentRepositoryId $contentRepositoryId, string $p
5959
if ($liveWorkspace === null) {
6060
throw new \RuntimeException('Failed to find live workspace', 1716652280);
6161
}
62+
if (!$liveWorkspace->isActive()) {
63+
//TODO: should be impossible
64+
throw new \RuntimeException('Live workspace was deactivated', 1768577266);
65+
}
6266

6367
$processors = Processors::fromArray([
6468
'Exporting events' => $this->contentRepositoryRegistry->buildService(

Neos.Neos/Classes/Domain/Service/WorkspacePublishingService.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -371,13 +371,17 @@ private function resolveNodeIdsToPublishOrDiscard(
371371
private function pendingWorkspaceChangesInternal(ContentRepository $contentRepository, WorkspaceName $workspaceName): Changes
372372
{
373373
$crWorkspace = $this->requireContentRepositoryWorkspace($contentRepository, $workspaceName);
374-
return $contentRepository->projectionState(ChangeFinder::class)->findByContentStreamId($crWorkspace->currentContentStreamId);
374+
return $crWorkspace->isActive()
375+
? $contentRepository->projectionState(ChangeFinder::class)->findByContentStreamId($crWorkspace->currentContentStreamId)
376+
: Changes::fromArray([]);
375377
}
376378

377379
private function countPendingWorkspaceChangesInternal(ContentRepository $contentRepository, WorkspaceName $workspaceName): int
378380
{
379381
$crWorkspace = $this->requireContentRepositoryWorkspace($contentRepository, $workspaceName);
380-
return $contentRepository->projectionState(ChangeFinder::class)->countByContentStreamId($crWorkspace->currentContentStreamId);
382+
return $crWorkspace->isActive()
383+
? $contentRepository->projectionState(ChangeFinder::class)->countByContentStreamId($crWorkspace->currentContentStreamId)
384+
: 0;
381385
}
382386

383387
private function isChangePublishableWithinAncestorScope(

Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,9 @@ public function deleteAction(WorkspaceName $workspaceName): void
466466
$nodesCount = 0;
467467

468468
try {
469-
$nodesCount = $contentRepository->projectionState(ChangeFinder::class)
470-
->countByContentStreamId(
471-
$workspace->currentContentStreamId
472-
);
469+
$nodesCount = $workspace->currentContentStreamId === null ? 0 :
470+
$contentRepository->projectionState(ChangeFinder::class)
471+
->countByContentStreamId($workspace->currentContentStreamId);
473472
} catch (\Exception $exception) {
474473
$message = $this->getModuleLabel(
475474
'workspaces.notDeletedErrorWhileFetchingUnpublishedNodes',
@@ -996,7 +995,7 @@ protected function renderContentChanges(
996995
ContentRepository $contentRepository,
997996
): ContentChangeItems {
998997
$currentWorkspace = $contentRepository->findWorkspaces()->find(
999-
fn (Workspace $potentialWorkspace) => $potentialWorkspace->currentContentStreamId->equals($contentStreamIdOfOriginalNode)
998+
fn (Workspace $potentialWorkspace) => $potentialWorkspace->currentContentStreamId?->equals($contentStreamIdOfOriginalNode) ?? false
1000999
);
10011000
$originalNode = null;
10021001
if ($currentWorkspace !== null) {
@@ -1327,6 +1326,10 @@ protected function getWorkspaceListItems(
13271326
}
13281327

13291328
protected function getChangesFromWorkspace(Workspace $selectedWorkspace,ContentRepository $contentRepository ): Changes{
1329+
if (!$selectedWorkspace->isActive()) {
1330+
// since there is no content stream associated to them, inactive workspaces cannot contain changes
1331+
return Changes::fromArray([]);
1332+
}
13301333
return $contentRepository->projectionState(ChangeFinder::class)
13311334
->findByContentStreamId(
13321335
$selectedWorkspace->currentContentStreamId

0 commit comments

Comments
 (0)