Skip to content

Commit 147a733

Browse files
authored
Merge pull request #3769 from neos/feature/conflict-resolution-03/rebase-during-publish
FEATURE: Integrate conflict resolution with publish/discard dialog workflow
2 parents aff0cd7 + 68fdde5 commit 147a733

File tree

27 files changed

+871
-357
lines changed

27 files changed

+871
-357
lines changed

Classes/Application/PublishChangesInDocument.php renamed to Classes/Application/PublishChangesInDocument/PublishChangesInDocumentCommand.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,43 @@
1212

1313
declare(strict_types=1);
1414

15-
namespace Neos\Neos\Ui\Application;
15+
namespace Neos\Neos\Ui\Application\PublishChangesInDocument;
1616

17+
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
1718
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
1819
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
1920
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
2021
use Neos\Flow\Annotations as Flow;
2122

2223
/**
23-
* The application layer level command DTO to communicate publication of all changes recorded for a given document
24+
* The application layer level command DTO to communicate publication of
25+
* all changes recorded for a given document
2426
*
2527
* @internal for communication within the Neos UI only
2628
*/
2729
#[Flow\Proxy(false)]
28-
final readonly class PublishChangesInDocument
30+
final readonly class PublishChangesInDocumentCommand
2931
{
3032
public function __construct(
3133
public ContentRepositoryId $contentRepositoryId,
3234
public WorkspaceName $workspaceName,
3335
public NodeAggregateId $documentId,
36+
public ?DimensionSpacePoint $preferredDimensionSpacePoint,
3437
) {
3538
}
3639

3740
/**
38-
* @param array<string,string> $values
41+
* @param array{contentRepositoryId:string,workspaceName:string,documentId:string,preferredDimensionSpacePoint?:array<string,string[]>} $values
3942
*/
4043
public static function fromArray(array $values): self
4144
{
4245
return new self(
4346
ContentRepositoryId::fromString($values['contentRepositoryId']),
4447
WorkspaceName::fromString($values['workspaceName']),
4548
NodeAggregateId::fromString($values['documentId']),
49+
isset($values['preferredDimensionSpacePoint']) && !empty($values['preferredDimensionSpacePoint'])
50+
? DimensionSpacePoint::fromLegacyDimensionArray($values['preferredDimensionSpacePoint'])
51+
: null,
4652
);
4753
}
4854
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Neos.Neos package.
5+
*
6+
* (c) Contributors of the Neos Project - www.neos.io
7+
*
8+
* This package is Open Source Software. For the full copyright and license
9+
* information, please view the LICENSE file which was distributed with this
10+
* source code.
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Neos\Neos\Ui\Application\PublishChangesInDocument;
16+
17+
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed;
18+
use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateCurrentlyDoesNotExist;
19+
use Neos\ContentRepository\Core\SharedModel\Exception\NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint;
20+
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
21+
use Neos\Flow\Annotations as Flow;
22+
use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface;
23+
use Neos\Neos\Domain\Service\WorkspacePublishingService;
24+
use Neos\Neos\Ui\Application\Shared\ConflictsOccurred;
25+
use Neos\Neos\Ui\Application\Shared\PublishSucceeded;
26+
use Neos\Neos\Ui\Controller\TranslationTrait;
27+
use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory;
28+
29+
/**
30+
* The application layer level command handler to perform publication of
31+
* all changes recorded for a given document
32+
*
33+
* @internal for communication within the Neos UI only
34+
*/
35+
#[Flow\Scope("singleton")]
36+
final class PublishChangesInDocumentCommandHandler
37+
{
38+
use TranslationTrait;
39+
40+
#[Flow\Inject]
41+
protected ContentRepositoryRegistry $contentRepositoryRegistry;
42+
43+
#[Flow\Inject]
44+
protected WorkspacePublishingService $workspacePublishingService;
45+
46+
#[Flow\Inject]
47+
protected NodeLabelGeneratorInterface $nodeLabelGenerator;
48+
49+
/**
50+
* @throws NodeAggregateCurrentlyDoesNotExist
51+
*/
52+
public function handle(
53+
PublishChangesInDocumentCommand $command
54+
): PublishSucceeded|ConflictsOccurred {
55+
try {
56+
$publishingResult = $this->workspacePublishingService->publishChangesInDocument(
57+
$command->contentRepositoryId,
58+
$command->workspaceName,
59+
$command->documentId
60+
);
61+
62+
$workspace = $this->contentRepositoryRegistry->get($command->contentRepositoryId)->findWorkspaceByName(
63+
$command->workspaceName
64+
);
65+
66+
return new PublishSucceeded(
67+
numberOfAffectedChanges: $publishingResult->numberOfPublishedChanges,
68+
baseWorkspaceName: $workspace?->baseWorkspaceName?->value
69+
);
70+
} catch (NodeAggregateCurrentlyDoesNotExist $e) {
71+
throw new \RuntimeException(
72+
$this->getLabel('NodeNotPublishedMissingParentNode'),
73+
1705053430,
74+
$e
75+
);
76+
} catch (NodeAggregateDoesCurrentlyNotCoverDimensionSpacePoint $e) {
77+
throw new \RuntimeException(
78+
$this->getLabel('NodeNotPublishedParentNodeNotInCurrentDimension'),
79+
1705053432,
80+
$e
81+
);
82+
} catch (WorkspaceRebaseFailed $e) {
83+
$conflictsFactory = new ConflictsFactory(
84+
contentRepository: $this->contentRepositoryRegistry
85+
->get($command->contentRepositoryId),
86+
nodeLabelGenerator: $this->nodeLabelGenerator,
87+
workspaceName: $command->workspaceName,
88+
preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint
89+
);
90+
91+
return new ConflictsOccurred(
92+
conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e)
93+
);
94+
}
95+
}
96+
}

