From 323e8ad0eb92795e8ac691586ff9fdd9d3046fe7 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Wed, 10 May 2023 19:16:49 +0200 Subject: [PATCH 1/8] FEATURE: Implement edit preview mode for Neos 9 The main change is that edit and preview action are now separated with distinct constraints. - NodeController: has separate edit and preview actions that take the previewMode as argument - NodeUriBuilder and LinkingService: will use preview / edit action once the main request is from the same action - Neos.BackendHelper: has additional methods isEditMode, isPreviewMode and editPreviewModeCacheIdentifier --- .../InvalidEditPreviewModeException.php | 25 +++++++++ .../Controller/Frontend/NodeController.php | 49 ++++++++++++++++-- .../Classes/Domain/Model/EditPreviewMode.php | 43 ++++++++++++++++ .../Repository/EditPreviewModeRepository.php | 44 ++++++++++++++++ .../FrontendRouting/NodeUriBuilder.php | 51 +++++++++++++++++-- .../Classes/Fusion/Helper/BackendHelper.php | 29 +++++++++++ Neos.Neos/Classes/Service/LinkingService.php | 19 +++++-- Neos.Neos/Configuration/Policy.yaml | 8 +++ Neos.Neos/Configuration/Routes.Frontend.yaml | 7 +++ Neos.Neos/Configuration/Settings.yaml | 2 +- .../Override/GlobalCacheIdentifiers.fusion | 2 +- .../Private/Fusion/Prototypes/Editable.fusion | 4 +- .../Resources/Private/Fusion/RootCase.fusion | 5 +- 13 files changed, 269 insertions(+), 19 deletions(-) create mode 100644 Neos.Neos/Classes/Controller/Exception/InvalidEditPreviewModeException.php create mode 100644 Neos.Neos/Classes/Domain/Model/EditPreviewMode.php create mode 100644 Neos.Neos/Classes/Domain/Repository/EditPreviewModeRepository.php diff --git a/Neos.Neos/Classes/Controller/Exception/InvalidEditPreviewModeException.php b/Neos.Neos/Classes/Controller/Exception/InvalidEditPreviewModeException.php new file mode 100644 index 00000000000..1d73fde9f91 --- /dev/null +++ b/Neos.Neos/Classes/Controller/Exception/InvalidEditPreviewModeException.php @@ -0,0 +1,25 @@ +editPreviewModeRepository->findByName($editPreviewMode) : $this->editPreviewModeRepository->findDefault(); + if ($editPreviewModeObject->isPreviewMode === false) { + throw new InvalidEditPreviewModeException(sprintf('"%s" is not a preview mode', $editPreviewMode), 1683127314); + } + $this->renderEditPreviewMode($node, $editPreviewModeObject); + } + /** * @param string $node Legacy name for backwards compatibility of route components + * @param string|null $editPreviewMode Rendering mode like "rawContent" defaults to defaultEditPreviewMode from settings * @throws NodeNotFoundException * @throws \Neos\Flow\Mvc\Exception\StopActionException * @throws \Neos\Flow\Mvc\Exception\UnsupportedRequestTypeException @@ -119,7 +151,16 @@ class NodeController extends ActionController * with unsafe requests from widgets or plugins that are rendered on the node * - For those the CSRF token is validated on the sub-request, so it is safe to be skipped here */ - public function previewAction(string $node): void + public function editAction(string $node, ?string $editPreviewMode = null): void + { + $editPreviewModeObject = $editPreviewMode ? $this->editPreviewModeRepository->findByName($editPreviewMode) : $this->editPreviewModeRepository->findDefault(); + if ($editPreviewModeObject->isEditMode === false) { + throw new InvalidEditPreviewModeException(sprintf('"%s" is not an edit mode', $editPreviewModeObject->name), 1683127295); + } + $this->renderEditPreviewMode($node, $editPreviewModeObject); + } + + protected function renderEditPreviewMode(string $node, EditPreviewMode $editPreviewMode): void { $visibilityConstraints = VisibilityConstraints::frontend(); if ($this->privilegeManager->isPrivilegeTargetGranted('Neos.Neos:Backend.GeneralAccess')) { @@ -163,6 +204,7 @@ public function previewAction(string $node): void $this->view->assignMultiple([ 'value' => $nodeInstance, 'site' => $site, + 'editPreviewMode' => $editPreviewMode ]); if (!$nodeAddress->isInLiveWorkspace()) { @@ -192,7 +234,7 @@ public function previewAction(string $node): void * with unsafe requests from widgets or plugins that are rendered on the node * - For those the CSRF token is validated on the sub-request, so it is safe to be skipped here */ - public function showAction(string $node, bool $showInvisible = false): void + public function showAction(string $node): void { $siteDetectionResult = SiteDetectionResult::fromRequest($this->request->getHttpRequest()); $contentRepository = $this->contentRepositoryRegistry->get($siteDetectionResult->contentRepositoryId); @@ -203,9 +245,6 @@ public function showAction(string $node, bool $showInvisible = false): void } $visibilityConstraints = VisibilityConstraints::frontend(); - if ($showInvisible && $this->privilegeManager->isPrivilegeTargetGranted('Neos.Neos:Backend.GeneralAccess')) { - $visibilityConstraints = VisibilityConstraints::withoutRestrictions(); - } $subgraph = $contentRepository->getContentGraph()->getSubgraph( $nodeAddress->contentStreamId, diff --git a/Neos.Neos/Classes/Domain/Model/EditPreviewMode.php b/Neos.Neos/Classes/Domain/Model/EditPreviewMode.php new file mode 100644 index 00000000000..2d38336edc4 --- /dev/null +++ b/Neos.Neos/Classes/Domain/Model/EditPreviewMode.php @@ -0,0 +1,43 @@ + + */ + #[Flow\InjectConfiguration(path:"userInterface.editPreviewModes")] + protected array $editPreviewModeConfigurations; + + public function findDefault(): EditPreviewMode + { + return EditPreviewMode::fromNameAndConfiguration($this->defaultEditPreviewMode, $this->editPreviewModeConfigurations[$this->defaultEditPreviewMode]); + } + + public function findByName(string $name): EditPreviewMode + { + if (array_key_exists($name,$this->editPreviewModeConfigurations)) { + return EditPreviewMode::fromNameAndConfiguration($name, $this->editPreviewModeConfigurations[$name]); + } + throw new InvalidEditPreviewModeException(sprintf('"%s" is not a valid editPreviewMode', $name), 1683790077); + } +} diff --git a/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php b/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php index 3f77257f2a5..e042b2b954a 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php @@ -65,6 +65,17 @@ public static function fromUriBuilder(UriBuilder $uriBuilder): self public function uriFor(NodeAddress $nodeAddress): UriInterface { if (!$nodeAddress->isInLiveWorkspace()) { + $request = $this->uriBuilder->getRequest(); + if ($request->getControllerPackageKey() === 'Neos.Neos' + && $request->getControllerName() === "Frontend\Node" + ) { + if ($request->getControllerActionName() == 'edit') { + return $this->editUriFor($nodeAddress, $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null); + } elseif ($request->getControllerActionName() == 'preview') { + return $this->previewUriFor($nodeAddress, $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null); + } + } + return $this->previewUriFor($nodeAddress); } return new Uri($this->uriBuilder->uriFor('show', ['node' => $nodeAddress], 'Frontend\Node', 'Neos.Neos')); @@ -75,16 +86,50 @@ public function uriFor(NodeAddress $nodeAddress): UriInterface * A preview URI is used to display a node that is not public yet (i.e. not in a live workspace). * * @param NodeAddress $nodeAddress + * @param string|null $editPreviewMode * @return UriInterface * @throws NoMatchingRouteException | MissingActionNameException | HttpException */ - public function previewUriFor(NodeAddress $nodeAddress): UriInterface + public function previewUriFor(NodeAddress $nodeAddress, ?string $editPreviewMode = null): UriInterface { - return new Uri($this->uriBuilder->uriFor( + $uri = new Uri($this->uriBuilder->uriFor( 'preview', - ['node' => $nodeAddress->serializeForUri()], + [], 'Frontend\Node', 'Neos.Neos' )); + + $queryParameters = ['node' => $nodeAddress->serializeForUri()]; + if ($editPreviewMode) { + $queryParameters['editPreviewMode'] = $editPreviewMode; + } + + return $uri->withQuery(http_build_query($queryParameters)); + } + + /** + * Renders a stable "edit" URI for the given $nodeAddress + * A edit URI is used to render a node for inline editing that is not public yet (i.e. not in a live workspace). + * + * @param NodeAddress $nodeAddress + * @param string|null $editPreviewMode + * @return UriInterface + * @throws NoMatchingRouteException | MissingActionNameException | HttpException + */ + public function editUriFor(NodeAddress $nodeAddress, ?string $editPreviewMode = null): UriInterface + { + $uri = new Uri($this->uriBuilder->uriFor( + 'edit', + [], + 'Frontend\Node', + 'Neos.Neos' + )); + + $queryParameters = ['node' => $nodeAddress->serializeForUri()]; + if ($editPreviewMode) { + $queryParameters['editPreviewMode'] = $editPreviewMode; + } + + return $uri->withQuery(http_build_query($queryParameters)); } } diff --git a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php index 53c5bd94779..dfebb300a32 100644 --- a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php @@ -14,6 +14,7 @@ use Neos\Eel\ProtectedContextAwareInterface; use Neos\Flow\Annotations as Flow; +use Neos\Flow\Mvc\ActionRequest; use Neos\Neos\Service\UserService; /** @@ -40,6 +41,34 @@ public function interfaceLanguage(): string return $this->userService->getInterfaceLanguage(); } + public function isEditMode(ActionRequest $request): bool + { + return ($request->getControllerPackageKey() === 'Neos.Neos' + && $request->getControllerName() === "Frontend\Node" + && $request->getControllerActionName() === 'edit' + ); + } + + public function isPreviewMode(ActionRequest $request): bool + { + return ($request->getControllerPackageKey() === 'Neos.Neos' + && $request->getControllerName() === "Frontend\Node" + && $request->getControllerActionName() === 'preview' + ); + } + + public function editPreviewModeCacheIdentifier(ActionRequest $request): string + { + if ($request->getControllerPackageKey() === 'Neos.Neos' + && $request->getControllerName() === "Frontend\Node" + && ($request->getControllerActionName() === 'edit' || $request->getControllerActionName() === 'preview') + ) { + return $request->getControllerActionName() . ($request->hasArgument('editPreviewMode') ? ':' . $request->getArgument('editPreviewMode') : ''); + } else { + return ""; + } + } + public function allowsCallOfMethod($methodName) { return true; diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index 5355f8125e8..a1eb2b7235f 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -19,6 +19,8 @@ use Neos\ContentRepository\Core\Projection\NodeHiddenState\NodeHiddenStateProjection; use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId; use Neos\ContentRepository\Core\Projection\ContentGraph\NodePath; +use Neos\Flow\Mvc\ActionRequest; +use Neos\Flow\Persistence\Exception\IllegalObjectTypeException; use Neos\Neos\FrontendRouting\NodeAddressFactory; use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints; use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry; @@ -272,7 +274,7 @@ public function convertUriToObject($uri, Node $contextNode = null) * @throws \Neos\Flow\Property\Exception * @throws \Neos\Flow\Security\Exception * @throws HttpException - * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException + * @throws IllegalObjectTypeException */ public function createNodeUri( ControllerContext $controllerContext, @@ -367,7 +369,18 @@ public function createNodeUri( $request = $controllerContext->getRequest()->getMainRequest(); $uriBuilder = clone $controllerContext->getUriBuilder(); $uriBuilder->setRequest($request); - $action = $workspace && $workspace->isPublicWorkspace() && !$hiddenState->isHidden ? 'show' : 'preview'; + + if ($request->getControllerPackageKey() === 'Neos.Neos' + && $request->getControllerName() === "Frontend\Node" + && in_array($request->getControllerActionName(), ['edit', 'preview']) + ) { + $action = $request->getControllerActionName(); + if ( $request->hasArgument('editPreviewMode')) { + $arguments['editPreviewMode'] = $request->getArgument('editPreviewMode'); + } + } else { + $action = $workspace && $workspace->isPublicWorkspace() && !$hiddenState->isHidden ? 'show' : 'preview'; + } return $uriBuilder ->reset() @@ -377,7 +390,7 @@ public function createNodeUri( ->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString) ->setFormat($format ?: $request->getFormat()) ->setCreateAbsoluteUri($absolute) - ->uriFor($action, ['node' => $node], 'Frontend\Node', 'Neos.Neos'); + ->uriFor($action, ['node' => $node], 'Frontend\Node', 'Neos.Neos') . '&grrr'; } /** diff --git a/Neos.Neos/Configuration/Policy.yaml b/Neos.Neos/Configuration/Policy.yaml index 004c42eee77..accafd81c71 100644 --- a/Neos.Neos/Configuration/Policy.yaml +++ b/Neos.Neos/Configuration/Policy.yaml @@ -27,6 +27,10 @@ privilegeTargets: label: Access to the backend content preview matcher: 'method(Neos\Neos\Controller\Frontend\NodeController->previewAction())' + 'Neos.Neos:ContentEdit': + label: Access to the backend edit mode + matcher: 'method(Neos\Neos\Controller\Frontend\NodeController->editAction())' + 'Neos.Neos:BackendLogin': label: General access to the backend login matcher: 'method(Neos\Neos\Controller\LoginController->(index|tokenLogin|authenticate)Action()) || method(Neos\Flow\Security\Authentication\Controller\AbstractAuthenticationController->authenticateAction())' @@ -225,6 +229,10 @@ roles: privilegeTarget: 'Neos.Neos:ContentPreview' permission: GRANT + - + privilegeTarget: 'Neos.Neos:ContentEdit' + permission: GRANT + - privilegeTarget: 'Neos.Neos:Backend.PersonalWorkspaceReadAccess.NodeConverter' permission: GRANT diff --git a/Neos.Neos/Configuration/Routes.Frontend.yaml b/Neos.Neos/Configuration/Routes.Frontend.yaml index 14c60fe202e..74920d9c617 100644 --- a/Neos.Neos/Configuration/Routes.Frontend.yaml +++ b/Neos.Neos/Configuration/Routes.Frontend.yaml @@ -9,6 +9,13 @@ '@action': 'preview' appendExceedingArguments: true +- + name: 'Edit' + uriPattern: 'neos/edit' + defaults: + '@action': 'edit' + appendExceedingArguments: true + - name: 'Default Frontend' uriPattern: '{node}' diff --git a/Neos.Neos/Configuration/Settings.yaml b/Neos.Neos/Configuration/Settings.yaml index 7a34862b337..513450fe9fd 100755 --- a/Neos.Neos/Configuration/Settings.yaml +++ b/Neos.Neos/Configuration/Settings.yaml @@ -278,7 +278,7 @@ Neos: title: Live inPlace: isEditingMode: true - isPreviewMode: false + isPreviewMode: true fusionRenderingPath: '' title: 'Neos.Neos:Main:editPreviewModes.inPlace' position: 100 diff --git a/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion b/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion index 6e5417ac858..e28d9f67e91 100644 --- a/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion @@ -6,5 +6,5 @@ prototype(Neos.Fusion:GlobalCacheIdentifiers) { workspaceChain = ${Array.join(Array.keys(Neos.Caching.getWorkspaceChain(documentNode)), ',')} workspaceChain.@if.has = ${!!documentNode} - editPreviewMode = ${editPreviewMode} + editPreviewMode = ${Neos.Backend.editPreviewModeCacheIdentifier(request)} } diff --git a/Neos.Neos/Resources/Private/Fusion/Prototypes/Editable.fusion b/Neos.Neos/Resources/Private/Fusion/Prototypes/Editable.fusion index 9693c1d04dd..98f4ac7fdb0 100644 --- a/Neos.Neos/Resources/Private/Fusion/Prototypes/Editable.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Prototypes/Editable.fusion @@ -12,9 +12,7 @@ prototype(Neos.Neos:Editable) < prototype(Neos.Fusion:Component) { renderer = Neos.Fusion:Case { editable { - // TODO: add props.node.context.currentRenderingMode.edit - // condition = ${Neos.Node.inBackend(props.node) && props.node.context.currentRenderingMode.edit} - condition = ${Neos.Node.inBackend(props.node)} + condition = ${!Neos.Node.isLive(props.node) && Neos.Backend.isEditMode(request)} renderer = Neos.Fusion:Tag { tagName = ${props.block ? 'div' : 'span'} diff --git a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion index cd1cf2a84f7..3755c3050ce 100644 --- a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion +++ b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion @@ -17,9 +17,8 @@ root { editPreviewMode { @position = 'end 9996' - possibleEditPreviewModePath = ${documentNode.context.currentRenderingMode.fusionPath} - condition = ${Neos.Node.inBackend(documentNode) && this.possibleEditPreviewModePath != null && this.possibleEditPreviewModePath != ''} - renderPath = ${'/' + this.possibleEditPreviewModePath} + condition = ${Neos.Node.inBackend(documentNode) && editPreviewMode.fusionPath} + renderPath = ${'/' + editPreviewMode.fusionPath} } format { From 7a84dae2e0330fe76d5769630447af26d14dbaa2 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Mon, 22 May 2023 16:41:21 +0200 Subject: [PATCH 2/8] TASK: Do not pass the edit preview mode to the rendering but. Instead the fusion path is set by the nodeController The main difference is that this leads to edit preview modes now rendering shortcut nodes using the configured fusionPath --- Neos.Neos/Classes/Controller/Frontend/NodeController.php | 7 +++++-- Neos.Neos/Resources/Private/Fusion/RootCase.fusion | 6 ------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Neos.Neos/Classes/Controller/Frontend/NodeController.php b/Neos.Neos/Classes/Controller/Frontend/NodeController.php index 870f637c80e..884a8a577ef 100644 --- a/Neos.Neos/Classes/Controller/Frontend/NodeController.php +++ b/Neos.Neos/Classes/Controller/Frontend/NodeController.php @@ -201,10 +201,13 @@ protected function renderEditPreviewMode(string $node, EditPreviewMode $editPrev $this->handleShortcutNode($nodeAddress, $contentRepository); } + if ($editPreviewMode->fusionPath) { + $this->view->setFusionPath($editPreviewMode->fusionPath); + } + $this->view->assignMultiple([ 'value' => $nodeInstance, - 'site' => $site, - 'editPreviewMode' => $editPreviewMode + 'site' => $site ]); if (!$nodeAddress->isInLiveWorkspace()) { diff --git a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion index 3755c3050ce..f4e5babd400 100644 --- a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion +++ b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion @@ -15,12 +15,6 @@ root { type = 'Neos.Neos:Page' } - editPreviewMode { - @position = 'end 9996' - condition = ${Neos.Node.inBackend(documentNode) && editPreviewMode.fusionPath} - renderPath = ${'/' + editPreviewMode.fusionPath} - } - format { @position = 'end 9997' condition = ${request.format != 'html'} From da5a085cf2333ce112670ff777718a146ac5a935 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Wed, 24 May 2023 17:52:28 +0200 Subject: [PATCH 3/8] TASK: Add case to rawContent fusion path to ensure shortcuts are rendered as before This is necessary as the fusionPath is now set on the view directly if the edit preview mode defined one. --- .../Private/Fusion/RawContentMode.fusion | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Neos.Neos/Resources/Private/Fusion/RawContentMode.fusion b/Neos.Neos/Resources/Private/Fusion/RawContentMode.fusion index ed7324aeec9..c26c78057d9 100644 --- a/Neos.Neos/Resources/Private/Fusion/RawContentMode.fusion +++ b/Neos.Neos/Resources/Private/Fusion/RawContentMode.fusion @@ -1,3 +1,20 @@ include: RawContent/*.fusion -rawContent = Neos.Neos:RawContent.Document +rawContent = Neos.Fusion:Case +rawContent { + shortcut { + prototype(Neos.Neos:Page) { + body = Neos.Neos:Shortcut + } + + @position = 'start' + condition = ${q(node).is('[instanceof Neos.Neos:Shortcut]')} + type = 'Neos.Neos:Page' + } + + default { + @position = 'end' + condition = true + renderer = Neos.Neos:RawContent.Document + } +} From c4fa79519d1a8fae61de7cb810d0ffb6fddd27f8 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Wed, 24 May 2023 17:58:27 +0200 Subject: [PATCH 4/8] TASK: Make linter happy --- .../Domain/Repository/EditPreviewModeRepository.php | 2 +- .../Classes/FrontendRouting/NodeUriBuilder.php | 9 ++++++--- Neos.Neos/Classes/Fusion/Helper/BackendHelper.php | 13 +++++++++---- Neos.Neos/Classes/Service/LinkingService.php | 5 +++-- .../Fusion/Override/GlobalCacheIdentifiers.fusion | 2 +- 5 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Neos.Neos/Classes/Domain/Repository/EditPreviewModeRepository.php b/Neos.Neos/Classes/Domain/Repository/EditPreviewModeRepository.php index bc392d0c873..d93a391c0b1 100644 --- a/Neos.Neos/Classes/Domain/Repository/EditPreviewModeRepository.php +++ b/Neos.Neos/Classes/Domain/Repository/EditPreviewModeRepository.php @@ -36,7 +36,7 @@ public function findDefault(): EditPreviewMode public function findByName(string $name): EditPreviewMode { - if (array_key_exists($name,$this->editPreviewModeConfigurations)) { + if (array_key_exists($name, $this->editPreviewModeConfigurations)) { return EditPreviewMode::fromNameAndConfiguration($name, $this->editPreviewModeConfigurations[$name]); } throw new InvalidEditPreviewModeException(sprintf('"%s" is not a valid editPreviewMode', $name), 1683790077); diff --git a/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php b/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php index e042b2b954a..571652c8893 100644 --- a/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php +++ b/Neos.Neos/Classes/FrontendRouting/NodeUriBuilder.php @@ -66,13 +66,16 @@ public function uriFor(NodeAddress $nodeAddress): UriInterface { if (!$nodeAddress->isInLiveWorkspace()) { $request = $this->uriBuilder->getRequest(); - if ($request->getControllerPackageKey() === 'Neos.Neos' + if ( + $request->getControllerPackageKey() === 'Neos.Neos' && $request->getControllerName() === "Frontend\Node" ) { if ($request->getControllerActionName() == 'edit') { - return $this->editUriFor($nodeAddress, $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null); + $editPreviewModeArgument = $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null; + return $this->editUriFor($nodeAddress, is_string($editPreviewModeArgument) ? $editPreviewModeArgument : null); } elseif ($request->getControllerActionName() == 'preview') { - return $this->previewUriFor($nodeAddress, $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null); + $editPreviewModeArgument = $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null; + return $this->previewUriFor($nodeAddress, is_string($editPreviewModeArgument) ? $editPreviewModeArgument : null); } } diff --git a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php index dfebb300a32..990d100deed 100644 --- a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php @@ -57,15 +57,20 @@ public function isPreviewMode(ActionRequest $request): bool ); } - public function editPreviewModeCacheIdentifier(ActionRequest $request): string + public function renderingModeCacheIdentifier(ActionRequest $request): string { - if ($request->getControllerPackageKey() === 'Neos.Neos' + if ( + $request->getControllerPackageKey() === 'Neos.Neos' && $request->getControllerName() === "Frontend\Node" && ($request->getControllerActionName() === 'edit' || $request->getControllerActionName() === 'preview') ) { - return $request->getControllerActionName() . ($request->hasArgument('editPreviewMode') ? ':' . $request->getArgument('editPreviewMode') : ''); + $editPreviewModeArgument = $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null; + if (is_string($editPreviewModeArgument)) { + return $request->getControllerActionName() . ':' . $editPreviewModeArgument; + } + return $request->getControllerActionName(); } else { - return ""; + return "show"; } } diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index a1eb2b7235f..19b70bb4b4b 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -370,12 +370,13 @@ public function createNodeUri( $uriBuilder = clone $controllerContext->getUriBuilder(); $uriBuilder->setRequest($request); - if ($request->getControllerPackageKey() === 'Neos.Neos' + if ( + $request->getControllerPackageKey() === 'Neos.Neos' && $request->getControllerName() === "Frontend\Node" && in_array($request->getControllerActionName(), ['edit', 'preview']) ) { $action = $request->getControllerActionName(); - if ( $request->hasArgument('editPreviewMode')) { + if ($request->hasArgument('editPreviewMode')) { $arguments['editPreviewMode'] = $request->getArgument('editPreviewMode'); } } else { diff --git a/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion b/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion index e28d9f67e91..f4980c77ddb 100644 --- a/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Override/GlobalCacheIdentifiers.fusion @@ -6,5 +6,5 @@ prototype(Neos.Fusion:GlobalCacheIdentifiers) { workspaceChain = ${Array.join(Array.keys(Neos.Caching.getWorkspaceChain(documentNode)), ',')} workspaceChain.@if.has = ${!!documentNode} - editPreviewMode = ${Neos.Backend.editPreviewModeCacheIdentifier(request)} + renderingMode = ${Neos.Backend.renderingModeCacheIdentifier(request)} } From 885eff7fbc42f6c3fda457d1463f6c0ff1cacaa8 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Thu, 25 May 2023 17:10:22 +0200 Subject: [PATCH 5/8] BUGFIX: Correct fallback to rawContentMode in backend to use new helpers --- Neos.Neos/Resources/Private/Fusion/RootCase.fusion | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion index f4e5babd400..b54a560b653 100644 --- a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion +++ b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion @@ -39,8 +39,8 @@ root { rawContent { @position = 'end 10000' - condition = ${Neos.Node.inBackend(documentNode) && documentNode.context.currentRenderingMode.edit} - renderPath = '/rawContent' + condition = ${Neos.Node.inBackend(documentNode) && Neos.Backend.isEditMode(request)} + renderer = Neos.Neos:RawContent.Document } # Fail but create a helpful error message From dcc9d3d6501f35c557b6b268cf80a971d73974f1 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Fri, 26 May 2023 15:16:32 +0200 Subject: [PATCH 6/8] TASK: Remove debug leftovers and ensure no content element wrapping is done in preview --- Neos.Neos/Classes/Service/LinkingService.php | 2 +- .../Private/Fusion/Prototypes/ContentElementWrapping.fusion | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index 19b70bb4b4b..d75d9dd152d 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -391,7 +391,7 @@ public function createNodeUri( ->setArgumentsToBeExcludedFromQueryString($argumentsToBeExcludedFromQueryString) ->setFormat($format ?: $request->getFormat()) ->setCreateAbsoluteUri($absolute) - ->uriFor($action, ['node' => $node], 'Frontend\Node', 'Neos.Neos') . '&grrr'; + ->uriFor($action, ['node' => $node], 'Frontend\Node', 'Neos.Neos'); } /** diff --git a/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentElementWrapping.fusion b/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentElementWrapping.fusion index f8f6553aee6..b239e91471c 100644 --- a/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentElementWrapping.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentElementWrapping.fusion @@ -5,6 +5,7 @@ # prototype(Neos.Neos:ContentElementWrapping) { @class = 'Neos\\Neos\\Fusion\\ContentElementWrappingImplementation' + @if.inEditMode = ${Neos.Backend.isEditMode(request)} node = ${node} value = ${value} # Additional attributes in the form '': '' that will be rendered in the ContentElementWrapping From ff71109c48f68b77ac2f5ddfbca295471ec0c3b4 Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Mon, 19 Jun 2023 14:29:09 +0200 Subject: [PATCH 7/8] Apply suggestions from code review Co-authored-by: Bastian Waidelich Co-authored-by: Marc Henry Schultz <85400359+mhsdesign@users.noreply.github.com> --- .../Classes/Fusion/Helper/BackendHelper.php | 18 +++++++----------- Neos.Neos/Classes/Service/LinkingService.php | 7 ++----- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php index 990d100deed..bcf7345beec 100644 --- a/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/BackendHelper.php @@ -41,36 +41,32 @@ public function interfaceLanguage(): string return $this->userService->getInterfaceLanguage(); } - public function isEditMode(ActionRequest $request): bool + public static function isEditMode(ActionRequest $request): bool { return ($request->getControllerPackageKey() === 'Neos.Neos' - && $request->getControllerName() === "Frontend\Node" + && $request->getControllerName() === 'Frontend\\Node' && $request->getControllerActionName() === 'edit' ); } - public function isPreviewMode(ActionRequest $request): bool + public static function isPreviewMode(ActionRequest $request): bool { return ($request->getControllerPackageKey() === 'Neos.Neos' - && $request->getControllerName() === "Frontend\Node" + && $request->getControllerName() === 'Frontend\\Node' && $request->getControllerActionName() === 'preview' ); } - public function renderingModeCacheIdentifier(ActionRequest $request): string + public static function renderingModeCacheIdentifier(ActionRequest $request): string { - if ( - $request->getControllerPackageKey() === 'Neos.Neos' - && $request->getControllerName() === "Frontend\Node" - && ($request->getControllerActionName() === 'edit' || $request->getControllerActionName() === 'preview') - ) { + if (self::isEditMode($request) || self::isPreviewMode($request)) { $editPreviewModeArgument = $request->hasArgument('editPreviewMode') ? $request->getArgument('editPreviewMode') : null; if (is_string($editPreviewModeArgument)) { return $request->getControllerActionName() . ':' . $editPreviewModeArgument; } return $request->getControllerActionName(); } else { - return "show"; + return 'show'; } } diff --git a/Neos.Neos/Classes/Service/LinkingService.php b/Neos.Neos/Classes/Service/LinkingService.php index d75d9dd152d..4a691aa3aff 100644 --- a/Neos.Neos/Classes/Service/LinkingService.php +++ b/Neos.Neos/Classes/Service/LinkingService.php @@ -39,6 +39,7 @@ use Neos\Neos\Exception as NeosException; use Neos\Neos\FrontendRouting\NodeShortcutResolver; use Neos\Neos\FrontendRouting\SiteDetection\SiteDetectionResult; +use Neos\Neos\Fusion\Helper\BackendHelper; use Psr\Http\Message\UriInterface; use Psr\Log\LoggerInterface; @@ -370,11 +371,7 @@ public function createNodeUri( $uriBuilder = clone $controllerContext->getUriBuilder(); $uriBuilder->setRequest($request); - if ( - $request->getControllerPackageKey() === 'Neos.Neos' - && $request->getControllerName() === "Frontend\Node" - && in_array($request->getControllerActionName(), ['edit', 'preview']) - ) { + if (BackendHelper::isEditMode($request) || BackendHelper::isPreviewMode($request)) { $action = $request->getControllerActionName(); if ($request->hasArgument('editPreviewMode')) { $arguments['editPreviewMode'] = $request->getArgument('editPreviewMode'); From d234ca33e35a81c833c4da480a115f518d6a8a0f Mon Sep 17 00:00:00 2001 From: Martin Ficzel Date: Sun, 9 Jul 2023 15:52:56 +0200 Subject: [PATCH 8/8] TASK: Remove the `Neos.Node.inBackend` and use `Neos.Backend.isEditMode()` && `Neos.Backend.isPreviewMode()` `Neos.Node.inBackend` was intended to be a replacement for `node.context.inBackend` but the detection wether a node is rendered in the backend is now done in the Backend helper by checking wich action has actually been called. --- Neos.Neos/Classes/Fusion/Helper/NodeHelper.php | 7 ------- .../Private/Fusion/Prototypes/ContentCollection.fusion | 2 +- .../Private/Fusion/Prototypes/FallbackNode.fusion | 2 +- Neos.Neos/Resources/Private/Fusion/Prototypes/Page.fusion | 2 +- Neos.Neos/Resources/Private/Fusion/RootCase.fusion | 2 +- 5 files changed, 4 insertions(+), 11 deletions(-) diff --git a/Neos.Neos/Classes/Fusion/Helper/NodeHelper.php b/Neos.Neos/Classes/Fusion/Helper/NodeHelper.php index 861ccaa21ed..cea293a0adc 100644 --- a/Neos.Neos/Classes/Fusion/Helper/NodeHelper.php +++ b/Neos.Neos/Classes/Fusion/Helper/NodeHelper.php @@ -84,13 +84,6 @@ public function labelForNode(Node $node): NodeLabelToken return new NodeLabelToken($node); } - public function inBackend(Node $node): bool - { - $contentRepository = $this->contentRepositoryRegistry->get($node->subgraphIdentity->contentRepositoryId); - $nodeAddressFactory = NodeAddressFactory::create($contentRepository); - return !$nodeAddressFactory->createFromNode($node)->isInLiveWorkspace(); - } - /** * @param Node $node * @return int diff --git a/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentCollection.fusion b/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentCollection.fusion index 995e421037b..594cafbf7c6 100644 --- a/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentCollection.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Prototypes/ContentCollection.fusion @@ -27,7 +27,7 @@ prototype(Neos.Neos:ContentCollection) < prototype(Neos.Fusion:Tag) { attributes { class.@process.collectionClass = ${Array.push(value, 'neos-contentcollection')} data-__neos-insertion-anchor = true - data-__neos-insertion-anchor.@if.onlyRenderInBackend = ${Neos.Node.inBackend(documentNode) && node.context.currentRenderingMode.edit} + data-__neos-insertion-anchor.@if.onlyRenderInBackend = ${Neos.Backend.isEditMode(request)} } # Doesn't need to be set, if the node is a content collection. diff --git a/Neos.Neos/Resources/Private/Fusion/Prototypes/FallbackNode.fusion b/Neos.Neos/Resources/Private/Fusion/Prototypes/FallbackNode.fusion index c2126858f69..3bb7f9353a2 100644 --- a/Neos.Neos/Resources/Private/Fusion/Prototypes/FallbackNode.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Prototypes/FallbackNode.fusion @@ -1,4 +1,4 @@ prototype(Neos.Neos:FallbackNode) < prototype(Neos.Neos:Content) { templatePath = 'resource://Neos.Neos/Private/Templates/FusionObjects/FallbackNode.html' - @if.onlyRenderInBackend = ${Neos.Node.inBackend(node)} + @if.onlyRenderInBackend = ${Neos.Backend.isEditMode(request) || Neos.Backend.isPreviewMode(request)} } diff --git a/Neos.Neos/Resources/Private/Fusion/Prototypes/Page.fusion b/Neos.Neos/Resources/Private/Fusion/Prototypes/Page.fusion index e519e01980d..e31d3ad6a7d 100644 --- a/Neos.Neos/Resources/Private/Fusion/Prototypes/Page.fusion +++ b/Neos.Neos/Resources/Private/Fusion/Prototypes/Page.fusion @@ -64,7 +64,7 @@ prototype(Neos.Neos:Page) < prototype(Neos.Fusion:Http.Message) { tagName = 'body' omitClosingTag = true attributes.class.@process.addNeosBackendClass = ${Array.push(value, 'neos-backend')} - attributes.class.@process.addNeosBackendClass.@if.onlyRenderWhenNotInLiveWorkspace = ${Neos.Node.inBackend(documentNode)} + attributes.class.@process.addNeosBackendClass.@if.onlyRenderWhenNotInLiveWorkspace = ${Neos.Backend.isEditMode(request) || Neos.Backend.isPreviewMode(request)} } # Content of the body tag. To be defined by the integrator. diff --git a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion index b54a560b653..e7571dd1235 100644 --- a/Neos.Neos/Resources/Private/Fusion/RootCase.fusion +++ b/Neos.Neos/Resources/Private/Fusion/RootCase.fusion @@ -39,7 +39,7 @@ root { rawContent { @position = 'end 10000' - condition = ${Neos.Node.inBackend(documentNode) && Neos.Backend.isEditMode(request)} + condition = ${Neos.Backend.isEditMode(request)} renderer = Neos.Neos:RawContent.Document }