diff --git a/Classes/Command/TradosCommandController.php b/Classes/Command/TradosCommandController.php index 924168f..f14d31c 100644 --- a/Classes/Command/TradosCommandController.php +++ b/Classes/Command/TradosCommandController.php @@ -23,6 +23,7 @@ */ class TradosCommandController extends CommandController { + /** * @Flow\Inject * @var ExportService @@ -40,25 +41,29 @@ class TradosCommandController extends CommandController * * This command exports a specific site including all content into an XML format. * - * @param string $startingPoint The node with which to start the export, relative to the site node. Optional. + * @param string $startingPoint The node with which to start the export: as identifier or the path relative to the site node. * @param string $sourceLanguage The language to use as base for the export. - * @param string $targetLanguage The target language for the translation, optional. - * @param string $filename Path and filename to the XML file to create. - * @param string $modifiedAfter + * @param string|null $targetLanguage The target language for the translation, optional. + * @param string|null $filename Path and filename to the XML file to create. + * @param string|null $modifiedAfter * @param boolean $ignoreHidden + * @param boolean $excludeChildDocuments * @return void + * @throws \Exception */ - public function exportCommand($startingPoint, $sourceLanguage, $targetLanguage = null, $filename = null, $modifiedAfter = null, $ignoreHidden = true) + public function exportCommand(string $startingPoint, string $sourceLanguage, string $targetLanguage = null, string $filename = null, string $modifiedAfter = null, bool $ignoreHidden = true, bool $excludeChildDocuments = false) { if ($modifiedAfter !== null) { $modifiedAfter = new \DateTime($modifiedAfter); } + $this->exportService->initialize($startingPoint, $sourceLanguage, $targetLanguage, $modifiedAfter, $ignoreHidden, $excludeChildDocuments); + try { if ($filename === null) { - $this->output($this->exportService->exportToString($startingPoint, $sourceLanguage, $targetLanguage, $modifiedAfter, $ignoreHidden)); + $this->output($this->exportService->exportToString()); } else { - $this->exportService->exportToFile($filename, $startingPoint, $sourceLanguage, $targetLanguage, $modifiedAfter, $ignoreHidden); + $this->exportService->exportToFile($filename); $this->outputLine('<success>The tree starting at "/sites/%s" has been exported to "%s".</success>', [$startingPoint, $filename]); } } catch (\Exception $exception) { @@ -73,10 +78,10 @@ public function exportCommand($startingPoint, $sourceLanguage, $targetLanguage = * This command imports translated content from XML. * * @param string $filename Path and filename to the XML file to import. - * @param string $targetLanguage The target language for the translation, optional if included in XML. + * @param string|null $targetLanguage The target language for the translation, optional if included in XML. * @param string $workspace A workspace to import into, optional but recommended */ - public function importCommand($filename, $targetLanguage = null, $workspace = 'live') + public function importCommand(string $filename, string $targetLanguage = null, string $workspace = 'live') { try { $importedLanguage = $this->importService->importFromFile($filename, $workspace, $targetLanguage); diff --git a/Classes/Service/AbstractService.php b/Classes/Service/AbstractService.php new file mode 100644 index 0000000..350910d --- /dev/null +++ b/Classes/Service/AbstractService.php @@ -0,0 +1,52 @@ +<?php +namespace Flownative\Neos\Trados\Service; + +/* + * This file is part of the Flownative.Neos.Trados package. + * + * (c) Flownative GmbH - https://www.flownative.com/ + * + * 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. + */ + +use Neos\Flow\Annotations as Flow; + +class AbstractService +{ + /** + * @var string + */ + const SUPPORTED_FORMAT_VERSION = '1.0'; + + /** + * @Flow\InjectConfiguration(path = "languageDimension") + * @var string + */ + protected $languageDimension; + + /** + * @Flow\Inject + * @var \Neos\Neos\Domain\Service\ContentContextFactory + */ + protected $contentContextFactory; + + /** + * @Flow\Inject + * @var \Neos\Flow\Security\Context + */ + protected $securityContext; + + /** + * @Flow\Inject + * @var \Neos\Neos\Domain\Repository\SiteRepository + */ + protected $siteRepository; + + /** + * @Flow\Inject + * @var \Neos\ContentRepository\Domain\Repository\WorkspaceRepository + */ + protected $workspaceRepository; +} diff --git a/Classes/Service/ExportService.php b/Classes/Service/ExportService.php index 244ac50..2eeecca 100644 --- a/Classes/Service/ExportService.php +++ b/Classes/Service/ExportService.php @@ -13,8 +13,8 @@ use Neos\ContentRepository\Domain\Model\NodeData; use Neos\ContentRepository\Domain\Model\NodeInterface; -use Neos\ContentRepository\Domain\Service\ContextFactoryInterface; -use Neos\ContentRepository\Domain\Utility\NodePaths; +use Neos\ContentRepository\Domain\Model\Workspace; +use Neos\ContentRepository\Domain\Repository\NodeDataRepository; use Neos\Flow\Annotations as Flow; use Neos\Neos\Domain\Model\Site; use Neos\Neos\Domain\Service\ContentContext; @@ -24,18 +24,19 @@ * * @Flow\Scope("singleton") */ -class ExportService +class ExportService extends AbstractService { /** + * @Flow\InjectConfiguration(path = "export.workspace") * @var string */ - const SUPPORTED_FORMAT_VERSION = '1.0'; + protected string $workspaceName; /** - * @Flow\InjectConfiguration(path = "languageDimension") + * @Flow\InjectConfiguration(path = "export.documentTypeFilter") * @var string */ - protected $languageDimension; + protected string $documentTypeFilter; /** * The XMLWriter that is used to construct the export. @@ -46,66 +47,141 @@ class ExportService /** * @Flow\Inject - * @var ContextFactoryInterface + * @var \Neos\ContentRepository\Domain\Service\NodeTypeManager */ - protected $contextFactory; + protected $nodeTypeManager; /** - * Doctrine's Entity Manager. Note that "ObjectManager" is the name of the related - * interface ... - * * @Flow\Inject - * @var \Doctrine\Common\Persistence\ObjectManager + * @var NodeDataRepository */ - protected $entityManager; + protected $nodeDataRepository; /** * @Flow\Inject - * @var \Neos\ContentRepository\Domain\Service\NodeTypeManager + * @var \Neos\ContentRepository\Domain\Service\ContentDimensionCombinator */ - protected $nodeTypeManager; + protected $contentDimensionCombinator; /** - * @Flow\Inject - * @var \Neos\Neos\Domain\Repository\SiteRepository + * @var ContentContext + * */ - protected $siteRepository; + protected ContentContext $contentContext; /** - * @Flow\Inject - * @var \Neos\ContentRepository\Domain\Repository\NodeDataRepository + * @var string */ - protected $nodeDataRepository; + protected string $startingPoint; /** - * @Flow\Inject - * @var \Neos\Flow\Security\Context + * @var NodeInterface */ - protected $securityContext; + protected NodeInterface $startingPointNode; /** - * @Flow\Inject - * @var \Neos\ContentRepository\Domain\Service\ContentDimensionCombinator + * @var Site */ - protected $contentDimensionCombinator; + protected Site $site; + + /** + * @var string + */ + protected string $sourceLanguage; + + /** + * @var string|null + */ + protected ?string $targetLanguage; + + /** + * @var \DateTime|null + */ + protected ?\DateTime $modifiedAfter; + + /** + * @var bool + */ + protected bool $ignoreHidden; + + /** + * @var bool + */ + protected bool $excludeChildDocuments; + + /** + * @var int + */ + protected int $depth; /** - * Fetches the site with the given name and exports it into XML. - * * @param string $startingPoint * @param string $sourceLanguage - * @param string $targetLanguage - * @param \DateTime $modifiedAfter - * @param boolean $ignoreHidden + * @param string|null $targetLanguage + * @param \DateTime|null $modifiedAfter + * @param bool $ignoreHidden + * @param string $documentTypeFilter + * @param int $depth + */ + public function initialize( + string $startingPoint, + string $sourceLanguage, + string $targetLanguage = null, + \DateTime $modifiedAfter = null, + bool $ignoreHidden = true, + bool $excludeChildDocuments = false, + string $documentTypeFilter = 'Neos.Neos:Document', + int $depth = 0 + ) { + $this->startingPoint = $startingPoint; + $this->sourceLanguage = $sourceLanguage; + $this->targetLanguage = $targetLanguage; + $this->modifiedAfter = $modifiedAfter; + $this->ignoreHidden = $ignoreHidden; + $this->excludeChildDocuments = $excludeChildDocuments; + $this->documentTypeFilter = $documentTypeFilter; + $this->depth = $depth; + + /** @var ContentContext $contentContext */ + $contentContext = $this->contentContextFactory->create([ + 'workspaceName' => $this->workspaceName, + 'invisibleContentShown' => !$this->ignoreHidden, + 'removedContentShown' => false, + 'inaccessibleContentShown' => !$this->ignoreHidden, + 'dimensions' => current($this->getAllowedContentCombinationsForSourceLanguage($this->sourceLanguage)) + ]); + $this->contentContext = $contentContext; + + $startingPointNode = $this->contentContext->getNodeByIdentifier($startingPoint); + if ($startingPointNode === null) { + $startingPointNode = $this->contentContext->getNode('/sites/' . $this->startingPoint); + if ($startingPointNode === null) { + throw new \RuntimeException(sprintf('Could not find node "%s"', $this->startingPoint), 1473241812); + } + } + + $this->startingPointNode = $startingPointNode; + $pathArray = explode('/', $this->startingPointNode->findNodePath()); + $this->site = $this->siteRepository->findOneByNodeName($pathArray[2]); + + if ($this->workspaceRepository->findOneByName($this->workspaceName) === null) { + throw new \RuntimeException(sprintf('Could not find workspace "%s"', $this->workspaceName), 14732418113); + } + } + + /** + * Fetches the site with the given name and exports it into XML. + * * @return string + * @throws \Exception */ - public function exportToString($startingPoint, $sourceLanguage, $targetLanguage = null, \DateTime $modifiedAfter = null, $ignoreHidden = true) + public function exportToString(): string { $this->xmlWriter = new \XMLWriter(); $this->xmlWriter->openMemory(); $this->xmlWriter->setIndent(true); - $this->export($startingPoint, $sourceLanguage, $targetLanguage, $modifiedAfter, 'live', $ignoreHidden); + $this->exportToXmlWriter(); return $this->xmlWriter->outputMemory(true); } @@ -114,20 +190,16 @@ public function exportToString($startingPoint, $sourceLanguage, $targetLanguage * Export into the given file. * * @param string $pathAndFilename Path to where the export output should be saved to - * @param string $startingPoint - * @param string $sourceLanguage - * @param string $targetLanguage - * @param \DateTime $modifiedAfter - * @param boolean $ignoreHidden * @return void + * @throws \Exception */ - public function exportToFile($pathAndFilename, $startingPoint, $sourceLanguage, $targetLanguage = null, \DateTime $modifiedAfter = null, $ignoreHidden = true) + public function exportToFile(string $pathAndFilename) { $this->xmlWriter = new \XMLWriter(); $this->xmlWriter->openUri($pathAndFilename); $this->xmlWriter->setIndent(true); - $this->export($startingPoint, $sourceLanguage, $targetLanguage, $modifiedAfter, 'live', $ignoreHidden); + $this->exportToXmlWriter(); $this->xmlWriter->flush(); } @@ -135,48 +207,28 @@ public function exportToFile($pathAndFilename, $startingPoint, $sourceLanguage, /** * Export to the XMLWriter. * - * @param string $startingPoint - * @param string $sourceLanguage - * @param string $targetLanguage - * @param \DateTime $modifiedAfter - * @param string $workspaceName - * @param boolean $ignoreHidden * @return void + * @throws \Exception */ - protected function export($startingPoint, $sourceLanguage, $targetLanguage = null, \DateTime $modifiedAfter = null, $workspaceName = 'live', $ignoreHidden = true) + protected function exportToXmlWriter() { - $siteNodeName = current(explode('/', $startingPoint)); - /** @var Site $site */ - $site = $this->siteRepository->findOneByNodeName($siteNodeName); - if ($site === null) { - throw new \RuntimeException(sprintf('No site found for node name "%s"', $siteNodeName), 1473241812); - } - - /** @var ContentContext $contentContext */ - $contentContext = $this->contextFactory->create([ - 'workspaceName' => $workspaceName, - 'currentSite' => $site, - 'invisibleContentShown' => !$ignoreHidden, - 'removedContentShown' => false, - 'inaccessibleContentShown' => !$ignoreHidden, - 'dimensions' => current($this->getAllowedContentCombinationsForSourceLanguage($sourceLanguage)) - ]); - $this->xmlWriter->startDocument('1.0', 'UTF-8'); $this->xmlWriter->startElement('content'); - $this->xmlWriter->writeAttribute('name', $site->getName()); - $this->xmlWriter->writeAttribute('sitePackageKey', $site->getSiteResourcesPackageKey()); - $this->xmlWriter->writeAttribute('workspace', $workspaceName); - $this->xmlWriter->writeAttribute('sourceLanguage', $sourceLanguage); - if ($targetLanguage !== null) { - $this->xmlWriter->writeAttribute('targetLanguage', $targetLanguage); + $this->xmlWriter->writeAttribute('name', $this->site->getName()); + $this->xmlWriter->writeAttribute('sitePackageKey', $this->site->getSiteResourcesPackageKey()); + $this->xmlWriter->writeAttribute('workspace', $this->workspaceName); + $this->xmlWriter->writeAttribute('sourceLanguage', $this->sourceLanguage); + if ($this->targetLanguage !== null) { + $this->xmlWriter->writeAttribute('targetLanguage', $this->targetLanguage); } - if ($modifiedAfter !== null) { - $this->xmlWriter->writeAttribute('modifiedAfter', $targetLanguage); + if ($this->modifiedAfter !== null) { + $this->xmlWriter->writeAttribute('modifiedAfter', $this->modifiedAfter->format('c')); } - $this->exportNodes('/sites/' . $startingPoint, $contentContext, $sourceLanguage, $targetLanguage, $modifiedAfter); + $this->xmlWriter->startElement('nodes'); + $this->exportNodes($this->startingPointNode->findNodePath(), $this->contentContext); + $this->xmlWriter->endElement(); // nodes $this->xmlWriter->endElement(); $this->xmlWriter->endDocument(); @@ -188,15 +240,13 @@ protected function export($startingPoint, $sourceLanguage, $targetLanguage = nul * * @param string $startingPointNodePath path to the root node of the sub-tree to export. The specified node will not be included, only its sub nodes. * @param ContentContext $contentContext - * @param string $sourceLanguage - * @param string $targetLanguage - * @param \DateTime $modifiedAfter * @return void + * @throws \Exception */ - public function exportNodes($startingPointNodePath, ContentContext $contentContext, $sourceLanguage, $targetLanguage = null, \DateTime $modifiedAfter = null) + protected function exportNodes(string $startingPointNodePath, ContentContext $contentContext) { - $this->securityContext->withoutAuthorizationChecks(function () use ($startingPointNodePath, $contentContext, $sourceLanguage, $targetLanguage, $modifiedAfter) { - $nodeDataList = $this->findNodeDataListToExport($startingPointNodePath, $contentContext, $sourceLanguage, $targetLanguage, $modifiedAfter); + $this->securityContext->withoutAuthorizationChecks(function () use ($startingPointNodePath, $contentContext) { + $nodeDataList = $this->findNodeDataListToExport($startingPointNodePath, $contentContext); $this->exportNodeDataList($nodeDataList); }); } @@ -207,25 +257,30 @@ public function exportNodes($startingPointNodePath, ContentContext $contentConte * * @param string $pathStartingPoint Absolute path specifying the starting point * @param ContentContext $contentContext - * @param string $sourceLanguage - * @param string $targetLanguage - * @param \DateTime $modifiedAfter * @return array<NodeData> + * @throws \Neos\Flow\Persistence\Exception\IllegalObjectTypeException */ - protected function findNodeDataListToExport($pathStartingPoint, ContentContext $contentContext, $sourceLanguage, $targetLanguage = null, \DateTime $modifiedAfter = null) + protected function findNodeDataListToExport(string $pathStartingPoint, ContentContext $contentContext): array { - $allowedContentCombinations = $this->getAllowedContentCombinationsForSourceLanguage($sourceLanguage); + $allowedContentCombinations = $this->getAllowedContentCombinationsForSourceLanguage($this->sourceLanguage); $sourceContexts = []; /** @var NodeData[] $nodeDataList */ $nodeDataList = []; foreach ($allowedContentCombinations as $contentDimensions) { + // If exclude-child-documents argument is set to true, only add content child nodes to nodeDataList (recursively), don't descend into document child nodes. + if ($this->excludeChildDocuments === true) { + $node = $contentContext->getNode($pathStartingPoint)->getNodeData(); + $childNodes = $this->getChildNodeDataOnPage($node, $contentContext->getWorkspace(), $contentDimensions, $contentContext->isRemovedContentShown()); + } else { + $childNodes = $this->nodeDataRepository->findByParentAndNodeType($pathStartingPoint, null, $contentContext->getWorkspace(), $contentDimensions, $contentContext->isRemovedContentShown() ? null : false, true); + } $nodeDataList = array_merge( $nodeDataList, [$contentContext->getNode($pathStartingPoint)->getNodeData()], - $this->nodeDataRepository->findByParentAndNodeType($pathStartingPoint, null, $contentContext->getWorkspace(), $contentDimensions, $contentContext->isRemovedContentShown() ? null : false, true) + $childNodes ); - $sourceContexts[] = $this->contextFactory->create([ + $sourceContexts[] = $this->contentContextFactory->create([ 'invisibleContentShown' => $contentContext->isInvisibleContentShown(), 'removedContentShown' => false, 'inaccessibleContentShown' => $contentContext->isInaccessibleContentShown(), @@ -234,11 +289,11 @@ protected function findNodeDataListToExport($pathStartingPoint, ContentContext $ } $uniqueNodeDataList = []; - usort($nodeDataList, function (NodeData $node1, NodeData $node2) use ($sourceLanguage) { - if ($node1->getDimensionValues()[$this->languageDimension][0] === $sourceLanguage) { + usort($nodeDataList, function (NodeData $node1, NodeData $node2) { + if ($node1->getDimensionValues()[$this->languageDimension][0] === $this->sourceLanguage) { return 1; } - if ($node2->getDimensionValues()[$this->languageDimension][0] === $sourceLanguage) { + if ($node2->getDimensionValues()[$this->languageDimension][0] === $this->sourceLanguage) { return -1; } @@ -247,13 +302,13 @@ protected function findNodeDataListToExport($pathStartingPoint, ContentContext $ foreach ($nodeDataList as $nodeData) { $uniqueNodeDataList[$nodeData->getIdentifier()] = $nodeData; } - $nodeDataList = array_filter(array_values($uniqueNodeDataList), function (NodeData $nodeData) use ($sourceContexts, $sourceLanguage) { + $nodeDataList = array_filter(array_values($uniqueNodeDataList), function (NodeData $nodeData) use ($sourceContexts) { /** @var ContentContext $sourceContext */ foreach ($sourceContexts as $sourceContext) { - if ($sourceContext->getDimensions()[$this->languageDimension][0] !== $sourceLanguage) { + if ($sourceContext->getDimensions()[$this->languageDimension][0] !== $this->sourceLanguage) { continue; } - if ($nodeData->getDimensionValues()[$this->languageDimension][0] !== $sourceLanguage) { + if ($nodeData->getDimensionValues()[$this->languageDimension][0] !== $this->sourceLanguage) { // "reload" nodedata in correct dimension $node = $sourceContext->getNodeByIdentifier($nodeData->getIdentifier()); if ($node === null || $node->getNodeData() === null) { @@ -302,28 +357,27 @@ function (NodeData $node1, NodeData $node2) { * * @param array<NodeData> $nodeDataList The nodes to export * @return void The result is written directly into $this->xmlWriter + * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException */ protected function exportNodeDataList(array &$nodeDataList) { - $this->xmlWriter->startElement('nodes'); $this->xmlWriter->writeAttribute('formatVersion', self::SUPPORTED_FORMAT_VERSION); $currentNodeDataIdentifier = null; foreach ($nodeDataList as $nodeData) { $this->writeNode($nodeData, $currentNodeDataIdentifier); } - - $this->xmlWriter->endElement(); } /** * Write a single node into the XML structure * * @param NodeData $nodeData The node data - * @param string $currentNodeDataIdentifier The "current" node, as passed by exportNodeDataList() + * @param string|null $currentNodeDataIdentifier The "current" node, as passed by exportNodeDataList() * @return void The result is written directly into $this->xmlWriter + * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException */ - protected function writeNode(NodeData $nodeData, &$currentNodeDataIdentifier) + protected function writeNode(NodeData $nodeData, ?string &$currentNodeDataIdentifier) { $nodeName = $nodeData->getName(); @@ -349,6 +403,7 @@ protected function writeNode(NodeData $nodeData, &$currentNodeDataIdentifier) * * @param NodeData $nodeData * @return void + * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException */ protected function writeVariant(NodeData $nodeData) { @@ -383,6 +438,7 @@ protected function writeDimensions(NodeData $nodeData) * * @param NodeData $nodeData * @return void + * @throws \Neos\ContentRepository\Exception\NodeTypeNotFoundException */ protected function writeProperties(NodeData $nodeData) { @@ -411,7 +467,7 @@ protected function writeProperties(NodeData $nodeData) * @param string $propertyName The name of the property * @param string $propertyValue The value of the property */ - protected function writeProperty($propertyName, $propertyValue) + protected function writeProperty(string $propertyName, string $propertyValue) { $this->xmlWriter->startElement($propertyName); $this->xmlWriter->writeAttribute('type', 'string'); @@ -431,4 +487,52 @@ protected function getAllowedContentCombinationsForSourceLanguage(string $source return (isset($combination[$this->languageDimension]) && $combination[$this->languageDimension][0] === $sourceLanguage); }); } + + /** + * Find all content nodes under the given node. + * + * @param NodeData $node + * @param Workspace $workspace + * @param array $contentDimensions + * @param boolean $isRemovedContentShown + * @return array + */ + private function getChildNodeDataOnPage(NodeData $node, Workspace $workspace, array $contentDimensions, bool $isRemovedContentShown): array + { + $results = []; + // We traverse through all Content (=!Document) nodes underneath the start node here and add it to the result list. + // NOTE: The $results list is passed into the callback by reference, so that we can modify it in-place. + $this->traverseRecursively($node, '!Neos.Neos:Document', $workspace, $contentDimensions, $isRemovedContentShown, function($node) use (&$results) { + $results[] = $node; + }); + return $results; + } + + /** + * This function recursively traverses all nodes underneath $node which match $nodeTypeFilter; and calls + * $callback on each of them (Depth-First Traversal). + * + * You have to watch out yourself to not build very deep nesting; so we suggest to use a node type filter + * like "!Neos.Neos:Document" or "Neos.Neos:ContentCollection, Neos.Neos:Content" which matches only content. + * + * For reference how the Node Type filter works, see: + * + * - NodeDataRepository::addNodeTypeFilterConstraintsToQueryBuilder + * - NodeDataRepository::getNodeTypeFilterConstraintsForDql + * + * @param NodeData $node + * @param string $nodeTypeFilter + * @param Workspace $workspace + * @param array $contentDimensions + * @param boolean $isRemovedContentShown + * @param \Closure $callback + */ + private function traverseRecursively(NodeData $node, string $nodeTypeFilter, Workspace $workspace, array $contentDimensions, bool $isRemovedContentShown, \Closure $callback) + { + $callback($node); + $childNodes = $this->nodeDataRepository->findByParentAndNodeType($node->getPath(), $nodeTypeFilter, $workspace, $contentDimensions, $isRemovedContentShown ? null : false, false); + foreach ($childNodes as $childNode) { + $this->traverseRecursively($childNode, $nodeTypeFilter, $workspace, $contentDimensions, $isRemovedContentShown, $callback); + } + } } diff --git a/Classes/Service/ImportService.php b/Classes/Service/ImportService.php index 9645a76..a96b891 100644 --- a/Classes/Service/ImportService.php +++ b/Classes/Service/ImportService.php @@ -22,22 +22,11 @@ * * @Flow\Scope("singleton") */ -class ImportService +class ImportService extends AbstractService { - /** - * @var string - */ - const SUPPORTED_FORMAT_VERSION = '1.0'; - - /** - * @Flow\InjectConfiguration(path = "languageDimension") - * @var string - */ - protected $languageDimension; - /** * @Flow\Inject - * @var \Neos\Flow\Package\PackageManagerInterface + * @var \Neos\Flow\Package\PackageManager */ protected $packageManager; @@ -47,30 +36,6 @@ class ImportService */ protected $persistenceManager; - /** - * @Flow\Inject - * @var \Neos\Neos\Domain\Repository\SiteRepository - */ - protected $siteRepository; - - /** - * @Flow\Inject - * @var \Neos\ContentRepository\Domain\Repository\WorkspaceRepository - */ - protected $workspaceRepository; - - /** - * @Flow\Inject - * @var \Neos\Neos\Domain\Service\ContentContextFactory - */ - protected $contentContextFactory; - - /** - * @Flow\Inject - * @var \Neos\Flow\Security\Context - */ - protected $securityContext; - /** * @Flow\Inject * @var \Neos\Neos\Domain\Service\ContentDimensionPresetSourceInterface @@ -107,11 +72,6 @@ class ImportService */ protected $targetWorkspace; - /** - * @var string - */ - protected $sourceWorkspaceName; - /** * @var string */ @@ -136,13 +96,13 @@ class ImportService * * * @param string $pathAndFilename - * @param string $workspaceName - * @param string $targetLanguage + * @param string|null $workspaceName + * @param string|null $targetLanguage * @return string * @throws InvalidPackageStateException * @throws UnknownPackageException */ - public function importFromFile($pathAndFilename, $workspaceName = null, $targetLanguage = null) + public function importFromFile(string $pathAndFilename, string $workspaceName = null, string $targetLanguage = null): string { /** @var \Neos\Neos\Domain\Model\Site $importedSite */ $site = null; @@ -162,7 +122,7 @@ public function importFromFile($pathAndFilename, $workspaceName = null, $targetL throw new \RuntimeException('No target language given (neither in XML nor as argument)', 1475578770); } - $this->languageDimensionPreset = $this->contentDimensionPresetSource->findPresetByDimensionValues($this->languageDimension, [$this->targetLanguage]); + $this->languageDimensionPreset = $this->contentDimensionPresetSource->findPresetsByTargetValues([$this->languageDimension => [$this->targetLanguage]]); if ($this->languageDimensionPreset === null) { throw new \RuntimeException(sprintf('No language dimension preset found for language "%s".', $this->targetLanguage), 1571230670); @@ -274,7 +234,6 @@ protected function parseElement(\XMLReader $xmlReader) break; default: throw new \Exception(sprintf('Unexpected element <%s> ', $elementName), 1423578065); - break; } } @@ -296,7 +255,6 @@ protected function parseEndElement(\XMLReader $reader) break; default: throw new \Exception(sprintf('Unexpected end element <%s> ', $reader->name), 1423578066); - break; } } @@ -307,7 +265,7 @@ protected function parseEndElement(\XMLReader $reader) * @param \XMLReader $reader reader positioned just after an opening dimensions-tag * @return array the dimension values */ - protected function parseDimensionsElement(\XMLReader $reader) + protected function parseDimensionsElement(\XMLReader $reader): array { $dimensions = []; $currentDimension = null; @@ -340,7 +298,7 @@ protected function parseDimensionsElement(\XMLReader $reader) * @return array the properties * @throws \Exception */ - protected function parsePropertiesElement(\XMLReader $reader) + protected function parsePropertiesElement(\XMLReader $reader): array { $properties = []; $currentProperty = null; @@ -398,7 +356,7 @@ protected function persistNodeData(array $translatedData) return $carry; }); - $dimensions = array_merge($translatedData['dimensionValues'], [$this->languageDimension => $this->languageDimensionPreset['values']]); + $dimensions = array_merge($translatedData['dimensionValues'], [$this->languageDimension => $this->languageDimensionPreset[$this->languageDimension]['values']]); $targetDimensions = array_map( function ($values) { return current($values); diff --git a/Configuration/Settings.yaml b/Configuration/Settings.yaml index 88fcac6..19e0cdf 100644 --- a/Configuration/Settings.yaml +++ b/Configuration/Settings.yaml @@ -2,3 +2,6 @@ Flownative: Neos: Trados: languageDimension: 'language' + export: + workspace: 'live' + documentTypeFilter: 'Neos.Neos:Document' diff --git a/README.md b/README.md index 52fbae1..bb0ef67 100644 --- a/README.md +++ b/README.md @@ -26,14 +26,15 @@ The export command: ./flow trados:export [<options>] <starting point> <source language> ARGUMENTS: - --starting-point The node with which to start the export, relative to the - site node. Optional. - --source-language The language to use as base for the export. + --starting-point The node with which to start the export, relative to the + site node. Optional. + --source-language The language to use as base for the export. OPTIONS: - --target-language The target language for the translation, optional. - --filename Path and filename to the XML file to create. + --target-language The target language for the translation, optional. + --filename Path and filename to the XML file to create. --modified-after + --exclude-child-documents If child documents should not be included in the export. The import command: diff --git a/composer.json b/composer.json index 76af44f..649a79a 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,10 @@ "license": "MIT", "description": "XML import/export for Neos, suited for Trados translations", "require": { - "neos/neos": "~3.0 || ^4.0 || ^5.0" + "neos/neos": "^4.3 || ^5.0 || ^7.0", + "php": "^7.4 || ^8.0", + "ext-xmlwriter": "*", + "ext-xmlreader": "*" }, "autoload": { "psr-4": {