Skip to content

Commit

Permalink
Feature: Add basic popup for partial publish
Browse files Browse the repository at this point in the history
  • Loading branch information
pKallert committed Dec 10, 2024
1 parent 4e3824a commit c1fe4f5
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
namespace Neos\Neos\Ui\Application\PublishChangesInDocument;

use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed;
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\PartialWorkspaceRebaseFailed;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist;
use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint;
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
Expand All @@ -23,6 +24,7 @@
use Neos\Neos\Domain\Service\WorkspacePublishingService;
use Neos\Neos\Ui\Application\Shared\ConflictsOccurred;
use Neos\Neos\Ui\Application\Shared\PublishSucceeded;
use Neos\Neos\Ui\Application\Shared\PartialPublishFailed;
use Neos\Neos\Ui\Controller\TranslationTrait;
use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory;

Expand Down Expand Up @@ -51,7 +53,8 @@ final class PublishChangesInDocumentCommandHandler
*/
public function handle(
PublishChangesInDocumentCommand $command
): PublishSucceeded|ConflictsOccurred {
): PublishSucceeded|ConflictsOccurred|PartialPublishFailed
{
try {
$publishingResult = $this->workspacePublishingService->publishChangesInDocument(
$command->contentRepositoryId,
Expand All @@ -67,6 +70,25 @@ public function handle(
numberOfAffectedChanges: $publishingResult->numberOfPublishedChanges,
baseWorkspaceName: $workspace?->baseWorkspaceName?->value
);
} catch (WorkspaceRebaseFailed $e) {
$conflictsFactory = new ConflictsFactory(
contentRepository: $this->contentRepositoryRegistry
->get($command->contentRepositoryId),
nodeLabelGenerator: $this->nodeLabelGenerator,
workspaceName: $command->workspaceName,
preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint
);

return new ConflictsOccurred(
conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e)
);
} catch (PartialWorkspaceRebaseFailed $e) {
$workspace = $this->contentRepositoryRegistry->get($command->contentRepositoryId)->findWorkspaceByName(
$command->workspaceName
);
return new PartialPublishFailed(
baseWorkspaceName: $workspace?->baseWorkspaceName?->value
);
} catch (NodeAggregateCurrentlyDoesNotExist $e) {
throw new \RuntimeException(
$this->getLabel('NodeNotPublishedMissingParentNode'),
Expand All @@ -79,18 +101,7 @@ public function handle(
1705053432,
$e
);
} catch (WorkspaceRebaseFailed $e) {
$conflictsFactory = new ConflictsFactory(
contentRepository: $this->contentRepositoryRegistry
->get($command->contentRepositoryId),
nodeLabelGenerator: $this->nodeLabelGenerator,
workspaceName: $command->workspaceName,
preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint
);

return new ConflictsOccurred(
conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e)
);
}
}
}
36 changes: 36 additions & 0 deletions Classes/Application/Shared/PartialPublishFailed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the Neos.Neos.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\Neos\Ui\Application\Shared;

use Neos\Flow\Annotations as Flow;

