Skip to content

Commit

Permalink
Merge pull request #5311 from pKallert/feature/new-workspace-review
Browse files Browse the repository at this point in the history
FEATURE: convert detail view to fusion
  • Loading branch information
pKallert authored Oct 23, 2024
2 parents e614497 + 7f26ffa commit 64fa1b7
Show file tree
Hide file tree
Showing 14 changed files with 620 additions and 338 deletions.
75 changes: 53 additions & 22 deletions Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

use Doctrine\DBAL\Exception as DBALException;
use Neos\ContentRepository\Core\ContentRepository;
use Neos\ContentRepository\Core\Dimension\ContentDimensionId;
use Neos\ContentRepository\Core\Feature\SubtreeTagging\Dto\SubtreeTag;
use Neos\ContentRepository\Core\Feature\WorkspaceCreation\Exception\WorkspaceAlreadyExists;
use Neos\ContentRepository\Core\Feature\WorkspaceModification\Command\DeleteWorkspace;
use Neos\ContentRepository\Core\Feature\WorkspacePublication\Command\DiscardIndividualNodesFromWorkspace;
Expand All @@ -37,6 +39,7 @@
use Neos\Diff\Renderer\Html\HtmlArrayRenderer;
use Neos\Error\Messages\Message;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Exception as FlowException;
use Neos\Flow\I18n\Exception\IndexOutOfBoundsException;
use Neos\Flow\I18n\Exception\InvalidFormatPlaceholderException;
use Neos\Flow\I18n\Translator;
Expand All @@ -56,6 +59,7 @@
use Neos\Neos\Domain\Model\WorkspaceRole;
use Neos\Neos\Domain\Model\WorkspaceRoleAssignment;
use Neos\Neos\Domain\Model\WorkspaceTitle;
use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface;
use Neos\Neos\Domain\Repository\SiteRepository;
use Neos\Neos\Domain\Service\NodeTypeNameFactory;
use Neos\Neos\Domain\Service\UserService;
Expand Down Expand Up @@ -107,6 +111,9 @@ class WorkspaceController extends AbstractModuleController
#[Flow\Inject]
protected WorkspaceService $workspaceService;

#[Flow\Inject]
protected NodeLabelGeneratorInterface $nodeLabelGenerator;

#[Flow\Inject]
protected Translator $translator;

Expand Down Expand Up @@ -147,7 +154,7 @@ public function indexAction(): void
]);
}

