Skip to content

Commit 320a9f9

Browse files
committed
Merge branch 'release/3.0.2'
2 parents ccf3968 + 43b592e commit 320a9f9

8 files changed

Lines changed: 276 additions & 52 deletions

File tree

.travis.yml

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,71 +21,73 @@ notifications:
2121
email:
2222
- typo3@evoweb.de
2323

24-
install:
25-
- >
26-
export TYPO3_PATH_WEB=$PWD/.Build/Web;
27-
composer require typo3/minimal="$TYPO3_VERSION" $PREFER_LOWEST;
28-
# composer require --dev typo3/testing-framework="$TESTING_FRAMEWORK";
29-
git checkout composer.json;
24+
# This is executed for all stages
25+
before_install:
26+
- if php -i | grep -v TRAVIS_CMD | grep -q xdebug; then phpenv config-rm xdebug.ini; fi
3027

31-
before_script:
32-
- phpenv config-rm xdebug.ini
28+
install:
29+
- export TYPO3_PATH_WEB=$PWD/.Build/Web;
30+
- |
31+
composer require typo3/cms-core="$TYPO3_VERSION" $PREFER_LOWEST;
32+
# if [ ! -z "$TESTING_FRAMEWORK" ]; then composer require --dev typo3/testing-framework="$TESTING_FRAMEWORK"; fi;
33+
- git checkout composer.json;
3334

3435
script:
35-
- >
36+
- |
3637
echo "Running functional tests -- not implemented jet";
3738
#export typo3DatabaseName="typo3";
3839
#export typo3DatabaseHost="localhost";
3940
#export typo3DatabaseUsername="root";
4041
#export typo3DatabasePassword="";
41-
#php .Build/bin/phpunit --colors -c .Build/Web/vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml Tests/Functional/;
42+
#php .Build/Web/vendor/bin/phpunit --colors -c .Build/Web/vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml Tests/Functional/;
4243
4344
jobs:
44-
fast_finish: true
4545
allow_failures:
46-
- env:
47-
- TYPO3_VERSION="dev-master as 10.0.0"
46+
- env: TYPO3_VERSION="dev-master as 10.4.0"
4847

4948
include:
5049
- &lint
5150
stage: test
52-
php: 7.3
51+
php: 7.4
5352
env: TASK="PHP Lint"
54-
before_install: skip
5553
install: skip
56-
before_script: skip
5754
script:
58-
- echo "Running php lint";
55+
- |
56+
echo "Running php lint";
5957
errors=$(find . -name \*.php ! -path "./.Build/*" -exec php -d display_errors=stderr -l {} 2>&1 >/dev/null \;) && echo "$errors" && test -z "$errors"
58+
- <<: *lint
59+
php: 7.3
6060
- <<: *lint
6161
php: 7.2
6262

63+
- stage: test
64+
php: 7.4
65+
env: TYPO3_VERSION=^10.4
6366
- stage: test
6467
php: 7.3
65-
env:
66-
- TYPO3_VERSION=^10.0
68+
env: TYPO3_VERSION=^10.4
6769
- stage: test
6870
php: 7.2
69-
env:
70-
- TYPO3_VERSION=^10.0
71+
env: TYPO3_VERSION=^10.4
7172
- stage: test
7273
php: 7.2
73-
env:
74-
- TYPO3_VERSION="dev-master as 10.0.0"
74+
env: TYPO3_VERSION=^10.4 PREFER_LOWEST="--prefer-lowest"
75+
- stage: test
76+
php: 7.2
77+
env: TYPO3_VERSION="dev-master as 10.4.0"
7578

