diff --git a/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php b/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php index e124b5cd0a1..d94f1ecef01 100755 --- a/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php +++ b/Neos.Neos/Classes/Controller/Module/Administration/SitesController.php @@ -16,11 +16,11 @@ use Neos\ContentRepository\Core\Feature\NodeRenaming\Command\ChangeNodeAggregateName; use Neos\ContentRepository\Core\Projection\ContentGraph\NodeAggregate; -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; use Neos\ContentRepository\Core\SharedModel\Node\NodeName; +use Neos\ContentRepository\Core\SharedModel\Workspace\Workspace; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\ContentRepositoryRegistry\Exception\ContentRepositoryNotFoundException; @@ -41,10 +41,6 @@ use Neos\Neos\Domain\Service\NodeTypeNameFactory; use Neos\Neos\Domain\Service\SiteService; use Neos\Neos\Domain\Service\UserService; -use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; -use Neos\SiteKickstarter\Generator\SitePackageGeneratorInterface; -use Neos\SiteKickstarter\Service\SiteGeneratorCollectingService; -use Neos\SiteKickstarter\Service\SitePackageGeneratorNameService; /** * The Neos Sites Management module controller @@ -89,6 +85,18 @@ class SitesController extends AbstractModuleController */ protected $session; + /** + * This is not 100% correct, but it is as good as we can get it to work right now + * It works when the created site's name will use the configuration "*" which by default uses the default preset. + * + * As proposed here https://github.com/neos/neos-development-collection/issues/4470#issuecomment-2432140074 + * the site must have a field to define the contentRepositoryId to correctly create sites dynamically. + * + * @Flow\InjectConfiguration("sitePresets.default.contentRepository") + * @var string|null + */ + protected $defaultContentRepositoryForNewSites; + #[Flow\Inject] protected UserService $domainUserService; @@ -236,65 +244,29 @@ public function updateSiteAction(Site $site, $newSiteNodeName) */ public function newSiteAction(): void { - // This is not 100% correct, but it is as good as we can get it to work right now try { - $contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest()) - ->contentRepositoryId; - } catch (\RuntimeException) { - $contentRepositoryId = ContentRepositoryId::fromString('default'); + $contentRepositoryId = ContentRepositoryId::fromString($this->defaultContentRepositoryForNewSites ?? ''); + } catch (\InvalidArgumentException $e) { + throw new \RuntimeException('The default content repository for new sites configured in "Neos.Neos.sitePresets.default.contentRepository" is not valid.', 1736946907, $e); } try { $contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId); - $documentNodeTypes = $contentRepository->getNodeTypeManager()->getSubNodeTypes(NodeTypeNameFactory::forSite(), false); - } catch (ContentRepositoryNotFoundException) { - $documentNodeTypes = []; + } catch (ContentRepositoryNotFoundException $e) { + throw new \RuntimeException(sprintf('The default content repository for new sites "%s" could not be instantiated.', $contentRepositoryId->value), 1736946907); } + $documentNodeTypes = $contentRepository->getNodeTypeManager()->getSubNodeTypes(NodeTypeNameFactory::forSite(), false); $sitePackages = $this->packageManager->getFilteredPackages('available', 'neos-site'); $this->view->assignMultiple([ + 'defaultContentRepositoryForNewSites' => $contentRepositoryId->value, 'sitePackages' => $sitePackages, 'documentNodeTypes' => $documentNodeTypes ]); } - /** - * Import a site from site package. - * - * @param string $packageKey Package from where the import will come - * @Flow\Validate(argumentName="$packageKey", type="\Neos\Neos\Validation\Validator\PackageKeyValidator") - * @return void - */ - /*public function importSiteAction($packageKey) - { - try { - $this->siteImportService->importFromPackage($packageKey); - $this->addFlashMessage( - $this->getModuleLabel('sites.theSiteHasBeenImported.body'), - '', - Message::SEVERITY_OK, - [], - 1412372266 - ); - } catch (\Exception $exception) { - $logMessage = $this->throwableStorage->logThrowable($exception); - $this->logger->error($logMessage, LogEnvironment::fromMethodName(__METHOD__)); - $this->addFlashMessage( - $this->getModuleLabel( - 'sites.importError.body', - [htmlspecialchars($packageKey), htmlspecialchars($exception->getMessage())] - ), - $this->getModuleLabel('sites.importError.title'), - Message::SEVERITY_ERROR, - [], - 1412372375 - ); - } - $this->unsetLastVisitedNodeAndRedirect('index'); - }*/ - /** * Create a new empty site. * diff --git a/Neos.Neos/Classes/Domain/Service/SiteService.php b/Neos.Neos/Classes/Domain/Service/SiteService.php index 42f7bea37e6..a43df73c453 100644 --- a/Neos.Neos/Classes/Domain/Service/SiteService.php +++ b/Neos.Neos/Classes/Domain/Service/SiteService.php @@ -20,6 +20,7 @@ use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; use Neos\Flow\Annotations as Flow; use Neos\Flow\Persistence\PersistenceManagerInterface; +use Neos\Flow\Security\Context as SecurityContext; use Neos\Media\Domain\Model\Asset; use Neos\Media\Domain\Repository\AssetCollectionRepository; use Neos\Neos\Domain\Exception\SiteNodeNameIsAlreadyInUseByAnotherSite; @@ -70,6 +71,12 @@ class SiteService */ protected $workspaceService; + /** + * @Flow\Inject + * @var SecurityContext + */ + protected $securityContext; + /** * Remove given site all nodes for that site and all domains associated. */ @@ -77,7 +84,8 @@ public function pruneSite(Site $site): void { $siteServiceInternals = new SiteServiceInternals( $this->contentRepositoryRegistry->get($site->getConfiguration()->contentRepositoryId), - $this->workspaceService + $this->workspaceService, + $this->securityContext ); try { @@ -181,7 +189,8 @@ public function createSite( $siteServiceInternals = new SiteServiceInternals( $this->contentRepositoryRegistry->get($site->getConfiguration()->contentRepositoryId), - $this->workspaceService + $this->workspaceService, + $this->securityContext ); $siteServiceInternals->createSiteNodeIfNotExists($site, $nodeTypeName); diff --git a/Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php b/Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php index 09d64fefd46..ceb708e20de 100644 --- a/Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php +++ b/Neos.Neos/Classes/Domain/Service/SiteServiceInternals.php @@ -30,6 +30,7 @@ use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\SharedModel\Node\NodeVariantSelectionStrategy; use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName; +use Neos\Flow\Security\Context as SecurityContext; use Neos\Neos\Domain\Exception\SiteNodeTypeIsInvalid; use Neos\Neos\Domain\Model\Site; use Neos\Neos\Domain\Model\SiteNodeName; @@ -49,6 +50,7 @@ public function __construct( private ContentRepository $contentRepository, private WorkspaceService $workspaceService, + private SecurityContext $securityContext ) { $this->nodeTypeManager = $this->contentRepository->getNodeTypeManager(); $this->interDimensionalVariationGraph = $this->contentRepository->getVariationGraph(); @@ -66,9 +68,13 @@ public function removeSiteNode(SiteNodeName $siteNodeName): void ); } + // todo only remove site node in base workspace and rebase dependant workspaces to avoid also the security hacks here. foreach ($this->contentRepository->findWorkspaces() as $workspace) { - $contentGraph = $this->contentRepository->getContentGraph($workspace->workspaceName); - $sitesNodeAggregate = $contentGraph->findRootNodeAggregateByType( + $contentGraph = null; + $this->securityContext->withoutAuthorizationChecks(function () use (&$contentGraph, $workspace) { + $contentGraph = $this->contentRepository->getContentGraph($workspace->workspaceName); + }); + $sitesNodeAggregate = $contentGraph?->findRootNodeAggregateByType( NodeTypeNameFactory::forSites() ); if (!$sitesNodeAggregate) { @@ -80,12 +86,14 @@ public function removeSiteNode(SiteNodeName $siteNodeName): void $siteNodeName->toNodeName() ); if ($siteNodeAggregate instanceof NodeAggregate) { - $this->contentRepository->handle(RemoveNodeAggregate::create( - $workspace->workspaceName, - $siteNodeAggregate->nodeAggregateId, - $arbitraryDimensionSpacePoint, - NodeVariantSelectionStrategy::STRATEGY_ALL_VARIANTS, - )); + $this->securityContext->withoutAuthorizationChecks( + fn () => $this->contentRepository->handle(RemoveNodeAggregate::create( + $workspace->workspaceName, + $siteNodeAggregate->nodeAggregateId, + $arbitraryDimensionSpacePoint, + NodeVariantSelectionStrategy::STRATEGY_ALL_VARIANTS, + )) + ); } } } @@ -94,12 +102,15 @@ public function createSiteNodeIfNotExists(Site $site, string $nodeTypeName): voi { $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() + // CreateRootWorkspace was denied: Creation of root workspaces is currently only allowed with disabled authorization checks + $this->securityContext->withoutAuthorizationChecks( + fn () => $this->workspaceService->createRootWorkspace( + $this->contentRepository->id, + WorkspaceName::forLive(), + WorkspaceTitle::fromString('Public live workspace'), + WorkspaceDescription::empty(), + WorkspaceRoleAssignments::createForLiveWorkspace() + ) ); } diff --git a/Neos.Neos/Resources/Private/Templates/Module/Administration/Sites/NewSite.html b/Neos.Neos/Resources/Private/Templates/Module/Administration/Sites/NewSite.html index fc2a4a830c0..4c9016a9e79 100644 --- a/Neos.Neos/Resources/Private/Templates/Module/Administration/Sites/NewSite.html +++ b/Neos.Neos/Resources/Private/Templates/Module/Administration/Sites/NewSite.html @@ -5,30 +5,11 @@

{neos:backend.translate(id: 'sites.new', value: 'New site', source: 'Modules')}

- -
- {neos:backend.translate(id: 'sites.import', value: 'Import a site', source: 'Modules')} - - -
- -
- -
-
- -
- -

{neos:backend.translate(id: 'sites.noPackackeInfo', value: 'No site packages are available. Make sure you have an active site package.', source: 'Modules')} -

-
-
-
-
- -
+
{neos:backend.translate(id: 'sites.createBlank', value: 'Create a blank site', source: 'Modules')} + {neos:backend.translate(id: 'sites.creationContentRepository', value: 'In the content repository: "{defaultContentRepositoryForNewSites}"', source: 'Modules', arguments: {0: defaultContentRepositoryForNewSites})} +
@@ -59,6 +40,7 @@

{neos:backend.translate(id: 'sites.new', value: 'New site', source: 'Modules

+