public function showAction(WorkspaceName $workspace): void
public function reviewAction(WorkspaceName $workspace): void
{
$currentUser = $this->userService->getCurrentUser();
if ($currentUser === null) {
Expand All @@ -171,7 +178,7 @@ public function showAction(WorkspaceName $workspace): void
$baseWorkspacePermissions = $this->workspaceService->getWorkspacePermissionsForUser($contentRepositoryId, $baseWorkspace->workspaceName, $currentUser);
}
$this->view->assignMultiple([
'selectedWorkspace' => $workspaceObj,
'selectedWorkspaceName' => $workspaceObj->workspaceName->value,
'selectedWorkspaceLabel' => $workspaceMetadata->title->value,
'baseWorkspaceName' => $workspaceObj->baseWorkspaceName,
'baseWorkspaceLabel' => $baseWorkspaceMetadata?->title->value,
Expand Down Expand Up @@ -445,11 +452,15 @@ public function deleteAction(WorkspaceName $workspaceName): void
* Rebase the current users personal workspace onto the given $targetWorkspace and then
* redirects to the $targetNode in the content module.
*/
public function rebaseAndRedirectAction(string $targetNode, Workspace $targetWorkspace): void
public function rebaseAndRedirectAction(string $targetNode, WorkspaceName $targetWorkspaceName): void
{
$targetNodeAddress = NodeAddress::fromJsonString(
$targetNode
);
$contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId;
$contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId);

$targetWorkspace = $contentRepository->findWorkspaceByName($targetWorkspaceName);

$user = $this->userService->getCurrentUser();
if ($user === null) {
Expand Down Expand Up @@ -520,34 +531,35 @@ public function publishNodeAction(string $nodeAddress, WorkspaceName $selectedWo
$contentRepository->handle($command);

$this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenPublished'));
$this->redirect('show', null, null, ['workspace' => $selectedWorkspace->value]);
$this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]);
}

/**
* Discard a a single node
* Discard a single node
*
* @param string $nodeAddress
* @param WorkspaceName $selectedWorkspace
*/
public function discardNodeAction(string $nodeAddress, WorkspaceName $selectedWorkspace): void
{
$nodeAddress = NodeAddress::fromJsonString($nodeAddress);

$contentRepository = $this->contentRepositoryRegistry->get($nodeAddress->contentRepositoryId);

$nodeIdsToDiscard = NodeIdsToPublishOrDiscard::create(
new NodeIdToPublishOrDiscard(
$nodeAddress->aggregateId,
$nodeAddress->dimensionSpacePoint
)
);
$command = DiscardIndividualNodesFromWorkspace::create(
$selectedWorkspace,
NodeIdsToPublishOrDiscard::create(
new NodeIdToPublishOrDiscard(
$nodeAddress->aggregateId,
$nodeAddress->dimensionSpacePoint
)
),
$nodeIdsToDiscard
);
$contentRepository->handle($command);

$this->addFlashMessage($this->getModuleLabel('workspaces.selectedChangeHasBeenDiscarded'));
$this->redirect('show', null, null, ['workspace' => $selectedWorkspace->value]);

$this->redirect('review', null, null, ['workspace' => $selectedWorkspace->value]);

}

/**
Expand Down Expand Up @@ -595,7 +607,7 @@ public function publishOrDiscardNodesAction(array $nodes, string $action, string
throw new \RuntimeException('Invalid action "' . htmlspecialchars($action) . '" given.', 1346167441);
}

$this->redirect('show', null, null, ['workspace' => $selectedWorkspaceName->value]);
$this->redirect('review', null, null, ['workspace' => $selectedWorkspaceName->value]);
}

/**
Expand Down Expand Up @@ -704,6 +716,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos

$nodePathSegments = [];
$documentPathSegments = [];
$documentPathSegmentsNames = [];
foreach ($ancestors as $ancestor) {
$pathSegment = $ancestor->name ?: NodeName::fromString($ancestor->aggregateId->value);
// Don't include `sites` path as they are not needed
Expand All @@ -713,6 +726,7 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos
}
if ($this->getNodeType($ancestor)->isOfType(NodeTypeNameFactory::NAME_DOCUMENT)) {
$documentPathSegments[] = $pathSegment;
$documentPathSegmentsNames[] = $this->nodeLabelGenerator->getLabel($ancestor);
if (is_null($documentNode)) {
$documentNode = $ancestor;
}
Expand Down Expand Up @@ -748,16 +762,27 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos
)
)
);

if (!isset($siteChanges[$siteNodeName]['siteNode'])) {
$siteChanges[$siteNodeName]['siteNode']
= $this->siteRepository->findOneByNodeName(SiteNodeName::fromString($siteNodeName));
}

$documentNodeAddress = NodeAddress::create(
$contentRepository->id,
$selectedWorkspace->workspaceName,
$documentNode->originDimensionSpacePoint->toDimensionSpacePoint(),
$documentNode->aggregateId
);
$siteChanges[$siteNodeName]['documents'][$documentPath]['documentNode'] = $documentNode;
$siteChanges[$siteNodeName]['documents'][$documentPath]['documentBreadCrumb'] = array_reverse($documentPathSegmentsNames);

$siteChanges[$siteNodeName]['documents'][$documentPath]['documentNodeAddress'] = $documentNodeAddress->toJson();

// We need to set `isNew` and `isMoved` on document level to make our JS behave as before.
if ($documentNode->equals($node)) {
$siteChanges[$siteNodeName]['documents'][$documentPath]['isNew'] = $change->created;
$siteChanges[$siteNodeName]['documents'][$documentPath]['isMoved'] = $change->moved;
$siteChanges[$siteNodeName]['documents'][$documentPath]['isDeleted'] = $change->deleted;
}

// As for changes of type `delete` we are using nodes from the live workspace
Expand All @@ -769,25 +794,31 @@ protected function computeSiteChanges(Workspace $selectedWorkspace, ContentRepos
$change->originDimensionSpacePoint->toDimensionSpacePoint(),
$change->nodeAggregateId
);

$nodeType = $contentRepository->getNodeTypeManager()->getNodeType($node->nodeTypeName);
$dimensions = [];
foreach ($node->dimensionSpacePoint->coordinates as $id => $coordinate) {
$contentDimension = new ContentDimensionId($id);
$dimensions[] = $contentRepository->getContentDimensionSource()->getDimension($contentDimension)->getValue($coordinate)->configuration['label'];
}
$change = [
'node' => $node,
'serializedNodeAddress' => $nodeAddress->toJson(),
'hidden' => $node->tags->contain(SubtreeTag::disabled()),
'isRemoved' => $change->deleted,
'isNew' => $change->created,
'isMoved' => $change->moved,
'dimensions' => $dimensions,
'lastModificationDateTime' => $node->timestamps->lastModified,
'label' => $this->nodeLabelGenerator->getLabel($node),
'icon' => $nodeType->getFullConfiguration()['ui']['icon'],
'contentChanges' => $this->renderContentChanges(
$node,
$change->contentStreamId,
$contentRepository
)
];
$nodeType = $this->getNodeType($node);
if ($nodeType->isOfType('Neos.Neos:Node')) {
$change['configuration'] = $nodeType->getFullConfiguration();
}
$siteChanges[$siteNodeName]['documents'][$documentPath]['changes'][$relativePath] = $change;
}
$siteChanges[$siteNodeName]['documents'][$documentPath]['changesCount'] = count($siteChanges[$siteNodeName]['documents'][$documentPath]['changes']);
}
}

Expand Down
1 change: 1 addition & 0 deletions Neos.Workspace.Ui/Configuration/Settings.Neos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Neos:
javaScripts:
'HtmxLibrary': 'resource://Neos.Workspace.Ui/Public/Scripts/htmx.min.js'
'Module': 'resource://Neos.Workspace.Ui/Public/Scripts/Module.js'
'Review': 'resource://Neos.Workspace.Ui/Public/Scripts/Review.js'
styleSheets:
'Module': 'resource://Neos.Workspace.Ui/Public/Styles/Module.css'

Expand Down
11 changes: 2 additions & 9 deletions Neos.Workspace.Ui/Configuration/Views.yaml
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && isFormat("html") && isAction("show")'
viewObjectName: 'Neos\FluidAdaptor\View\StandaloneView'
options:
layoutRootPathPattern: 'resource://Neos.Neos/Private/Layouts'
partialRootPaths:
- 'resource://Neos.Workspace.Ui/Private/Partials'
- 'resource://Neos.Neos/Private/Partials'

- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace") && !isAction("show")'
- requestFilter: 'isPackage("Neos.Workspace.Ui") && isController("Workspace")'
viewObjectName: 'Neos\Fusion\View\FusionView'
options:
fusionPathPatterns:
- 'resource://Neos.Neos/Private/Fusion'
- 'resource://Neos.Fusion/Private/Fusion'
- 'resource://Neos.Fusion.Form/Private/Fusion'
- 'resource://Neos.Workspace.Ui/Private/Fusion'
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
##
# Renders a document for review
#
prototype(Neos.Workspace.Ui:Component.ReviewActions) < prototype(Neos.Fusion:Component) {

canPublishToBaseWorkspace = ${canPublishToBaseWorkspace}
selectedWorkspaceName = ${selectedWorkspaceName}
baseWorkspaceLabel = ${baseWorkspaceLabel}

@private {
i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')}
indexWorkspaceUri = Neos.Fusion:ActionUri {
action = 'index'

}
discardWorkspaceUri = Neos.Fusion:ActionUri {
action = 'discardWorkspace'
arguments {
workspace = ${props.selectedWorkspaceName}
}
}
publishWorkspaceUri = Neos.Fusion:ActionUri {
action = 'publishWorkspace'
arguments {
workspace = ${props.selectedWorkspaceName}
}
}
discardSelectedChanges = Neos.Fusion:ActionUri {
action = 'PublishOrDiscardNodes'
arguments {
action = 'discard'
workspace = ${props.selectedWorkspaceName}
}
}
publishSelectedChangesTo = Neos.Fusion:ActionUri {
action = 'PublishOrDiscardNodes'
arguments {
action = 'publish'
workspace = ${props.selectedWorkspaceName}
}
}

}

renderer = afx`
<div class="neos-footer">
<div class="neos-pull-left">
<Neos.Workspace.Ui:Component.Button
icon="fas fa-chevron-left"
label={props.i18n.id('back')}
attributes.hx-get={private.indexWorkspaceUri}
attributes.hx-target="body"
attributes.hx-swap="innerHTML"
/>
</div>
<div class="neos-pull-right">
<Neos.Workspace.Ui:Component.Button
label={private.i18n.id('workspaces.discardAllChanges')}
icon="fas fa-trash-alt icon-white"
isDanger={true}
attributes.hx-get={private.discardWorkspaceUri}
attributes.hx-target="body"
/>
<Neos.Workspace.Ui:Component.Button
@if.canPublishToBaseWorkspace={props.canPublishToBaseWorkspace}
label={private.i18n.id('workspaces.publishAllChangesTo').arguments([props.baseWorkspaceLabel])}
icon="fas fa-check-double icon-white"
isPrimary={true}
attributes.hx-get={private.publishWorkspaceUri}
attributes.hx-target="body"
/>
<Neos.Workspace.Ui:Component.Button
label={private.i18n.id('workspaces.discardSelectedChanges')}
icon="fas fa-trash-alt icon-white"
isDanger={true}
attributes.hx-get={private.discardSelectedChanges}
attributes.hx-target="body"
/>
<Neos.Workspace.Ui:Component.Button
@if.canPublishToBaseWorkspace={props.canPublishToBaseWorkspace}
label={I18n.id('workspaces.publishSelectedChangesTo')}
icon="fas fa-check icon-white"
isDanger={true}
attributes.hx-get={private.publishSelectedChangesTo}
attributes.hx-target="body"
/>


</div>
</div>
`
}
Loading

0 comments on commit 64fa1b7

Please sign in to comment.