Skip to content

Commit adb6713

Browse files
authored
Merge pull request #5334 from mhsdesign/bugfix/workspace-service-adjustments
BUGFIX: `WorkspaceService` adjust to real world use cases
2 parents 84c0e70 + 08d01e1 commit adb6713

File tree

24 files changed

+690
-162
lines changed

24 files changed

+690
-162
lines changed

Neos.ContentRepository.Core/Classes/SharedModel/Exception/WorkspaceDoesNotExist.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,28 @@
1414

1515
namespace Neos\ContentRepository\Core\SharedModel\Exception;
1616

17+
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
1718
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
1819

1920
/**
20-
* @api because exception is thrown during invariant checks on command execution
21+
* @api because exception is thrown during invariant checks on command execution or when attempting to query a non-existing workspace
2122
*/
2223
final class WorkspaceDoesNotExist extends \DomainException
2324
{
2425
public static function butWasSupposedTo(WorkspaceName $name): self
2526
{
2627
return new self(sprintf(
27-
'The source workspace %s does not exist',
28+
'The workspace "%s" does not exist',
2829
$name->value
2930
), 1513924741);
3031
}
32+
33+
public static function butWasSupposedToInContentRepository(WorkspaceName $name, ContentRepositoryId $contentRepositoryId): self
34+
{
35+
return new self(sprintf(
36+
'The workspace "%s" does not exist in content repository "%s"',
37+
$name->value,
38+
$contentRepositoryId->value
39+
), 1733737361);
40+
}
3141
}

Neos.ContentRepositoryRegistry/Classes/Service/EventMigrationService.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ public function migrateWorkspaceMetadataToWorkspaceService(\Closure $outputFn):
716716
];
717717
$roleAssignments[] = [
718718
'subject_type' => WorkspaceRoleSubjectType::GROUP->value,
719-
'subject' => 'Neos.Neos:Everybody',
719+
'subject' => 'Neos.Flow:Everybody',
720720
'role' => WorkspaceRole::VIEWER->value,
721721
];
722722
} elseif ($isInternalWorkspace) {

Neos.Neos/Classes/Command/WorkspaceCommandController.php

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
namespace Neos\Neos\Command;
1616

1717
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists;
18-
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace;
1918
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Dto\RebaseErrorHandlingStrategy;
2019
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed;
2120
use Neos\ContentRepository\Core\Service\WorkspaceMaintenanceServiceFactory;
@@ -31,6 +30,7 @@
3130
use Neos\Neos\Domain\Model\WorkspaceDescription;
3231
use Neos\Neos\Domain\Model\WorkspaceRole;
3332
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
33+
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
3434
use Neos\Neos\Domain\Model\WorkspaceRoleSubject;
3535
use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType;
3636
use Neos\Neos\Domain\Model\WorkspaceTitle;
@@ -148,7 +148,8 @@ public function createRootCommand(string $name, string $contentRepository = 'def
148148
$contentRepositoryId,
149149
$workspaceName,
150150
WorkspaceTitle::fromString($title ?? $name),
151-
WorkspaceDescription::fromString($description ?? '')
151+
WorkspaceDescription::fromString($description ?? ''),
152+
WorkspaceRoleAssignments::createEmpty()
152153
);
153154
$this->outputLine('<success>Created root workspace "%s" in content repository "%s"</success>', [$workspaceName->value, $contentRepositoryId->value]);
154155
}
@@ -206,6 +207,7 @@ public function createSharedCommand(string $workspace, string $baseWorkspace = '
206207
WorkspaceTitle::fromString($title ?? $workspaceName->value),
207208
WorkspaceDescription::fromString($description ?? ''),
208209
WorkspaceName::fromString($baseWorkspace),
210+
WorkspaceRoleAssignments::createEmpty()
209211
);
210212
$this->outputLine('<success>Created shared workspace "%s"</success>', [$workspaceName->value]);
211213
}
@@ -400,11 +402,7 @@ public function deleteCommand(string $workspace, bool $force = false, string $co
400402
$this->workspacePublishingService->discardAllWorkspaceChanges($contentRepositoryId, $workspaceName);
401403
}
402404

403-
$contentRepositoryInstance->handle(
404-
DeleteWorkspace::create(
405-
$workspaceName
406-
)
407-
);
405+
$this->workspaceService->deleteWorkspace($contentRepositoryId, $workspaceName);
408406
$this->outputLine('Deleted workspace "%s"', [$workspaceName->value]);
409407
}
410408

