Skip to content

Commit c0ffa59

Browse files
authored
Merge pull request #51 from sitegeist/neos9
Neos9
2 parents 3cbd9fb + 6f4b9a7 commit c0ffa59

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+743
-6586
lines changed

.github/workflows/build.yml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ on:
44
push:
55
branches:
66
- 'main'
7-
pull_request: ~
7+
- 'neos9'
8+
pull_request:
9+
branches:
10+
- 'main'
11+
- 'neos9'
812

913
jobs:
1014
test:
@@ -13,11 +17,8 @@ jobs:
1317
strategy:
1418
fail-fast: false
1519
matrix:
16-
php-versions: ['8.1']
17-
neos-versions: ['7.3', '8.3']
18-
include:
19-
- php-versions: '8.2'
20-
neos-versions: '8.3'
20+
php-versions: ['8.2', '8.3']
21+
neos-versions: ['9.0.0-beta19']
2122
runs-on: ubuntu-latest
2223

2324
env:
@@ -44,7 +45,7 @@ jobs:
4445
composer config --no-plugins allow-plugins.neos/composer-plugin true
4546
4647
- name: Checkout
47-
uses: actions/checkout@v2
48+
uses: actions/checkout@v4
4849
with:
4950
path: ${{ env.NEOS_FOLDER }}${{ env.DIST_FOLDER }}
5051

