Skip to content

Commit

Permalink
TASK: Improve HTMX integration in workspace UI
Browse files Browse the repository at this point in the history
Use http headers for proper status codes and
transport flash messages
  • Loading branch information
Sebobo committed Jul 18, 2024
1 parent 3a3ea13 commit 57a021d
Show file tree
Hide file tree
Showing 7 changed files with 94 additions and 27 deletions.
24 changes: 5 additions & 19 deletions Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException;
use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Flow\Mvc\View\ViewInterface;
use Neos\Flow\Package\PackageManager;
use Neos\Flow\Property\PropertyMapper;
use Neos\Flow\Security\Account;
Expand Down Expand Up @@ -107,18 +106,6 @@ class WorkspaceController extends AbstractModuleController
#[Flow\Inject]
protected WorkspaceProvider $workspaceProvider;

protected function initializeView(ViewInterface $view): void
{
parent::initializeView($view);
// If we're in a HTMX-request...
if ($this->request->getHttpRequest()->hasHeader('HX-Request')) {
// We append an "/htmx" segment to the fusion path, changing it from "<PackageKey>/<ControllerName>/<ActionName>" to "<PackageKey>/<ControllerName>/<ActionName>/htmx"
$htmxFusionPath = str_replace(['\\Controller\\', '\\'], ['\\', '/'], $this->request->getControllerObjectName());
$htmxFusionPath .= '/' . $this->request->getControllerActionName() . '/htmx';
$view->setOption('fusionPath', $htmxFusionPath);
}
}

/**
* Display a list of unpublished content
*/
Expand Down Expand Up @@ -383,11 +370,11 @@ public function deleteAction(WorkspaceName $workspaceName): void
'',
Message::SEVERITY_ERROR
);
$this->redirect('index');
$this->throwStatus(404, 'Workspace does not exist');
}

if ($workspace->isPersonalWorkspace()) {
$this->redirect('index');
$this->throwStatus(403, 'Personal workspaces cannot be deleted');
}

$dependentWorkspaces = $contentRepository->getWorkspaceFinder()
Expand All @@ -408,7 +395,7 @@ public function deleteAction(WorkspaceName $workspaceName): void
'Neos.Workspace.Ui'
) ?: 'workspaces.workspaceCannotBeDeletedBecauseOfDependencies';
$this->addFlashMessage($message, '', Message::SEVERITY_WARNING);
$this->redirect('index');
$this->throwStatus(403, 'Workspace has dependencies');
}

$nodesCount = 0;
Expand All @@ -428,7 +415,7 @@ public function deleteAction(WorkspaceName $workspaceName): void
'Neos.Workspace.Ui'
) ?: 'workspaces.notDeletedErrorWhileFetchingUnpublishedNodes';
$this->addFlashMessage($message, '', Message::SEVERITY_WARNING);
$this->redirect('index');
$this->throwStatus(500, 'Error while fetching unpublished nodes');
}
if ($nodesCount > 0) {
$message = $this->translator->translateById(
Expand All @@ -440,7 +427,7 @@ public function deleteAction(WorkspaceName $workspaceName): void
'Neos.Workspace.Ui'
) ?: 'workspaces.workspaceCannotBeDeletedBecauseOfUnpublishedNodes';
$this->addFlashMessage($message, '', Message::SEVERITY_WARNING);
$this->redirect('index');
$this->throwStatus(403, 'Workspace has unpublished nodes');
}

$contentRepository->handle(
Expand All @@ -457,7 +444,6 @@ public function deleteAction(WorkspaceName $workspaceName): void
'Main',
'Neos.Workspace.Ui'
) ?: 'workspaces.workspaceHasBeenRemoved');
$this->redirect('index');
}

/**
Expand Down
26 changes: 26 additions & 0 deletions Neos.Workspace.Ui/Classes/Mvc/HtmxRequestPattern.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

/*
* This file is part of the Neos.Workspace.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\Workspace\Ui\Mvc;

use Neos\Flow\Mvc\ActionRequest;
use Neos\Flow\Security\RequestPatternInterface;

final class HtmxRequestPattern implements RequestPatternInterface
{
public function matchRequest(ActionRequest $request): bool
{
return $request->getFormat() === 'htmx';
}
}
48 changes: 48 additions & 0 deletions Neos.Workspace.Ui/Classes/Mvc/HttpHeaderFlashMessageStorage.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

/*
* This file is part of the Neos.Workspace.Ui package.
*
* (c) Contributors of the Neos Project - www.neos.io
*
* This package is Open Source Software. For the full copyright and license
* information, please view the LICENSE file which was distributed with this
* source code.
*/

declare(strict_types=1);

namespace Neos\Workspace\Ui\Mvc;

use Neos\Error\Messages\Message;
use Neos\Flow\Mvc\FlashMessage\FlashMessageContainer;
use Neos\Flow\Mvc\FlashMessage\FlashMessageStorageInterface;
use Psr\Http\Message\ResponseInterface as HttpResponseInterface;
use Psr\Http\Message\ServerRequestInterface as HttpRequestInterface;

final class HttpHeaderFlashMessageStorage implements FlashMessageStorageInterface
{
private FlashMessageContainer|null $flashMessageContainer = null;

public function load(HttpRequestInterface $request): FlashMessageContainer
{
if ($this->flashMessageContainer === null) {
$this->flashMessageContainer = new FlashMessageContainer();
}
return $this->flashMessageContainer;
}

public function persist(HttpResponseInterface $response): HttpResponseInterface
{
$messages = array_map(static fn(Message $message) => [
'title' => $message->getTitle(),
'message' => $message->render(),
'severity' => $message->getSeverity(),
'code' => $message->getCode(),
], $this->flashMessageContainer?->getMessagesAndFlush() ?? []);
if ($messages === []) {
return $response;
}
return $response->withAddedHeader('X-Flow-FlashMessages', json_encode($messages, JSON_THROW_ON_ERROR));
}
}
10 changes: 10 additions & 0 deletions Neos.Workspace.Ui/Configuration/Settings.Flow.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Neos:
Flow:
mvc:
flashMessages:
containers:
'httpHeaderFlashMessages':
storage: 'Neos\Workspace\Ui\Mvc\HttpHeaderFlashMessageStorage'
requestPatterns:
'htmx':
pattern: 'Neos\Workspace\Ui\Mvc\HtmxRequestPattern'
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ prototype(Neos.Workspace.Ui:Component.Modal.Delete) < prototype(Neos.Fusion:Comp
form.target.action="delete"
form.target.format="htmx"
form.target.arguments.workspaceName={props.workspaceName}
class="neos-inline"
attributes.class="neos-inline"
attributes.hx-post={form.getTarget()}
attributes.hx-target="closest tr"
attributes.hx-swap="delete"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
##
# Empty template for the delete response as the payload is contained in the HTTP headers
#
Neos.Workspace.Ui.WorkspaceController.delete.htmx = ''

This file was deleted.

0 comments on commit 57a021d

Please sign in to comment.