Skip to content

Commit

Permalink
Merge pull request #5334 from mhsdesign/bugfix/workspace-service-adju…
Browse files Browse the repository at this point in the history
…stments

BUGFIX: `WorkspaceService` adjust to real world use cases
  • Loading branch information
mhsdesign authored Dec 12, 2024
2 parents 84c0e70 + 08d01e1 commit adb6713
Show file tree
Hide file tree
Showing 24 changed files with 690 additions and 162 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,28 @@

namespace Neos\ContentRepository\Core\SharedModel\Exception;

use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;

/**
* @api because exception is thrown during invariant checks on command execution
* @api because exception is thrown during invariant checks on command execution or when attempting to query a non-existing workspace
*/
final class WorkspaceDoesNotExist extends \DomainException
{
public static function butWasSupposedTo(WorkspaceName $name): self
{
return new self(sprintf(
'The source workspace %s does not exist',
'The workspace "%s" does not exist',
$name->value
), 1513924741);
}

public static function butWasSupposedToInContentRepository(WorkspaceName $name, ContentRepositoryId $contentRepositoryId): self
{
return new self(sprintf(
'The workspace "%s" does not exist in content repository "%s"',
$name->value,
$contentRepositoryId->value
), 1733737361);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ public function migrateWorkspaceMetadataToWorkspaceService(\Closure $outputFn):
];
$roleAssignments[] = [
'subject_type' => WorkspaceRoleSubjectType::GROUP->value,
'subject' => 'Neos.Neos:Everybody',
'subject' => 'Neos.Flow:Everybody',
'role' => WorkspaceRole::VIEWER->value,
];
} elseif ($isInternalWorkspace) {
Expand Down
12 changes: 5 additions & 7 deletions Neos.Neos/Classes/Command/WorkspaceCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
namespace Neos\Neos\Command;

use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists;
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\Service\WorkspaceMaintenanceServiceFactory;
Expand All @@ -31,6 +30,7 @@
use Neos\Neos\Domain\Model\WorkspaceDescription;
use Neos\Neos\Domain\Model\WorkspaceRole;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
use Neos\Neos\Domain\Model\WorkspaceRoleSubject;
use Neos\Neos\Domain\Model\WorkspaceRoleSubjectType;
use Neos\Neos\Domain\Model\WorkspaceTitle;
Expand Down Expand Up @@ -148,7 +148,8 @@ public function createRootCommand(string $name, string $contentRepository = 'def
$contentRepositoryId,
$workspaceName,
WorkspaceTitle::fromString($title ?? $name),
WorkspaceDescription::fromString($description ?? '')
WorkspaceDescription::fromString($description ?? ''),
WorkspaceRoleAssignments::createEmpty()
);
$this->outputLine('<success>Created root workspace "%s" in content repository "%s"</success>', [$workspaceName->value, $contentRepositoryId->value]);
}
Expand Down Expand Up @@ -206,6 +207,7 @@ public function createSharedCommand(string $workspace, string $baseWorkspace = '
WorkspaceTitle::fromString($title ?? $workspaceName->value),
WorkspaceDescription::fromString($description ?? ''),
WorkspaceName::fromString($baseWorkspace),
WorkspaceRoleAssignments::createEmpty()
);
$this->outputLine('<success>Created shared workspace "%s"</success>', [$workspaceName->value]);
}
Expand Down Expand Up @@ -400,11 +402,7 @@ public function deleteCommand(string $workspace, bool $force = false, string $co
$this->workspacePublishingService->discardAllWorkspaceChanges($contentRepositoryId, $workspaceName);
}

$contentRepositoryInstance->handle(
DeleteWorkspace::create(
$workspaceName
)
);
$this->workspaceService->deleteWorkspace($contentRepositoryId, $workspaceName);
$this->outputLine('Deleted workspace "%s"', [$workspaceName->value]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
use Neos\ContentRepository\Export\ProcessorInterface;
use Neos\ContentRepository\Export\Severity;
use Neos\Neos\Domain\Model\WorkspaceDescription;
use Neos\Neos\Domain\Model\WorkspaceRole;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
use Neos\Neos\Domain\Model\WorkspaceTitle;
use Neos\Neos\Domain\Service\WorkspaceService;

Expand All @@ -44,7 +43,12 @@ public function run(ProcessingContext $context): void
$context->dispatch(Severity::NOTICE, 'Workspace already exists, skipping');
return;
}
$this->workspaceService->createRootWorkspace($this->contentRepository->id, WorkspaceName::forLive(), WorkspaceTitle::fromString('Live workspace'), WorkspaceDescription::fromString(''));
$this->workspaceService->assignWorkspaceRole($this->contentRepository->id, WorkspaceName::forLive(), WorkspaceRoleAssignment::createForGroup('Neos.Neos:LivePublisher', WorkspaceRole::COLLABORATOR));
$this->workspaceService->createRootWorkspace(
$this->contentRepository->id,
WorkspaceName::forLive(),
WorkspaceTitle::fromString('Public live workspace'),
WorkspaceDescription::empty(),
WorkspaceRoleAssignments::createForLiveWorkspace()
);
}
}
6 changes: 6 additions & 0 deletions Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignment.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,10 @@ public static function createForGroup(string $flowRoleIdentifier, WorkspaceRole
$role
);
}

public function equals(WorkspaceRoleAssignment $other): bool
{
return $this->subject->equals($other->subject)
&& $this->role === $other->role;
}
}
64 changes: 64 additions & 0 deletions Neos.Neos/Classes/Domain/Model/WorkspaceRoleAssignments.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Neos\Neos\Domain\Model;