/**
* @internal for communication within the Neos UI only
*/
#[Flow\Proxy(false)]
final readonly class PartialPublishFailed implements \JsonSerializable
{
public function __construct(
public ?string $baseWorkspaceName
) {
}

public function jsonSerialize(): mixed
{
return [
'partialPublishFail' => get_object_vars($this)
];
}
}
9 changes: 9 additions & 0 deletions Resources/Private/Translations/en/PublishingDialog.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@
<trans-unit id="discard.document.success.acknowledge" xml:space="preserve">
<source>OK</source>
</trans-unit>
<trans-unit id="partialPublishFailed.title" xml:space="preserve">
<source>Could not publish all changes in "{scopeTitle}"</source>
</trans-unit>
<trans-unit id="partialPublishFailed.message" xml:space="preserve">
<source>Some changes in this document are dependant on changes in other documents. Do you want to publish all changes to the workspace "{targetWorkspaceName}"?</source>
</trans-unit>
<trans-unit id="partialPublishFailed.publishAll" xml:space="preserve">
<source>Yes, publish all changes</source>
</trans-unit>
</body>
</file>
</xliff>
17 changes: 17 additions & 0 deletions packages/neos-ui-redux-store/src/CR/Publishing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export enum PublishingPhase {
START,
ONGOING,
CONFLICTS,
PARTIALLYFAILED,
SUCCESS,
ERROR
}
Expand All @@ -37,6 +38,7 @@ export type State = null | {
| { phase: PublishingPhase.START }
| { phase: PublishingPhase.ONGOING }
| { phase: PublishingPhase.CONFLICTS }
| { phase: PublishingPhase.PARTIALLYFAILED }
| {
phase: PublishingPhase.ERROR;
error: null | AnyError;
Expand All @@ -56,7 +58,9 @@ export enum actionTypes {
CONFLICTS_OCCURRED = '@neos/neos-ui/CR/Publishing/CONFLICTS_OCCURRED',
CONFLICTS_RESOLVED = '@neos/neos-ui/CR/Publishing/CONFLICTS_RESOLVED',
FAILED = '@neos/neos-ui/CR/Publishing/FAILED',
PARTIALLYFAILED = '@neos/neos-ui/CR/Publishing/PARTIALLYFAILED',
RETRIED = '@neos/neos-ui/CR/Publishing/RETRIED',
RETRIEDWITHSTATE = '@neos/neos-ui/CR/Publishing/RETRIEDWITHSTATE',
SUCEEDED = '@neos/neos-ui/CR/Publishing/SUCEEDED',
ACKNOWLEDGED = '@neos/neos-ui/CR/Publishing/ACKNOWLEDGED',
FINISHED = '@neos/neos-ui/CR/Publishing/FINISHED'
Expand Down Expand Up @@ -94,6 +98,11 @@ const resolveConflicts = () => createAction(actionTypes.CONFLICTS_RESOLVED);
const fail = (error: null | AnyError) =>
createAction(actionTypes.FAILED, {error});

/**
* Signal that the ongoing publish/discard workflow has partially failed
*/
const partialFail = () => createAction(actionTypes.PARTIALLYFAILED);

/**
* Attempt to retry a failed publish/discard workflow
*/
Expand Down Expand Up @@ -125,6 +134,7 @@ export const actions = {
conflicts,
resolveConflicts,
fail,
partialFail,
retry,
succeed,
acknowledge,
Expand Down Expand Up @@ -183,6 +193,13 @@ export const reducer = (state: State = defaultState, action: Action): State => {
error: action.payload.error
}
};
case actionTypes.PARTIALLYFAILED:
return {
...state,
process: {
phase: PublishingPhase.PARTIALLYFAILED
}
};
case actionTypes.RETRIED:
return {
...state,
Expand Down
7 changes: 7 additions & 0 deletions packages/neos-ui-sagas/src/Publish/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ type PublishingResponse =
numberOfAffectedChanges: number;
}
}
| {
partialPublishFail: {
numberOfAffectedChanges: number;
}
}
| { conflicts: Conflict[] }
| { error: AnyError };

Expand Down Expand Up @@ -131,6 +136,8 @@ export function * watchPublishing({routes}: {routes: Routes}) {
}
} else if ('error' in result) {
yield put(actions.CR.Publishing.fail(result.error));
} else if ('partialPublishFail' in result) {
yield put(actions.CR.Publishing.partialFail());
} else {
yield put(actions.CR.Publishing.fail(null));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* This file is part of the Neos.Neos.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.
*/
import React from 'react';

import {Button, Dialog, Icon} from '@neos-project/react-ui-components';
import I18n from '@neos-project/neos-ui-i18n';
import {PublishingMode, PublishingPhase, PublishingScope} from '@neos-project/neos-ui-redux-store/src/CR/Publishing';

import {Diagram} from './Diagram';

import style from './style.module.css';

type ConfirmationDialogProps = {
mode: PublishingMode;
scope: PublishingScope;
scopeTitle: string;
sourceWorkspaceName: string;
targetWorkspaceName: null | string;
numberOfChanges: number;
onAbort: () => void;
onConfirm: () => void;
}

export const PublishAllConfirmationDialog: React.FC<ConfirmationDialogProps> = (props) => {
const variant = {
id: '',
style: 'error',
icon: {
title: 'exclamation-triangle',
confirm: 'exclamation-triangle'
},
label: {
title: {
id: 'Neos.Neos.Ui:PublishingDialog:partialPublishFailed.title',
fallback: (props: { scopeTitle: string; }) =>
`Could not publish all changes in "${props.scopeTitle}"`
},
message: {
id: 'Neos.Neos.Ui:PublishingDialog:partialPublishFailed.message',
fallback: (props: { scopeTitle: string; targetWorkspaceName: null | string; }) =>
`Some changes in this document are dependent on changes in other documents. Do you want to publish all changes to the workspace "${props.targetWorkspaceName}"?`
},
cancel: {
id: 'Neos.Neos.Ui:PublishingDialog:publish.all.confirmation.cancel',
fallback: 'No, cancel'
},
confirm: {
id: 'Neos.Neos.Ui:PublishingDialog:partialPublishFailed.publishAll',
fallback: 'Yes, publish all changes'
}
}
}

return (
<Dialog
actions={[
<Button
id={`${variant.id}-Cancel`}
key="cancel"
style="lighter"
hoverStyle="brand"
onClick={props.onAbort}
>
<I18n {...variant.label.cancel} />
</Button>,
<Button
id={`${variant.id}-Confirm`}
key="confirm"
style={variant.style}
hoverStyle={variant.style}
onClick={props.onConfirm}
>
<Icon icon={variant.icon.confirm} className={style.buttonIcon} />
<I18n {...variant.label.confirm} />
</Button>
]}
title={<div>
<Icon icon={variant.icon.title} />
<span className={style.modalTitle}>
<I18n
id={variant.label.title.id}
params={props}
fallback={variant.label.title.fallback(props)}
/>
</span>
</div>}
onRequestClose={props.onAbort}
type={variant.style}
isOpen
autoFocus
theme={undefined as any}
style={undefined as any}
>
<div className={style.modalContents}>
<Diagram
phase={PublishingPhase.START}
sourceWorkspaceName={props.sourceWorkspaceName}
targetWorkspaceName={props.targetWorkspaceName}
numberOfChanges={props.numberOfChanges}
/>
<I18n
id={variant.label.message.id}
params={props}
fallback={variant.label.message.fallback(props)}
/>
</div>
</Dialog>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
import {ConfirmationDialog} from './ConfirmationDialog';
import {ProcessIndicator} from './ProcessIndicator';
import {ResultDialog} from './ResultDialog';
import {PublishAllConfirmationDialog} from './PublishAllConfirmationDialog';

const {
publishableNodesSelector,
Expand All @@ -46,6 +47,7 @@ type PublishingDialogHandlers = {
confirm: () => void;
retry: () => void;
acknowledge: () => void;
start: (mode: PublishingMode, scope: PublishingScope) => void;
}

type PublishingDialogProps =
Expand All @@ -65,6 +67,11 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
props.acknowledge();
}, []);

const handlePublishAllClick = React.useCallback(() => {
props.start(PublishingMode.PUBLISH, PublishingScope.SITE);
props.confirm();
}, []);

if (props.publishingState === null) {
return null;
}
Expand Down Expand Up @@ -114,6 +121,19 @@ const PublishingDialog: React.FC<PublishingDialogProps> = (props) => {
onAcknowledge={handleAcknowledge}
/>
);
case PublishingPhase.PARTIALLYFAILED:
return (
<PublishAllConfirmationDialog
mode={props.publishingState.mode}
scope={props.publishingState.scope}
scopeTitle={props.scopeTitle}
sourceWorkspaceName={props.sourceWorkspaceName}
targetWorkspaceName={props.targetWorkspaceName}
numberOfChanges={props.numberOfChanges}
onAbort={handleCancel}
onConfirm={handlePublishAllClick}
/>
);
}
};

Expand Down Expand Up @@ -160,5 +180,6 @@ export default connect((state: GlobalState): PublishingDialogProperties => {
confirm: (actions as any).CR.Publishing.confirm,
cancel: (actions as any).CR.Publishing.cancel,
retry: (actions as any).CR.Publishing.retry,
acknowledge: (actions as any).CR.Publishing.acknowledge
acknowledge: (actions as any).CR.Publishing.acknowledge,
start: (actions as any).CR.Publishing.start
})(PublishingDialog);

0 comments on commit c1fe4f5

Please sign in to comment.