Neos.Neos/Classes/Domain/Import/LiveWorkspaceCreationProcessor.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
use Neos\ContentRepository\Export\ProcessorInterface;
2121
use Neos\ContentRepository\Export\Severity;
2222
use Neos\Neos\Domain\Model\WorkspaceDescription;
23-
use Neos\Neos\Domain\Model\WorkspaceRole;
24-
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
23+
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
2524
use Neos\Neos\Domain\Model\WorkspaceTitle;
2625
use Neos\Neos\Domain\Service\WorkspaceService;
2726

@@ -44,7 +43,12 @@ public function run(ProcessingContext $context): void
4443
$context->dispatch(Severity::NOTICE, 'Workspace already exists, skipping');
4544
return;
4645
}
47-
$this->workspaceService->createRootWorkspace($this->contentRepository->id, WorkspaceName::forLive(), WorkspaceTitle::fromString('Live workspace'), WorkspaceDescription::fromString(''));
48-
$this->workspaceService->assignWorkspaceRole($this->contentRepository->id, WorkspaceName::forLive(), WorkspaceRoleAssignment::createForGroup('Neos.Neos:LivePublisher', WorkspaceRole::COLLABORATOR));
46+
$this->workspaceService->createRootWorkspace(
47+
$this->contentRepository->id,
48+
WorkspaceName::forLive(),
49+
WorkspaceTitle::fromString('Public live workspace'),
50+
WorkspaceDescription::empty(),
51+
WorkspaceRoleAssignments::createForLiveWorkspace()
52+
);
4953
}
5054
}

Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignment.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,10 @@ public static function createForGroup(string $flowRoleIdentifier, WorkspaceRole
4242
$role
4343
);
4444
}
45+
46+
public function equals(WorkspaceRoleAssignment $other): bool
47+
{
48+
return $this->subject->equals($other->subject)
49+
&& $this->role === $other->role;
50+
}
4551
}

Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
namespace Neos\Neos\Domain\Model;
66

77
use Neos\Flow\Annotations as Flow;
8+
use Neos\Neos\Domain\Service\WorkspaceService;
89

910
/**
1011
* A set of {@see WorkspaceRoleAssignment} instances
@@ -25,6 +26,16 @@ private function __construct(WorkspaceRoleAssignment ...$assignments)
2526
$this->assignments = $assignments;
2627
}
2728

29+
public static function createEmpty(): self
30+
{
31+
return new self();
32+
}
33+
34+
public static function create(WorkspaceRoleAssignment ...$assignments): self
35+
{
36+
return new self(...$assignments);
37+
}
38+
2839
/**
2940
* @param array<WorkspaceRoleAssignment> $assignments
3041
*/
@@ -33,6 +44,44 @@ public static function fromArray(array $assignments): self
3344
return new self(...$assignments);
3445
}
3546

47+
/**
48+
* Default role assignment to be specified at creation via {@see WorkspaceService::createRootWorkspace()}
49+
*
50+
* Users with the role "Neos.Neos:LivePublisher" are collaborators and everybody can read.
51+
*/
52+
public static function createForLiveWorkspace(): self
53+
{
54+
return new self(
55+
WorkspaceRoleAssignment::createForGroup(
56+
'Neos.Neos:LivePublisher',
57+
WorkspaceRole::COLLABORATOR
58+
),
59+
WorkspaceRoleAssignment::createForGroup(
60+
'Neos.Flow:Everybody',
61+
WorkspaceRole::VIEWER
62+
)
63+
);
64+
}
65+
66+
/**
67+
* Default role assignment to be specified at creation via {@see WorkspaceService::createSharedWorkspace()}
68+
*
69+
* Users with the role "Neos.Neos:AbstractEditor" are collaborators and the specified user is manager
70+
*/
71+
public static function createForSharedWorkspace(UserId $userId): self
72+
{
73+
return new self(
74+
WorkspaceRoleAssignment::createForUser(
75+
$userId,
76+
WorkspaceRole::MANAGER,
77+
),
78+
WorkspaceRoleAssignment::createForGroup(
79+
'Neos.Neos:AbstractEditor',
80+
WorkspaceRole::COLLABORATOR,
81+
)
82+
);
83+
}
84+
3685
public function isEmpty(): bool
3786
{
3887
return $this->assignments === [];
@@ -47,4 +96,19 @@ public function count(): int
4796
{
4897
return count($this->assignments);
4998
}
99+
100+
public function contains(WorkspaceRoleAssignment $assignment): bool
101+
{
102+
foreach ($this->assignments as $existingAssignment) {
103+
if ($existingAssignment->equals($assignment)) {
104+
return true;
105+
}
106+
}
107+
return false;
108+
}
109+
110+
public function withAssignment(WorkspaceRoleAssignment $assignment): self
111+
{
112+
return new self(...[...$this->assignments, $assignment]);
113+
}
50114
}