use Neos\Flow\Annotations as Flow;
use Neos\Neos\Domain\Service\WorkspaceService;

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

public static function createEmpty(): self
{
return new self();
}

public static function create(WorkspaceRoleAssignment ...$assignments): self
{
return new self(...$assignments);
}

/**
* @param array<WorkspaceRoleAssignment> $assignments
*/
Expand All @@ -33,6 +44,44 @@ public static function fromArray(array $assignments): self
return new self(...$assignments);
}

/**
* Default role assignment to be specified at creation via {@see WorkspaceService::createRootWorkspace()}
*
* Users with the role "Neos.Neos:LivePublisher" are collaborators and everybody can read.
*/
public static function createForLiveWorkspace(): self
{
return new self(
WorkspaceRoleAssignment::createForGroup(
'Neos.Neos:LivePublisher',
WorkspaceRole::COLLABORATOR
),
WorkspaceRoleAssignment::createForGroup(
'Neos.Flow:Everybody',
WorkspaceRole::VIEWER
)
);
}

/**
* Default role assignment to be specified at creation via {@see WorkspaceService::createSharedWorkspace()}
*
* Users with the role "Neos.Neos:AbstractEditor" are collaborators and the specified user is manager
*/
public static function createForSharedWorkspace(UserId $userId): self
{
return new self(
WorkspaceRoleAssignment::createForUser(
$userId,
WorkspaceRole::MANAGER,
),
WorkspaceRoleAssignment::createForGroup(
'Neos.Neos:AbstractEditor',
WorkspaceRole::COLLABORATOR,
)
);
}

public function isEmpty(): bool
{
return $this->assignments === [];
Expand All @@ -47,4 +96,19 @@ public function count(): int
{
return count($this->assignments);
}

public function contains(WorkspaceRoleAssignment $assignment): bool
{
foreach ($this->assignments as $existingAssignment) {
if ($existingAssignment->equals($assignment)) {
return true;
}
}
return false;
}

public function withAssignment(WorkspaceRoleAssignment $assignment): self
{
return new self(...[...$this->assignments, $assignment]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,40 @@ public function getMostPrivilegedWorkspaceRoleForSubjects(ContentRepositoryId $c
return WorkspaceRole::from($role);
}

public function deleteWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void
{
try {
$this->dbal->delete(self::TABLE_NAME_WORKSPACE_METADATA, [
'content_repository_id' => $contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
]);
} catch (DbalException $e) {
throw new \RuntimeException(sprintf(
'Failed to delete metadata for workspace "%s" (Content Repository "%s"): %s',
$workspaceName->value,
$contentRepositoryId->value,
$e->getMessage()
), 1726821159, $e);
}
}

public function deleteWorkspaceRoleAssignments(ContentRepositoryId $contentRepositoryId, WorkspaceName $workspaceName): void
{
try {
$this->dbal->delete(self::TABLE_NAME_WORKSPACE_ROLE, [
'content_repository_id' => $contentRepositoryId->value,
'workspace_name' => $workspaceName->value,
]);
} catch (DbalException $e) {
throw new \RuntimeException(sprintf(
'Failed to delete role assignments for workspace "%s" (Content Repository "%s"): %s',
$workspaceName->value,
$contentRepositoryId->value,
$e->getMessage()
), 1726821159, $e);
}
}

/**
* Removes all workspace metadata records for the specified content repository id
*/
Expand Down Expand Up @@ -306,7 +340,7 @@ public function addWorkspaceMetadata(ContentRepositoryId $contentRepositoryId, W
}
}

public function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepositoryId, UserId $userId): ?WorkspaceName
public function findWorkspaceNameByUser(ContentRepositoryId $contentRepositoryId, UserId $userId): ?WorkspaceName
{
$tableMetadata = self::TABLE_NAME_WORKSPACE_METADATA;
$query = <<<SQL
Expand All @@ -326,4 +360,13 @@ public function findPrimaryWorkspaceNameForUser(ContentRepositoryId $contentRepo
]);
return $workspaceName === false ? null : WorkspaceName::fromString($workspaceName);
}

/**
* @param \Closure(): void $fn
* @return void
*/
public function transactional(\Closure $fn): void
{
$this->dbal->transactional($fn);
}
}
14 changes: 13 additions & 1 deletion Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
use Neos\Neos\Domain\Exception\SiteNodeTypeIsInvalid;
use Neos\Neos\Domain\Model\Site;
use Neos\Neos\Domain\Model\SiteNodeName;
use Neos\Neos\Domain\Model\WorkspaceDescription;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignments;
use Neos\Neos\Domain\Model\WorkspaceTitle;

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

public function createSiteNodeIfNotExists(Site $site, string $nodeTypeName): void
{
$this->workspaceService->createLiveWorkspaceIfMissing($this->contentRepository->id);
$liveWorkspace = $this->contentRepository->findWorkspaceByName(WorkspaceName::forLive());
if ($liveWorkspace === null) {
$this->workspaceService->createRootWorkspace(
$this->contentRepository->id,
WorkspaceName::forLive(),
WorkspaceTitle::fromString('Public live workspace'),
WorkspaceDescription::empty(),
WorkspaceRoleAssignments::createForLiveWorkspace()
);
}

$sitesNodeIdentifier = $this->getOrCreateRootNodeAggregate();
$siteNodeType = $this->nodeTypeManager->getNodeType($nodeTypeName);
Expand Down
Loading

0 comments on commit adb6713

Please sign in to comment.