Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEATURE: Workspace UI - Add second confirmation window for rebasing #5454

Open
wants to merge 9 commits into
base: 9.0
Choose a base branch
from
64 changes: 63 additions & 1 deletion Neos.Workspace.Ui/Classes/Controller/WorkspaceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
use Neos\Neos\PendingChangesProjection\ChangeFinder;
use Neos\Neos\PendingChangesProjection\Changes;
use Neos\Neos\Security\Authorization\ContentRepositoryAuthorizationService;
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\ConflictingEvent;
use Neos\Neos\Utility\NodeTypeWithFallbackProvider;
use Neos\Workspace\Ui\ViewModel\ChangeItem;
use Neos\Workspace\Ui\ViewModel\ContentChangeItem;
Expand Down Expand Up @@ -728,10 +729,71 @@ public function rebaseAction(WorkspaceName $workspaceName, bool $force): void
$this->addFlashMessage($this->getModuleLabel('workspaces.ForceRebaseWorkspaceFailed'));
$this->forward('index');
}
$conflictInformation = array_map(fn (ConflictingEvent $conflictingEvent) => [
'error' => $conflictingEvent->getException()->getMessage(),
'affectedNode' => $conflictingEvent->getAffectedNodeAggregateId(),
'event' => (new \ReflectionClass($conflictingEvent->getEvent()))->getShortName() . ' ' . $conflictingEvent->getSequenceNumber()->value,
'eventPayload' => htmlentities(json_encode($conflictingEvent->getEvent(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT), ENT_NOQUOTES),
], iterator_to_array($e->conflictingEvents));

}
$contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId);
$workspace = $contentRepository->findWorkspaceByName($workspaceName);
if ($workspace === null) {
$this->addFlashMessage(
$this->getModuleLabel('workspaces.workspaceDoesNotExist'),
'',
Message::SEVERITY_ERROR
);
$this->throwStatus(404, 'Workspace does not exist');
}
$workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName);
if ($workspace->baseWorkspaceName === null) {
$this->addFlashMessage(
$this->getModuleLabel('workspaces.workspaceDoesNotExist'),
'',
Message::SEVERITY_ERROR
);
$this->throwStatus(404, 'Workspace does not exist');
}
$baseWorkspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->baseWorkspaceName);
$this->response->addHttpHeader('HX-Retarget', '#popover-container');
$this->response->addHttpHeader('HX-ReSwap', 'innerHTML');
$this->view->assign('workspaceName', $workspaceName->value);

$this->view->assignMultiple([
'workspaceName' => $workspaceName->value,
'workspaceTitle' => $workspaceMetadata->title->value,
'baseWorkspaceTitle' => $baseWorkspaceMetadata->title->value,
'conflictInformation' => $conflictInformation,
]);


}