7679
- stage: publish in ter
7780
if: tag IS present
78-
php: 7.1
81+
php: 7.2
7982
before_install: skip
8083
install: skip
81-
before_script: skip
8284
script:
8385
- |
84-
if [ -n "$TYPO3_ORG_USERNAME" ] && [ -n "$TYPO3_ORG_PASSWORD" ]; then
86+
if [[ "$TRAVIS_TAG" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && [ -n "$TYPO3_ORG_USERNAME" ] && [ -n "$TYPO3_ORG_PASSWORD" ]; then
8587
echo -e "Preparing upload of release ${TRAVIS_TAG} to TER\n";
8688
8789
# Install requirements
88-
composer global require helhum/ter-client dev-master
90+
composer global require helhum/ter-client
8991
9092
# Cleanup before we upload
9193
git reset --hard HEAD && git clean -fx

Classes/Controller/FileController.php

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
<?php
2+
23
namespace Evoweb\EwLlxml2xliff\Controller;
34

5+
use Evoweb\EwLlxml2xliff\Utility\Convert as Convert;
46
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
57
use TYPO3\CMS\Core\Utility\GeneralUtility;
8+
use TYPO3\CMS\Extensionmanager\Utility\ListUtility as ListUtility;
69

710
class FileController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
811
{
912
/**
10-
* @var \TYPO3\CMS\Extensionmanager\Utility\ListUtility
13+
* @var ListUtility
1114
*/
1215
protected $listUtility;
1316

1417
/**
15-
* @param \TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility
18+
* @var Convert
1619
*/
17-
public function injectListUtility(\TYPO3\CMS\Extensionmanager\Utility\ListUtility $listUtility)
18-
{
20+
protected $convertUtility;
21+
22+
public function __construct(
23+
ListUtility $listUtility,
24+
Convert $convertUtility
25+
) {
1926
$this->listUtility = $listUtility;
27+
$this->convertUtility = $convertUtility;
2028
}
2129

22-
2330
public function indexAction()
2431
{
2532
$extensions = $this->getLocalExtensions();
@@ -70,13 +77,10 @@ public function convertFileAction()
7077
if ($this->xliffFileAlreadyExists($extensionPath, $selectedFile)) {
7178
$this->view->assign('wasConvertedPreviously', 1);
7279
} else {
73-
/** @var \Evoweb\EwLlxml2xliff\Utility\Convert $convertUtility */
74-
$convertUtility = $this->objectManager->get(\Evoweb\EwLlxml2xliff\Utility\Convert::class);
75-
7680
if (strpos($selectedFile, '.xml') !== false) {
77-
$messages = $convertUtility->writeXmlAsXlfFilesInPlace($selectedFile, $selectedExtension);
81+
$messages = $this->convertUtility->writeXmlAsXlfFilesInPlace($selectedFile, $selectedExtension);
7882
} else {
79-
$messages = $convertUtility->writePhpAsXlfFilesInPlace($selectedFile, $selectedExtension);
83+
$messages = $this->convertUtility->writePhpAsXlfFilesInPlace($selectedFile, $selectedExtension);
8084
}
8185

8286
if (strpos($messages, 'ERROR') === false) {
@@ -93,11 +97,12 @@ public function convertFileAction()
9397
$this->view->assign('convertedFile', $selectedFile);
9498
}
9599

96-
97100
protected function getLocalExtensions(): array
98101
{
99102
$availableExtensions = $this->listUtility->getAvailableExtensions();
100103
$extensions = array_filter($availableExtensions, function ($extension, $key) {
104+
/** @var array $extension */
105+
/** @var string $key */
101106
return $extension['type'] == 'Local' && ExtensionManagementUtility::isLoaded($key);
102107
}, ARRAY_FILTER_USE_BOTH);
103108
ksort($extensions);
@@ -107,7 +112,7 @@ protected function getLocalExtensions(): array
107112
/**
108113
* Gather files that need to be converted
109114
*
110-
* @param string $extensionKey List of file extensions to select
115+
* @param string $extensionKey Extension for which to get list of files of
111116
*
112117
* @return array
113118
*/
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the TYPO3 CMS project.
5+
*
6+
* It is free software; you can redistribute it and/or modify it under
7+
* the terms of the GNU General Public License, either version 2
8+
* of the License, or any later version.
9+
*
10+
* For the full copyright and license information, please read the
11+
* LICENSE.txt file that was distributed with this source code.
12+
*
13+
* The TYPO3 project - inspiring people to share!
14+
*/
15+
16+
namespace Evoweb\EwLlxml2xliff\Localization\Parser;
17+
18+
use TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException;
19+
use TYPO3\CMS\Core\Utility\ArrayUtility;
20+
use TYPO3\CMS\Core\Utility\GeneralUtility;
21+
use TYPO3\CMS\Core\Utility\PathUtility;
22+
23+
/**
24+
* Parser for XML locallang file.
25+
*/
26+
class LocallangXmlParser extends \TYPO3\CMS\Core\Localization\Parser\AbstractXmlParser
27+
{
28+
/**
29+
* Associative array of "filename => parsed data" pairs.
30+
*
31+
* @var array
32+
*/
33+
protected $parsedTargetFiles;
34+
35+
/**
36+
* Returns parsed representation of XML file.
37+
*
38+
* @param string $sourcePath Source file path
39+
* @param string $languageKey Language key
40+
* @return array
41+
*/
42+
public function getParsedData($sourcePath, $languageKey)
43+
{
44+
$this->sourcePath = $sourcePath;
45+
$this->languageKey = $languageKey;
46+
// Parse source
47+
$parsedSource = $this->parseXmlFile();
48+
// Parse target
49+
$localizedTargetPath = $this->getLocalizedFileName($this->sourcePath, $this->languageKey);
50+
$targetPath = $this->languageKey !== 'default' && @is_file($localizedTargetPath)
51+
? $localizedTargetPath : $this->sourcePath;
52+
try {
53+
$parsedTarget = $this->getParsedTargetData($targetPath);
54+
} catch (InvalidXmlFileException $e) {
55+
$parsedTarget = $this->getParsedTargetData($this->sourcePath);
56+
}
57+
$LOCAL_LANG = [];
58+
$LOCAL_LANG[$languageKey] = $parsedSource;
59+
ArrayUtility::mergeRecursiveWithOverrule($LOCAL_LANG[$languageKey], $parsedTarget);
60+
return $LOCAL_LANG;
61+
}
62+
63+
/**
64+
* Returns array representation of XLIFF data, starting from a root node.
65+
*
66+
* @param \SimpleXMLElement $root XML root element
67+
* @param string $element Target or Source
68+
* @return array
69+
* @throws InvalidXmlFileException
70+
*/
71+
protected function doParsingFromRootForElement(\SimpleXMLElement $root, $element)
72+
{
73+
$bodyOfFileTag = $root->data->languageKey;
74+
if ($bodyOfFileTag === null) {
75+
throw new InvalidXmlFileException(
76+
'Invalid locallang.xml language file "' . PathUtility::stripPathSitePrefix($this->sourcePath) . '"',
77+
1487944884
78+
);
79+
}
80+
81+
if ($element === 'source' || $this->languageKey === 'default') {
82+
$parsedData = $this->getParsedDataForElement($bodyOfFileTag, $element);
83+
} else {
84+
$parsedData = [];
85+
}
86+
if ($element === 'target') {
87+
// Check if the source llxml file contains localized records
88+
$localizedBodyOfFileTag = $root->data->xpath('languageKey[@index=\'' . $this->languageKey . '\']');
89+
if (isset($localizedBodyOfFileTag[0]) && $localizedBodyOfFileTag[0] instanceof \SimpleXMLElement) {
90+
$parsedDataTarget = $this->getParsedDataForElement($localizedBodyOfFileTag[0], $element);
91+
$mergedData = $parsedDataTarget + $parsedData;
92+
if ($this->languageKey === 'default') {
93+
$parsedData = array_intersect_key($mergedData, $parsedData, $parsedDataTarget);
94+
} else {
95+
$parsedData = array_intersect_key($mergedData, $parsedDataTarget);
96+
}
97+
}
98+
}
99+
return $parsedData;
100+
}
101+
102+
/**
103+
* Parse the given language key tag
104+
*
105+
* @param \SimpleXMLElement $bodyOfFileTag
106+
* @param string $element
107+
* @return array
108+
*/
109+
protected function getParsedDataForElement(\SimpleXMLElement $bodyOfFileTag, $element)
110+
{
111+
$parsedData = [];
112+
$children = $bodyOfFileTag->children();
113+
if ($children->count() === 0) {
114+
// Check for externally-referenced resource:
115+
// <languageKey index="fr">EXT:yourext/path/to/localized/locallang.xml</languageKey>
116+
$reference = sprintf('%s', $bodyOfFileTag);
117+
if (substr($reference, -4) === '.xml') {
118+
return $this->getParsedTargetData(GeneralUtility::getFileAbsFileName($reference));
119+
}
120+
}
121+
122+
foreach ($children as $translationElement) {
123+
if ($translationElement->getName() === 'label') {
124+
$parsedData[(string)$translationElement['index']][0] = [
125+
$element => (string)$translationElement
126+
];
127+
}
128+
}
129+
return $parsedData;
130+
}
131+
132+
/**
133+
* Returns array representation of XLIFF data, starting from a root node.
134+
*
135+
* @param \SimpleXMLElement $root A root node
136+
* @return array An array representing parsed XLIFF
137+
*/
138+
protected function doParsingFromRoot(\SimpleXMLElement $root)
139+
{
140+
return $this->doParsingFromRootForElement($root, 'source');
141+
}
142+
143+
/**
144+
* Returns array representation of XLIFF data, starting from a root node.
145+
*
146+
* @param \SimpleXMLElement $root A root node
147+
* @return array An array representing parsed XLIFF
148+
*/
149+
protected function doParsingTargetFromRoot(\SimpleXMLElement $root)
150+
{
151+
return $this->doParsingFromRootForElement($root, 'target');
152+
}
153+
154+
/**
155+
* Returns parsed representation of XML file.
156+
*
157+
* Parses XML if it wasn't done before. Caches parsed data.
158+
*
159+
* @param string $path An absolute path to XML file
160+
* @return array Parsed XML file
161+
*/
162+
public function getParsedTargetData($path)
163+
{
164+
if (!isset($this->parsedTargetFiles[$path])) {
165+
$this->parsedTargetFiles[$path] = $this->parseXmlTargetFile($path);
166+
}
167+
return $this->parsedTargetFiles[$path];
168+
}
169+
170+
/**
171+
* Reads and parses XML file and returns internal representation of data.
172+
*
173+
* @param string $targetPath Path of the target file
174+
* @return array
175+
* @throws \TYPO3\CMS\Core\Localization\Exception\InvalidXmlFileException
176+
*/
177+
protected function parseXmlTargetFile($targetPath)
178+
{
179+
$rootXmlNode = false;
180+
if (file_exists($targetPath)) {
181+
$xmlContent = file_get_contents($targetPath);
182+
// Disables the functionality to allow external entities to be loaded when parsing the XML, must be kept
183+
$previousValueOfEntityLoader = libxml_disable_entity_loader(true);
184+
$rootXmlNode = simplexml_load_string($xmlContent, \SimpleXMLElement::class, LIBXML_NOWARNING);
185+
libxml_disable_entity_loader($previousValueOfEntityLoader);
186+
}
187+
if ($rootXmlNode === false) {
188+
$xmlError = libxml_get_last_error();
189+
throw new InvalidXmlFileException(
190+
'The path provided does not point to existing and accessible well-formed XML file. Reason: '
191+
. $xmlError->message . ' in ' . $targetPath . ', line ' . $xmlError->line,
192+
1278155987
193+
);
194+
}
195+
return $this->doParsingTargetFromRoot($rootXmlNode);
196+
}
197+
}

0 commit comments

Comments
 (0)