@@ -56,7 +57,7 @@ jobs:
5657
5758
- name: Cache Composer packages
5859
id: composer-cache
59-
uses: actions/cache@v2
60+
uses: actions/cache@v4
6061
with:
6162
path: |
6263
~/.cache/composer
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\LostInTranslation\Command;
6+
7+
use Neos\ContentGraph\DoctrineDbalAdapter\Domain\Repository\ContentSubgraph;
8+
use Neos\ContentRepository\Core\ContentRepository;
9+
use Neos\ContentRepository\Core\DimensionSpace\DimensionSpacePoint;
10+
use Neos\ContentRepository\Core\DimensionSpace\OriginDimensionSpacePoint;
11+
use Neos\ContentRepository\Core\Feature\NodeVariation\Command\CreateNodeVariant;
12+
use Neos\ContentRepository\Core\Projection\ContentGraph\AbsoluteNodePath;
13+
use Neos\ContentRepository\Core\Projection\ContentGraph\Filter\FindChildNodesFilter;
14+
use Neos\ContentRepository\Core\Projection\ContentGraph\Node;
15+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
16+
use Neos\ContentRepository\Core\SharedModel\ContentRepository\ContentRepositoryId;
17+
use Neos\ContentRepository\Core\SharedModel\Workspace\WorkspaceName;
18+
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
19+
use Neos\Flow\Cli\CommandController;
20+
use Neos\Flow\Annotations as Flow;
21+
use Neos\Flow\Security\Context;
22+
23+
class LostInTranslationCommandController extends CommandController
24+
{
25+
#[Flow\InjectConfiguration(path:'nodeTranslation.languageDimensionName')]
26+
public string $languageDimensionName;
27+
28+
#[Flow\Inject]
29+
public ContentRepositoryRegistry $contentRepositoryRegistry;
30+
31+
#[Flow\Inject]
32+
public Context $securityContext;
33+
34+
public function translateCommand(string $source, string $target, string $contentRepository = 'default', string $workspace = 'live', string $nodePath = '/<Neos.Neos:Sites>'): void
35+
{
36+
$cr = $this->contentRepositoryRegistry->get(ContentRepositoryId::fromString($contentRepository));
37+
38+
$workspaceName = WorkspaceName::fromString($workspace);
39+
40+
if ($cr->findWorkspaceByName($workspaceName) === null) {
41+
$this->outputLine("workspace not fround");
42+
$this->quit(1);
43+
}
44+
45+
$graph = $cr->getContentGraph($workspaceName);
46+
$originSubgraph = $graph->getSubgraph(DimensionSpacePoint::fromArray([$this->languageDimensionName => $source]), VisibilityConstraints::withoutRestrictions());
47+
$targetSubgraph = $graph->getSubgraph(DimensionSpacePoint::fromArray([$this->languageDimensionName => $target]), VisibilityConstraints::withoutRestrictions());
48+
49+
$start = $originSubgraph->findNodeByAbsolutePath(AbsoluteNodePath::fromString($nodePath));
50+
51+
if ($start === null) {
52+
$this->outputLine("No node found for path {$nodePath}");
53+
$this->quit(1);
54+
}
55+
$this->translateNodeRecursive($cr, $start, $originSubgraph, $targetSubgraph);
56+
}
57+
58+
public function translateNodeRecursive(ContentRepository $cr, Node $originNode, ContentSubgraph $originSubgraph, ContentSubgraph $targetSubgraph): void
59+
{
60+
$targetNode = $targetSubgraph->findNodeById($originNode->aggregateId);
61+
if ($targetNode === null) {
62+
$cr->handle(CreateNodeVariant::create(
63+
$originSubgraph->getWorkspaceName(),
64+
$originNode->aggregateId,
65+
OriginDimensionSpacePoint::fromDimensionSpacePoint($originNode->dimensionSpacePoint),
66+
OriginDimensionSpacePoint::fromDimensionSpacePoint($targetSubgraph->getDimensionSpacePoint())
67+
));
68+
}
69+
foreach ($originSubgraph->findChildNodes($originNode->aggregateId, FindChildNodesFilter::create())->getIterator() as $childNode) {
70+
$this->translateNodeRecursive($cr, $childNode, $originSubgraph, $targetSubgraph);
71+
}
72+
}
73+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\LostInTranslation\ContentRepository\CommandHook;
6+
7+
use Neos\ContentRepository\Core\CommandHandler\CommandHookInterface;
8+
use Neos\ContentRepository\Core\CommandHandler\CommandInterface;
9+
use Neos\ContentRepository\Core\CommandHandler\Commands;
10+
use Neos\ContentRepository\Core\Dimension\ContentDimension;
11+
use Neos\ContentRepository\Core\EventStore\PublishedEvents;
12+
use Neos\ContentRepository\Core\Feature\NodeModification\Command\SetNodeProperties;
13+
use Neos\ContentRepository\Core\Feature\NodeModification\Dto\PropertyValuesToWrite;
14+
use Neos\ContentRepository\Core\Feature\NodeVariation\Command\CreateNodeVariant;
15+
use Neos\ContentRepository\Core\NodeType\NodeTypeManager;
16+
use Neos\ContentRepository\Core\Projection\ContentGraph\ContentGraphReadModelInterface;
17+
use Neos\ContentRepository\Core\Projection\ContentGraph\VisibilityConstraints;
18+
use Sitegeist\LostInTranslation\Domain\Directive\DimensionValueDirectiveFactory;
19+
use Sitegeist\LostInTranslation\Domain\Directive\NodeTypeTranslationDirectiveFactory;
20+
use Sitegeist\LostInTranslation\Domain\TranslationServiceInterface;
21+
22+
final class TranslationCommandHook implements CommandHookInterface
23+
{
24+
public function __construct(
25+
private bool $enabled,
26+
private readonly ContentGraphReadModelInterface $contentGraphReadModel,
27+
private readonly NodeTypeManager $nodeTypeManager,
28+
private readonly NodeTypeTranslationDirectiveFactory $nodeTypeTranslationDirectiveFactory,
29+
private readonly DimensionValueDirectiveFactory $dimensionValueDirectiveFactory,
30+
private readonly TranslationServiceInterface $translationService,
31+
private readonly ContentDimension $languageDimension,
32+
) {
33+
}
34+
35+
public function onBeforeHandle(CommandInterface $command): CommandInterface
36+
{
37+
if ($this->enabled === false) {
38+
return $command;
39+
}
40+
41+
return $command;
42+
}
43+
44+
public function onAfterHandle(CommandInterface $command, PublishedEvents $events): Commands
45+
{
46+
if ($this->enabled === false) {
47+
return Commands::createEmpty();
48+
}
49+
50+
if ($command instanceof CreateNodeVariant) {
51+
return $this->createNodeVariantCommandWasHandled($command);
52+
} else {
53+
return Commands::createEmpty();
54+
}
55+
}
56+
57+
public function createNodeVariantCommandWasHandled(CreateNodeVariant $command): Commands
58+
{
59+
$sourceLanguageDirective = $this->dimensionValueDirectiveFactory->tryCreateForDimensionAndOriginDimensionSpacePoint(
60+
$this->languageDimension,
61+
$command->sourceOrigin
62+
);
63+
$targetLanguageDirective = $this->dimensionValueDirectiveFactory->tryCreateForDimensionAndOriginDimensionSpacePoint(
64+
$this->languageDimension,
65+
$command->targetOrigin
66+
);
67+
68+
$sourceDeeplLanguage = $sourceLanguageDirective?->deeplSourceId;
69+
$targetDeeplLanguage = $targetLanguageDirective?->deeplTargetId;
70+
71+
if ($sourceDeeplLanguage === null || $targetDeeplLanguage === null) {
72+
return Commands::createEmpty();
73+
}
74+
75+
$command->targetOrigin->getCoordinate($this->languageDimension->id);
76+
$sourceNode = $this->contentGraphReadModel
77+
->getContentGraph($command->workspaceName)
78+
->getSubgraph($command->sourceOrigin->toDimensionSpacePoint(), VisibilityConstraints::withoutRestrictions())
79+
->findNodeById($command->nodeAggregateId);
80+
if ($sourceNode === null) {
81+
return Commands::createEmpty();
82+
}
83+
84+
$nodeType = $this->nodeTypeManager->getNodeType($sourceNode->nodeTypeName);
85+
if ($nodeType === null) {
86+
return Commands::createEmpty();
87+
}
88+
89+
$translationDirective = $this->nodeTypeTranslationDirectiveFactory->createForNodeType($nodeType);
90+
91+
if ($translationDirective->enabled === false) {
92+
return Commands::createEmpty();
93+
}
94+
95+
$propertiesToTranslate = [];
96+
foreach ($translationDirective->translatablePropertyNames as $translatablePropertyName) {
97+
if ($sourceNode->hasProperty($translatablePropertyName)) {
98+
$sourceValue = $sourceNode->getProperty($translatablePropertyName);
99+
if (!empty($sourceValue)) {
100+
$propertiesToTranslate[$translatablePropertyName->value] = $sourceValue;
101+
}
102+
}
103+
}
104+
105+
if (empty($propertiesToTranslate)) {
106+
return Commands::createEmpty();
107+
}
108+
109+
$translatedProperties = $this->translationService->translate(
110+
$propertiesToTranslate,
111+
$targetDeeplLanguage,
112+
$sourceDeeplLanguage,
113+
);
114+
115+
if (empty($translatedProperties)) {
116+
return Commands::createEmpty();
117+
}
118+
119+
$newCommand = SetNodeProperties::create(
120+
$command->workspaceName,
121+
$command->nodeAggregateId,
122+
$command->targetOrigin,
123+
PropertyValuesToWrite::fromArray($translatedProperties)
124+
);
125+
126+
return Commands::fromArray([$newCommand]);
127+
}
128+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Sitegeist\LostInTranslation\ContentRepository\CommandHook;
6+
7+
use Neos\ContentRepository\Core\CommandHandler\CommandHookInterface;
8+
use Neos\ContentRepository\Core\Dimension\ContentDimension;
9+
use Neos\ContentRepository\Core\Dimension\ContentDimensionId;
10+
use Neos\ContentRepository\Core\Factory\CommandHookFactoryInterface;
11+
use Neos\ContentRepository\Core\Factory\CommandHooksFactoryDependencies;
12+
use Neos\ContentRepositoryRegistry\ContentRepositoryRegistry;
13+
use Neos\Flow\Annotations as Flow;
14+
use Sitegeist\LostInTranslation\Domain\Directive\DimensionValueDirectiveFactory;
15+
use Sitegeist\LostInTranslation\Domain\Directive\NodeTypeTranslationDirectiveFactory;
16+
use Sitegeist\LostInTranslation\Domain\TranslationServiceInterface;
17+
18+
class TranslationCommandHookFactory implements CommandHookFactoryInterface
19+
{
20+
#[Flow\InjectConfiguration(path:'nodeTranslation.enabled')]
21+
public bool $enabled = false;
22+
23+
#[Flow\InjectConfiguration(path:'nodeTranslation.languageDimensionName')]
24+
public string $languageDimensionName;
25+
26+
public function __construct(
27+
protected readonly ContentRepositoryRegistry $contentRepositoryRegistry,
28+
protected readonly NodeTypeTranslationDirectiveFactory $translatablePropertyNamesFactory,
29+
protected readonly TranslationServiceInterface $translationService,
30+
) {
31+
}
32+
33+
public function build(CommandHooksFactoryDependencies $commandHooksFactoryDependencies): CommandHookInterface
34+
{
35+
$languageDimension = $commandHooksFactoryDependencies->contentDimensionSource->getDimension(
36+
new ContentDimensionId($this->languageDimensionName)
37+
);
38+
39+
if ($languageDimension instanceof ContentDimension) {
40+
return new TranslationCommandHook(
41+
$this->enabled,
42+
$commandHooksFactoryDependencies->contentGraphReadModel,
43+
$commandHooksFactoryDependencies->nodeTypeManager,
44+
$this->translatablePropertyNamesFactory,
45+
new DimensionValueDirectiveFactory(),
46+
$this->translationService,
47+
$languageDimension
48+
);
49+
} else {
50+
throw new \Exception(sprintf('Lamguage dimension %s was nou found in content repository %s', $this->languageDimensionName, $commandHooksFactoryDependencies->contentRepositoryId->value));
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)