Skip to content

Commit

Permalink
Merge pull request #20 from sitegeist/TASK/improveSyncProcess
Browse files Browse the repository at this point in the history
TASK: Improve sync process
  • Loading branch information
mficzel authored May 3, 2023
2 parents 1e17e1e + 66b94fd commit 390a79a
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 60 deletions.
171 changes: 112 additions & 59 deletions Classes/ContentRepository/NodeTranslationService.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,37 @@

namespace Sitegeist\LostInTranslation\ContentRepository;

use InvalidArgumentException;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Model\Workspace;
use Neos\ContentRepository\Domain\Repository\NodeDataRepository;
use Neos\ContentRepository\Domain\Service\Context;
use Neos\ContentRepository\Domain\Service\ContextFactory;
use Neos\ContentRepository\Exception\NodeException;
use Neos\Flow\Annotations as Flow;
use Neos\ContentRepository\Domain\Model\NodeInterface;
use Neos\ContentRepository\Domain\Service\Context;
use Neos\Neos\Service\PublishingService;
use Sitegeist\LostInTranslation\Domain\TranslationServiceInterface;

/**
* @Flow\Scope("singleton")
*/
class NodeTranslationService
{
protected const TRANSLATION_STRATEGY_ONCE = 'once';
protected const TRANSLATION_STRATEGY_SYNC = 'sync';
protected const TRANSLATION_STRATEGY_NONE = 'none';
public const TRANSLATION_STRATEGY_ONCE = 'once';
public const TRANSLATION_STRATEGY_SYNC = 'sync';
public const TRANSLATION_STRATEGY_NONE = 'none';

/**
* @Flow\Inject
* @var TranslationServiceInterface
*/
protected $translationService;

/**
* @Flow\Inject
* @var PublishingService
*/
protected $publishingService;

/**
* @Flow\InjectConfiguration(path="nodeTranslation.enabled")
* @var bool
Expand Down Expand Up @@ -63,8 +71,20 @@ class NodeTranslationService
protected $contextFactory;

/**
* @param NodeInterface $node
* @param Context $context
* @Flow\Inject
* @var \Neos\Flow\Security\Context
*/
protected $securityContext;

/**
* @Flow\Inject()
* @var NodeDataRepository
*/
protected $nodeDataRepository;

/**
* @param NodeInterface $node
* @param Context $context
* @param $recursive
* @return void
*/
Expand All @@ -81,8 +101,8 @@ public function afterAdoptNode(NodeInterface $node, Context $context, $recursive

$targetDimensionValue = $context->getTargetDimensions()[$this->languageDimensionName];
$languagePreset = $this->contentDimensionConfiguration[$this->languageDimensionName]['presets'][$targetDimensionValue];
$translationStrategy = $languagePreset['options']['translationStrategy'] ?? null;
if (!in_array($translationStrategy, [null, self::TRANSLATION_STRATEGY_ONCE, self::TRANSLATION_STRATEGY_SYNC])) {
$translationStrategy = $languagePreset['options']['translationStrategy'] ?? self::TRANSLATION_STRATEGY_ONCE;
if ($translationStrategy !== self::TRANSLATION_STRATEGY_ONCE) {
return;
}

Expand All @@ -105,39 +125,7 @@ public function afterNodePublish(NodeInterface $node, Workspace $workspace): voi
return;
}

$isAutomaticTranslationEnabledForNodeType = $node->getNodeType()->getConfiguration('options.automaticTranslation') ?? true;
if (!$isAutomaticTranslationEnabledForNodeType) {
return;
}

$nodeSourceDimensionValue = $node->getContext()->getTargetDimensions()[$this->languageDimensionName];
$defaultPreset = $this->contentDimensionConfiguration[$this->languageDimensionName]['defaultPreset'];

if ($nodeSourceDimensionValue !== $defaultPreset) {
return;
}

foreach ($this->contentDimensionConfiguration[$this->languageDimensionName]['presets'] as $presetIdentifier => $languagePreset) {
if ($nodeSourceDimensionValue === $presetIdentifier) {
continue;
}

$translationStrategy = $languagePreset['options']['translationStrategy'] ?? null;
if ($translationStrategy !== self::TRANSLATION_STRATEGY_SYNC) {
continue;
}

$context = $this->getContextForLanguageDimensionAndWorkspaceName($presetIdentifier, $workspace->getName());
if (!$node->isRemoved()) {
$adoptedNode = $context->adoptNode($node);
$this->translateNode($node, $adoptedNode, $context);
} else {
$adoptedNode = $context->getNodeByIdentifier((string) $node->getNodeAggregateIdentifier());
if ($adoptedNode !== null) {
$adoptedNode->setRemoved(true);
}
}
}
$this->syncNode($node);
}

/**
Expand All @@ -149,7 +137,7 @@ public function afterNodePublish(NodeInterface $node, Workspace $workspace): voi
* @param Context $context
* @return void
*/
protected function translateNode(NodeInterface $sourceNode, NodeInterface $targetNode, Context $context): void
public function translateNode(NodeInterface $sourceNode, NodeInterface $targetNode, Context $context): void
{
$propertyDefinitions = $sourceNode->getNodeType()->getProperties();

Expand All @@ -174,14 +162,7 @@ protected function translateNode(NodeInterface $sourceNode, NodeInterface $targe
return;
}

// Sync internal properties
$targetNode->setNodeType($sourceNode->getNodeType());
$targetNode->setHidden($sourceNode->isHidden());
$targetNode->setHiddenInIndex($sourceNode->isHiddenInIndex());
$targetNode->setHiddenBeforeDateTime($sourceNode->getHiddenBeforeDateTime());
$targetNode->setHiddenAfterDateTime($sourceNode->getHiddenAfterDateTime());

$properties = (array)$sourceNode->getProperties();
$properties = (array) $sourceNode->getProperties(true);
$propertiesToTranslate = [];
foreach ($properties as $propertyName => $propertyValue) {
if (empty($propertyValue)) {
Expand Down Expand Up @@ -210,11 +191,12 @@ protected function translateNode(NodeInterface $sourceNode, NodeInterface $targe

if (count($propertiesToTranslate) > 0) {
$translatedProperties = $this->translationService->translate($propertiesToTranslate, $targetLanguage, $sourceLanguage);
$translatedProperties = array_merge($translatedProperties, $properties);
foreach ($translatedProperties as $propertyName => $translatedValue) {
if ($targetNode->getProperty($propertyName) != $translatedValue) {
$targetNode->setProperty($propertyName, $translatedValue);
}
$properties = array_merge($translatedProperties, $properties);
}

foreach ($properties as $propertyName => $propertyValue) {
if ($targetNode->getProperty($propertyName) != $propertyValue) {
$targetNode->setProperty($propertyName, $propertyValue);
}
}
}
Expand All @@ -224,9 +206,9 @@ protected function translateNode(NodeInterface $sourceNode, NodeInterface $targe
* @param string $workspaceName
* @return Context
*/
protected function getContextForLanguageDimensionAndWorkspaceName(string $language, string $workspaceName = 'live'): Context
public function getContextForLanguageDimensionAndWorkspaceName(string $language, string $workspaceName = 'live'): Context
{
$dimensionAndWorkspaceIdentifierHash = md5($language . $workspaceName);
$dimensionAndWorkspaceIdentifierHash = md5(trim($language . $workspaceName));

if (array_key_exists($dimensionAndWorkspaceIdentifierHash, $this->contextFirstLevelCache)) {
return $this->contextFirstLevelCache[$dimensionAndWorkspaceIdentifierHash];
Expand All @@ -236,6 +218,8 @@ protected function getContextForLanguageDimensionAndWorkspaceName(string $langua
array(
'workspaceName' => $workspaceName,
'invisibleContentShown' => true,
'removedContentShown' => true,
'inaccessibleContentShown' => true,
'dimensions' => array(
$this->languageDimensionName => array($language),
),
Expand All @@ -245,4 +229,73 @@ protected function getContextForLanguageDimensionAndWorkspaceName(string $langua
)
);
}

/**
* Checks the requirements if a node can be synchronised and executes the sync.
*
* @param NodeInterface $sourceNode
* @param string $workspaceName
* @return void
*/
public function syncNode(NodeInterface $sourceNode, string $workspaceName = 'live'): void
{
$isAutomaticTranslationEnabledForNodeType = $sourceNode->getNodeType()->getConfiguration('options.automaticTranslation') ?? true;
if (!$isAutomaticTranslationEnabledForNodeType) {
return;
}

$nodeSourceDimensionValue = $sourceNode->getContext()->getTargetDimensions()[$this->languageDimensionName];
$defaultPreset = $this->contentDimensionConfiguration[$this->languageDimensionName]['defaultPreset'];

if ($nodeSourceDimensionValue !== $defaultPreset) {
return;
}

foreach ($this->contentDimensionConfiguration[$this->languageDimensionName]['presets'] as $presetIdentifier => $languagePreset) {
if ($nodeSourceDimensionValue === $presetIdentifier) {
continue;
}

$translationStrategy = $languagePreset['options']['translationStrategy'] ?? null;
if ($translationStrategy !== self::TRANSLATION_STRATEGY_SYNC) {
continue;
}

if (!$sourceNode->isRemoved()) {
$context = $this->getContextForLanguageDimensionAndWorkspaceName($presetIdentifier, $workspaceName);
$context->getFirstLevelNodeCache()->flush();

$targetNode = $context->adoptNode($sourceNode);

// Move node if targetNode has no parent or node parents are not matching
if (!$targetNode->getParent() || ($sourceNode->getParentPath() !== $targetNode->getParentPath())) {
try {
$referenceNode = $context->getNodeByIdentifier($sourceNode->getParent()->getIdentifier());
$targetNode->moveInto($referenceNode);
} catch (InvalidArgumentException $e) {
}
}

// Sync internal properties
$targetNode->setNodeType($sourceNode->getNodeType());
$targetNode->setHidden($sourceNode->isHidden());
$targetNode->setHiddenInIndex($sourceNode->isHiddenInIndex());
$targetNode->setHiddenBeforeDateTime($sourceNode->getHiddenBeforeDateTime());
$targetNode->setHiddenAfterDateTime($sourceNode->getHiddenAfterDateTime());
$targetNode->setIndex($sourceNode->getIndex());

$this->translateNode($sourceNode, $targetNode, $context);

$context->getFirstLevelNodeCache()->flush();
$this->publishingService->publishNode($targetNode);
$this->nodeDataRepository->persistEntities();
} else {
$removeContext = $this->getContextForLanguageDimensionAndWorkspaceName($presetIdentifier, $workspaceName);
$targetNode = $removeContext->getNodeByIdentifier($sourceNode->getIdentifier());
if ($targetNode !== null) {
$targetNode->setRemoved(true);
}
}
}
}
}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ Sitegeist:
languageDimensionName: 'language'
```
To enable automated translations for a language preset, just set `options.translationStrategy` to `once` or `sync`.
To enable automated translations for a language preset, set `options.translationStrategy` to `once`, `sync` or `none`.
The default mode is `once`;

* `once` will translate the node only once when the editor switches the language in the backend while editing this node. This is useful if you want to get an initial translation, but work on the different variants on your own after that.
* `sync` will translate and sync the node every time the node in the default language is published. Thus, it will not make sense to edit the node variant in an automatically translated language using this options, as your changed will be overwritten every time.
* `none` will not translate variants for this dimension.

If a preset of the language dimension uses a locale identifier that is not compatible with DeepL the deeplLanguage can
be configured explicitly for this preset via `options.deeplLanguage`.
Expand Down

0 comments on commit 390a79a

Please sign in to comment.