/**
* Confirm force rebase a workspace
*/
public function rebaseConfirmAction(WorkspaceName $workspaceName, int $conflictCount): void
{
$contentRepositoryId = SiteDetectionResult::fromRequest($this->request->getHttpRequest())->contentRepositoryId;

$contentRepository = $this->contentRepositoryRegistry->get($contentRepositoryId);
$workspace = $contentRepository->findWorkspaceByName($workspaceName);
if ($workspace === null) {
$this->addFlashMessage(
$this->getModuleLabel('workspaces.workspaceDoesNotExist'),
'',
Message::SEVERITY_ERROR
);
$this->throwStatus(404, 'Workspace does not exist');
}
$workspaceMetadata = $this->workspaceService->getWorkspaceMetadata($contentRepositoryId, $workspace->workspaceName);

$this->view->assignMultiple([
'workspaceName' => $workspaceName->value,
'workspaceTitle' => $workspaceMetadata->title->value,
'conflictCount' => $conflictCount
]);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
Neos.Workspace.Ui.WorkspaceController.rebase = Neos.Fusion:Component {
/// string
workspaceName = ${workspaceName}
/// string
workspaceTitle = ${workspaceTitle}
/// string
baseWorkspaceTitle = ${baseWorkspaceTitle}
/// array
conflictInformation = ${conflictInformation}


renderer = afx`
<Neos.Workspace.Ui:Component.Modal.Rebase
workspaceName={props.workspaceName}
workspaceTitle={props.workspaceTitle}
baseWorkspaceTitle={props.baseWorkspaceTitle}
conflictInformation={conflictInformation}
/>
`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Neos.Workspace.Ui.WorkspaceController.rebaseConfirm = Neos.Fusion:Component {
/// string
workspaceName = ${workspaceName}
/// string
workspaceTitle = ${workspaceTitle}
/// int
conflictCount = ${conflictCount}

renderer = afx`
<Neos.Workspace.Ui:Component.Modal.RebaseConfirm
workspaceName={props.workspaceName}
workspaceTitle={workspaceTitle}
conflictCount={conflictCount}
/>
`
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
prototype(Neos.Workspace.Ui:Component.Modal.Rebase) < prototype(Neos.Fusion:Component) {
/// string
workspaceName = null
workspaceName = ''
/// string
workspaceTitle = ''
/// string
baseWorkspaceTitle = ''
/// array
conflictInformation = null

@private {
i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')}
popoverId = 'workspace-rebase-modal'

forceRebaseWorkspaceUri = Neos.Fusion:ActionUri {
action = 'rebase'
popoverConfirmId = 'workspace-rebase-confirm-modal'
confirmRebaseUri = Neos.Fusion:ActionUri {
action = 'rebaseConfirm'
format = 'htmx'
arguments {
workspaceName = ${workspaceName}
force = true
conflictCount = ${Array.length(conflictInformation)}
}
}
}

renderer = afx`
<div popover id={private.popoverId}>
<div popover class="error" id={private.popoverId}>
<header>
<button
type="button"
Expand All @@ -28,11 +34,45 @@ prototype(Neos.Workspace.Ui:Component.Modal.Rebase) < prototype(Neos.Fusion:Comp
<Neos.Workspace.Ui:Component.Icon icon="times"/>
</button>
<div class="neos-header">
{private.i18n.id('workspaces.dialog.forceSyncWorkspace')}
<i class="fas fa-bolt"></i> {private.i18n.id('workspaces.dialog.forceSyncWorkspace').arguments([props.workspaceTitle, props.baseWorkspaceTitle])}
</div>
</header>
<section>
<p>{private.i18n.id('workspaces.dialog.thisCanLeadToLostChanges')}</p>
<p>{private.i18n.id('workspaces.dialog.thisCanLeadToLostChanges').arguments([props.baseWorkspaceTitle, props.workspaceTitle])}</p>
<details>
<summary>
{private.i18n.id('workspaces.dialog.ShowInformationAboutConflict').arguments([Array.length(props.conflictInformation)])}
</summary>
<br/>
<Neos.Fusion:Loop items={props.conflictInformation} itemName="conflict">
<table class="neos-table">
<tbody>
<tr>
<td>{private.i18n.id('workspaces.dialog.rebase.error')}:</td>
<td><b>{conflict.error}</b></td>
</tr>
<tr>
<td>{private.i18n.id('workspaces.dialog.rebase.node')}:</td>
<td>
{conflict.affectedNode}
</td>
</tr>
<tr>
<td>{private.i18n.id('workspaces.dialog.rebase.event')}:</td>
<td>
<details>
<summary>{private.i18n.id('workspaces.dialog.rebase.eventDetails')}: {conflict.event}</summary>
<pre style="line-height: 1.5">
{conflict.eventPayload}
</pre>
</details>
</td>
</tr>
</tbody>
</table>
</Neos.Fusion:Loop>
</details>
<p>{private.i18n.id('workspaces.dialog.thisCanLeadToLostChangesConfirmation').arguments([Array.length(props.conflictInformation)])}</p>
</section>
<footer>
<button
Expand All @@ -46,12 +86,13 @@ prototype(Neos.Workspace.Ui:Component.Modal.Rebase) < prototype(Neos.Fusion:Comp
<Neos.Workspace.Ui:Component.Button
isDanger
label={private.i18n.id('workspaces.modal.yesRebaseWorkspace')}
attributes.hx-get={private.forceRebaseWorkspaceUri}
attributes.hx-target="#workspace-module-content"
attributes.hx-get={private.confirmRebaseUri}
attributes.hx-target={'#' + private.popoverConfirmId}
attributes.hx-swap="outerHTML"
attributes.hx-on--after-request={'document.getElementById("' + private.popoverId + '").hidePopover()'}
attributes.hx-on--after-request={'document.getElementById("' + private.popoverConfirmId + '").showPopover()'}
/>
</footer>
<div popover id={private.popoverConfirmId}></div>
</div>
`
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
prototype(Neos.Workspace.Ui:Component.Modal.RebaseConfirm) < prototype(Neos.Fusion:Component) {
/// string
workspaceName = ''
/// string
workspaceTitle = ''
/// int
conflictCount = 0

@private {
i18n = ${I18n.id('').source('Main').package('Neos.Workspace.Ui')}
popoverId = 'workspace-rebase-confirm-modal'
popoverRebaseId = 'workspace-rebase-modal'

forceRebaseWorkspaceUri = Neos.Fusion:ActionUri {
action = 'rebase'
format = 'htmx'
arguments {
workspaceName = ${workspaceName}
force = true
}
}
}

renderer = afx`
<div popover class="error" id={private.popoverId}>
<header>
<button
type="button"
class="neos-close neos-button"
popovertarget={private.popoverId}
popovertargetaction="close"
>
<Neos.Workspace.Ui:Component.Icon icon="times"/>
</button>
<div class="neos-header">
<i class="fas fa-exclamation-triangle"></i> {private.i18n.id('workspaces.dialog.ConfirmationDropConflictingChanges').arguments([props.workspaceTitle])}
</div>
</header>
<section>
<p>{private.i18n.id('workspaces.dialog.ConfirmationDropConflictingChangesCount').arguments([props.conflictCount])}</p>
<p>{private.i18n.id('workspaces.dialog.ConfirmationDropConflictingChangesText')}</p>
</section>
<footer>
<button
type="button"
class="neos-button"
popovertarget={private.popoverRebaseId}
popovertargetaction="close"
>
{private.i18n.id('cancel')}
</button>
<Neos.Workspace.Ui:Component.Button
isDanger
label={private.i18n.id('workspaces.modal.yesRebaseWorkspace')}
attributes.hx-get={private.forceRebaseWorkspaceUri}
attributes.hx-target="#workspace-module-content"
attributes.hx-swap="outerHTML"
attributes.hx-on--after-request={'document.getElementById("' + private.popoverRebaseId + '").hidePopover()'}
/>
</footer>
</div>
`
}
31 changes: 29 additions & 2 deletions Neos.Workspace.Ui/Resources/Private/Translations/en/Main.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,37 @@
<source>This will delete the workspace including all unpublished content. This operation cannot be undone.</source>
</trans-unit>
<trans-unit id="workspaces.dialog.forceSyncWorkspace" xml:space="preserve">
<source>Syncing has failed</source>
<source>Conflicts between workspace "{0}" and "{1}"</source>
</trans-unit>
<trans-unit id="workspaces.dialog.ShowInformationAboutConflict" xml:space="preserve">
<source>Show information about {0} conflict(s)</source>
</trans-unit>
<trans-unit id="workspaces.dialog.thisCanLeadToLostChanges" xml:space="preserve">
<source>A conflict occured while syncing the workspace. Do you want to drop the conflicting changes?</source>
<source>Workspace "{0}" contains modifications that are in conflict with the changes in workspace "{1}".</source>
</trans-unit>
<trans-unit id="workspaces.dialog.thisCanLeadToLostChangesConfirmation" xml:space="preserve">
<source>Do you want to drop {0} conflicting change(s)?</source>
</trans-unit>
<trans-unit id="workspaces.dialog.ConfirmationDropConflictingChanges" xml:space="preserve">
<source>Do you really want to drop all conflicting changes from workspace "{0}"?</source>
</trans-unit>
<trans-unit id="workspaces.dialog.ConfirmationDropConflictingChangesCount" xml:space="preserve">
<source>This will remove {0} change(s) from the workspace.</source>
</trans-unit>
<trans-unit id="workspaces.dialog.ConfirmationDropConflictingChangesText" xml:space="preserve">
<source>This can not be undone! Do you wish to proceed?</source>
</trans-unit>
<trans-unit id="workspaces.dialog.rebase.error" xml:space="preserve">
<source>Error</source>
</trans-unit>
<trans-unit id="workspaces.dialog.rebase.node" xml:space="preserve">
<source>Node</source>
</trans-unit>
<trans-unit id="workspaces.dialog.rebase.event" xml:space="preserve">
<source>Event</source>
</trans-unit>
<trans-unit id="workspaces.dialog.rebase.eventDetails" xml:space="preserve">
<source>Event details</source>
</trans-unit>
<trans-unit id="workspaces.modal.yesRebaseWorkspace" xml:space="preserve">
<source>Yes, drop changes</source>
Expand Down
19 changes: 19 additions & 0 deletions Neos.Workspace.Ui/Resources/Public/Styles/Module.css
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,25 @@ button .icon:not(:last-child) {
justify-content: flex-end;
}

[popover].error {
border: 1px solid var(--warning);
}

[popover].error .neos-header i {
color: var(--orange);
}
[popover].error p {
margin: var(--defaultMargin) 0;
}
[popover]#workspace-rebase-modal{
width: 50vw;
}
[popover] details[open] {
max-height: 50vh;
overflow: auto;
max-width: max-content;
}

/**
* Form styles
*/
Expand Down