Neos.Neos/Classes/Domain/Repository/WorkspaceMetadataAndRoleRepository.php

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,40 @@ public function getMostPrivilegedWorkspaceRoleForSubjects(ContentRepositoryId $c
178178
return WorkspaceRole::from($role);
179179
}
180180

181+
public function deleteWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void
182+
{
183+
try {
184+
$this->dbal->delete(self::TABLE_NAME_WORKSPACE_METADATA, [
185+
'content_repository_id' => $contentRepositoryId->value,
186+
'workspace_name' => $workspaceName->value,
187+
]);
188+
} catch (DbalException $e) {
189+
throw new \RuntimeException(sprintf(
190+
'Failed to delete metadata for workspace "%s" (Content Repository "%s"): %s',
191+
$workspaceName->value,
192+
$contentRepositoryId->value,
193+
$e->getMessage()
194+
), 1726821159, $e);
195+
}
196+
}
197+
198+
public function deleteWorkspaceRoleAssignments(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void
199+
{
200+
try {
201+
$this->dbal->delete(self::TABLE_NAME_WORKSPACE_ROLE, [
202+
'content_repository_id' => $contentRepositoryId->value,
203+
'workspace_name' => $workspaceName->value,
204+
]);
205+
} catch (DbalException $e) {
206+
throw new \RuntimeException(sprintf(
207+
'Failed to delete role assignments for workspace "%s" (Content Repository "%s"): %s',
208+
$workspaceName->value,
209+
$contentRepositoryId->value,
210+
$e->getMessage()
211+
), 1726821159, $e);
212+
}
213+
}
214+
181215
/**
182216
* Removes all workspace metadata records for the specified content repository id
183217
*/
@@ -306,7 +340,7 @@ public function addWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, W
306340
}
307341
}
308342

309-
public function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepositoryId, UserId $userId): ?WorkspaceName
343+
public function findWorkspaceNameByUser(ContentRepositoryId $contentRepositoryId, UserId $userId): ?WorkspaceName
310344
{
311345
$tableMetadata = self::TABLE_NAME_WORKSPACE_METADATA;
312346
$query = <<<SQL
@@ -326,4 +360,13 @@ public function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepo
326360
]);
327361
return $workspaceName === false ? null : WorkspaceName::fromString($workspaceName);
328362
}
363+
364+
/**
365+
* @param \Closure(): void $fn
366+
* @return void
367+
*/
368+
public function transactional(\Closure $fn): void
369+
{
370+
$this->dbal->transactional($fn);
371+
}
329372
}

Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
use Neos\Neos\Domain\Exception\SiteNodeTypeIsInvalid;
3434
use Neos\Neos\Domain\Model\Site;
3535
use Neos\Neos\Domain\Model\SiteNodeName;
36+
use Neos\Neos\Domain\Model\WorkspaceDescription;
37+
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
38+
use Neos\Neos\Domain\Model\WorkspaceTitle;
3639

3740
/**
3841
* @internal FIXME refactor and incorporate into SiteService
@@ -89,7 +92,16 @@ public function removeSiteNode(SiteNodeName $siteNodeName): void
8992

9093
public function createSiteNodeIfNotExists(Site $site, string $nodeTypeName): void
9194
{
92-
$this->workspaceService->createLiveWorkspaceIfMissing($this->contentRepository->id);
95+
$liveWorkspace = $this->contentRepository->findWorkspaceByName(WorkspaceName::forLive());
96+
if ($liveWorkspace === null) {
97+
$this->workspaceService->createRootWorkspace(
98+
$this->contentRepository->id,
99+
WorkspaceName::forLive(),
100+
WorkspaceTitle::fromString('Public live workspace'),
101+
WorkspaceDescription::empty(),
102+
WorkspaceRoleAssignments::createForLiveWorkspace()
103+
);
104+
}
93105

94106
$sitesNodeIdentifier = $this->getOrCreateRootNodeAggregate();
95107
$siteNodeType = $this->nodeTypeManager->getNodeType($nodeTypeName);

0 commit comments

Comments
 (0)