Classes/Application/PublishChangesInSite.php renamed to Classes/Application/PublishChangesInSite/PublishChangesInSiteCommand.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,43 @@
1212

1313
declare(strict_types=1);
1414

15-
namespace Neos\Neos\Ui\Application;
15+
namespace Neos\Neos\Ui\Application\PublishChangesInSite;
1616

17+
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
1718
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
1819
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
1920
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
2021
use Neos\Flow\Annotations as Flow;
2122

2223
/**
23-
* The application layer level command DTO to communicate publication of all changes recorded for a given site
24+
* The application layer level command DTO to communicate publication of
25+
* all changes recorded for a given site
2426
*
2527
* @internal for communication within the Neos UI only
2628
*/
2729
#[Flow\Proxy(false)]
28-
final readonly class PublishChangesInSite
30+
final readonly class PublishChangesInSiteCommand
2931
{
3032
public function __construct(
3133
public ContentRepositoryId $contentRepositoryId,
3234
public WorkspaceName $workspaceName,
3335
public NodeAggregateId $siteId,
36+
public ?DimensionSpacePoint $preferredDimensionSpacePoint,
3437
) {
3538
}
3639

3740
/**
38-
* @param array<string,string> $values
41+
* @param array{contentRepositoryId:string,workspaceName:string,siteId:string,preferredDimensionSpacePoint?:array<string,string[]>} $values
3942
*/
4043
public static function fromArray(array $values): self
4144
{
4245
return new self(
4346
ContentRepositoryId::fromString($values['contentRepositoryId']),
4447
WorkspaceName::fromString($values['workspaceName']),
4548
NodeAggregateId::fromString($values['siteId']),
49+
isset($values['preferredDimensionSpacePoint']) && !empty($values['preferredDimensionSpacePoint'])
50+
? DimensionSpacePoint::fromLegacyDimensionArray($values['preferredDimensionSpacePoint'])
51+
: null,
4652
);
4753
}
4854
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Neos.Neos.Ui package.
5+
*
6+
* (c) Contributors of the Neos Project - www.neos.io
7+
*
8+
* This package is Open Source Software. For the full copyright and license
9+
* information, please view the LICENSE file which was distributed with this
10+
* source code.
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Neos\Neos\Ui\Application\PublishChangesInSite;
16+
17+
use Neos\ContentRepository\Core\Feature\WorkspaceRebase\Exception\WorkspaceRebaseFailed;
18+
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
19+
use Neos\Flow\Annotations as Flow;
20+
use Neos\Neos\Domain\NodeLabel\NodeLabelGeneratorInterface;
21+
use Neos\Neos\Domain\Service\WorkspacePublishingService;
22+
use Neos\Neos\Ui\Application\Shared\ConflictsOccurred;
23+
use Neos\Neos\Ui\Application\Shared\PublishSucceeded;
24+
use Neos\Neos\Ui\Infrastructure\ContentRepository\ConflictsFactory;
25+
26+
/**
27+
* The application layer level command handler to perform publication of
28+
* all changes recorded for a given site
29+
*
30+
* @internal for communication within the Neos UI only
31+
*/
32+
#[Flow\Scope("singleton")]
33+
final class PublishChangesInSiteCommandHandler
34+
{
35+
#[Flow\Inject]
36+
protected ContentRepositoryRegistry $contentRepositoryRegistry;
37+
38+
#[Flow\Inject]
39+
protected WorkspacePublishingService $workspacePublishingService;
40+
41+
#[Flow\Inject]
42+
protected NodeLabelGeneratorInterface $nodeLabelGenerator;
43+
44+
public function handle(
45+
PublishChangesInSiteCommand $command
46+
): PublishSucceeded|ConflictsOccurred {
47+
try {
48+
$publishingResult = $this->workspacePublishingService->publishChangesInSite(
49+
$command->contentRepositoryId,
50+
$command->workspaceName,
51+
$command->siteId
52+
);
53+
54+
$workspace = $this->contentRepositoryRegistry->get($command->contentRepositoryId)->findWorkspaceByName(
55+
$command->workspaceName
56+
);
57+
58+
return new PublishSucceeded(
59+
numberOfAffectedChanges: $publishingResult->numberOfPublishedChanges,
60+
baseWorkspaceName: $workspace?->baseWorkspaceName?->value
61+
);
62+
} catch (WorkspaceRebaseFailed $e) {
63+
$conflictsFactory = new ConflictsFactory(
64+
contentRepository: $this->contentRepositoryRegistry
65+
->get($command->contentRepositoryId),
66+
nodeLabelGenerator: $this->nodeLabelGenerator,
67+
workspaceName: $command->workspaceName,
68+
preferredDimensionSpacePoint: $command->preferredDimensionSpacePoint
69+
);
70+
71+
return new ConflictsOccurred(
72+
conflicts: $conflictsFactory->fromWorkspaceRebaseFailed($e)
73+
);
74+
}
75+
}
76+
}

Classes/Application/SyncWorkspace/Conflict.php renamed to Classes/Application/Shared/Conflict.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@
1212

1313
declare(strict_types=1);
1414

15-
namespace Neos\Neos\Ui\Application\SyncWorkspace;
15+
namespace Neos\Neos\Ui\Application\Shared;
1616

17+
use Neos\ContentRepository\Core\SharedModel\Node\NodeAggregateId;
1718
use Neos\Flow\Annotations as Flow;
1819

1920
/**
@@ -25,6 +26,7 @@
2526
final readonly class Conflict implements \JsonSerializable
2627
{
2728
public function __construct(
29+
public string $key,
2830
public ?IconLabel $affectedSite,
2931
public ?IconLabel $affectedDocument,
3032
public ?IconLabel $affectedNode,
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Neos.Neos.Ui package.
5+
*
6+
* (c) Contributors of the Neos Project - www.neos.io
7+
*
8+
* This package is Open Source Software. For the full copyright and license
9+
* information, please view the LICENSE file which was distributed with this
10+
* source code.
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Neos\Neos\Ui\Application\Shared;
16+
17+
use Neos\Flow\Annotations as Flow;
18+
19+
/**
20+
* @internal for communication within the Neos UI only
21+
*/
22+
#[Flow\Proxy(false)]
23+
final readonly class Conflicts implements \JsonSerializable, \Countable
24+
{
25+
/** @var Conflict[] */
26+
private array $items;
27+
28+
public function __construct(Conflict ...$items)
29+
{
30+
$this->items = array_values($items);
31+
}
32+
33+
public function jsonSerialize(): mixed
34+
{
35+
return $this->items;
36+
}
37+
38+
public function count(): int
39+
{
40+
return count($this->items);
41+
}
42+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Neos.Neos.Ui package.
5+
*
6+
* (c) Contributors of the Neos Project - www.neos.io
7+
*
8+
* This package is Open Source Software. For the full copyright and license
9+
* information, please view the LICENSE file which was distributed with this
10+
* source code.
11+
*/
12+
13+
declare(strict_types=1);
14+
15+
namespace Neos\Neos\Ui\Application\Shared;
16+
17+
use Neos\Flow\Annotations as Flow;
18+
19+
/**
20+
* @internal for communication within the Neos UI only
21+
*/
22+
#[Flow\Proxy(false)]
23+
final readonly class ConflictsOccurred implements \JsonSerializable
24+
{
25+
public function __construct(
26+
public readonly Conflicts $conflicts
27+
) {
28+
}
29+
30+
public function jsonSerialize(): mixed
31+
{
32+
return get_object_vars($this);
33+
}
34+
}

Classes/Application/SyncWorkspace/IconLabel.php renamed to Classes/Application/Shared/IconLabel.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
declare(strict_types=1);
1414

15-
namespace Neos\Neos\Ui\Application\SyncWorkspace;
15+
namespace Neos\Neos\Ui\Application\Shared;
1616

1717
use Neos\Flow\Annotations as Flow;
1818

0 commit comments

Comments
 (0)