Files
oai-symfony/typo3conf/ext/lfeditor/Classes/Service/FileOverrideService.php
2026-07-03 17:53:31 +02:00

361 lines
12 KiB
PHP

<?php
namespace SGalinski\Lfeditor\Service;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the TYPO3 project. The TYPO3 project is
* free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* The GNU General Public License can be found at
* http://www.gnu.org/copyleft/gpl.html.
*
* This script is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
use SGalinski\Lfeditor\Exceptions\LFException;
use SGalinski\Lfeditor\Utility\SgLib;
use SGalinski\Lfeditor\Utility\Typo3Lib;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* FileOverrideService
*/
class FileOverrideService extends FileBaseXLFService {
/**
* Object which represents original (overridden) language file.
*
* @var \SGalinski\Lfeditor\Service\FileService
*/
protected $originalFileObject;
/**
* Initialises fileObject of override language file.
*
* @param FileService $originalFileObject
* @return void
*/
public function init($originalFileObject, $path, $metaFile) {
$typo3ExtRelativeFilePath = '';
$file = $this->session->getDataByKey('languageFileSelection');
$extensionKey = $this->session->getDataByKey('extkey');
if ($file !== '' && $extensionKey !== '') {
$typo3ExtRelativeFilePath = 'EXT:' . $this->cleanSessionExtKey($extensionKey) . '/' . $file;
}
if ($typo3ExtRelativeFilePath === '') {
try {
$typo3ExtRelativeFilePath = Typo3Lib::transTypo3File($originalFileObject->getAbsFile(), FALSE);
} catch (\Exception $e) {
$typo3ExtRelativeFilePath = '';
}
}
$pathSite = Environment::getPublicPath() . '/';
$overrideFileAbsolutePath = Typo3Lib::fixFilePath(
$pathSite . '/' .
($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0] ?? '')
);
if (!is_file($overrideFileAbsolutePath)) {
$extRelativeFilePath = SgLib::trimPath('EXT:', $typo3ExtRelativeFilePath);
$extRelativeFilePath = substr($extRelativeFilePath, 0, strrpos($extRelativeFilePath, '.')) . '.xlf';
/** @var ConfigurationService $configurationService */
$configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
$extConfig = $configurationService->getExtConfig();
$overrideFileAbsolutePath = $extConfig['pathOverrideFiles']
. $extRelativeFilePath;
}
$this->originalFileObject = $originalFileObject;
parent::init(basename($overrideFileAbsolutePath), dirname($overrideFileAbsolutePath), $metaFile);
}
/**
* Calls the parent function and convert all values from utf-8 to the original charset
*
* @return void
* @throws LFException raised if the parent read file method fails
*/
public function readFile() {
if (is_file($this->absFile)) {
parent::readFile();
}
$oldXMLFile = $this->getOlderPath($this->absFile);
if (is_file($oldXMLFile)) {
$xmlFile = GeneralUtility::makeInstance(FileBaseXMLService::class);
// i do think we are fine omitting metaFile
$xmlFile->init(basename($oldXMLFile), dirname($oldXMLFile), '');
$xmlFile->readFile();
$this->mergeFileObjectWidthOverrideLangData($xmlFile);
}
$this->originalFileObject->readFile();
$this->mergeOriginalWidthOverrideLangData();
}
/**
* when setting session key, we are using the folder, which has
* a) "-" as delimiter
* b) "cms-" as prefix for core extension
*
* @param string $extkey
* @return string
*/
private function cleanSessionExtKey(string $extkey): string {
// cant be merged, because cms_ only is in string after we did first replacement
return str_replace('cms_', '', str_replace('-', '_', $extkey));
}
/**
* check for possible old files:
* 1. we do now have cms_ prefix for some reason
* 2. we only write localLang.xlf
*
* @param string $absolutePath
* @return string
*/
public function getOlderPath(string $absolutePath): string {
return str_replace(['.xlf', 'cms_'], ['.xml', ''], $absolutePath);
}
/**
* Merges Language data from original and override files, so all data can e presented to user.
*
* @return void
*/
protected function mergeOriginalWidthOverrideLangData() {
foreach ($this->originalFileObject->getLocalLang() as $lang => $langData) {
if (!is_array($langData)) {
continue;
}
foreach ($langData as $costKey => $constValue) {
if (isset($this->localLang[$lang][$costKey]) && $this->localLang[$lang][$costKey] !== $constValue) {
continue;
}
if (!array_key_exists($lang, $this->localLang) || !is_array($this->localLang[$lang])) {
$this->localLang[$lang] = [];
}
$this->localLang[$lang][$costKey] = $constValue;
}
}
foreach ($this->originalFileObject->getMeta() as $metaTag => $metaTagValue) {
if ($metaTag === '@attributes') {
foreach ($metaTagValue as $attributeKey => $attributeValue) {
if (isset($this->meta[$metaTag][$attributeKey])
&& $this->meta[$metaTag][$attributeKey] !== $attributeValue
) {
continue;
}
$this->meta[$metaTag][$attributeKey] = $attributeValue;
}
} else {
if (isset($this->meta[$metaTag]) && $this->meta[$metaTag] !== $metaTagValue) {
continue;
}
$this->meta[$metaTag] = $metaTagValue;
}
}
}
/**
* Merges Language data from original and override files, so all data can e presented to user.
*
* @param FileService $fileObject
* @return void
*/
protected function mergeFileObjectWidthOverrideLangData(FileService $fileObject) {
foreach ($fileObject->getLocalLang() as $lang => $langData) {
if (!is_array($langData)) {
continue;
}
foreach ($langData as $costKey => $constValue) {
if (isset($this->localLang[$lang][$costKey]) && $this->localLang[$lang][$costKey] !== $constValue) {
continue;
}
if (!array_key_exists($lang, $this->localLang) || !is_array($this->localLang[$lang])) {
$this->localLang[$lang] = [];
}
$this->localLang[$lang][$costKey] = $constValue;
}
}
foreach ($fileObject->getMeta() as $metaTag => $metaTagValue) {
if ($metaTag === '@attributes') {
foreach ($metaTagValue as $attributeKey => $attributeValue) {
if (isset($this->meta[$metaTag][$attributeKey])
&& $this->meta[$metaTag][$attributeKey] !== $attributeValue
) {
continue;
}
$this->meta[$metaTag][$attributeKey] = $attributeValue;
}
} else {
if (isset($this->meta[$metaTag]) && $this->meta[$metaTag] !== $metaTagValue) {
continue;
}
$this->meta[$metaTag] = $metaTagValue;
}
}
}
/**
* Writes language override files.
*
* @param array|null $editedLanguages
* @return void
* @throws \Exception
* @throws LFException raised if a file cant be written
*/
public function writeFile($editedLanguages = NULL) {
$this->deleteDuplicates();
if (!$this->langDataExists() && !is_file($this->absFile)) {
return;
}
$pathSite = Environment::getPublicPath() . '/';
try {
SgLib::createDir($this->absPath, $pathSite);
} catch (\Exception $exception) {
throw new LFException('failure.failure', 0, '(' . $exception->getMessage() . ')');
}
$translationFile = basename($this->absFile);
$translationDir = dirname($this->absFile);
foreach ($this->localLang as $langKey => $val) {
if ($langKey !== 'default' && $langKey !== 'trans-unit') {
$this->originLang[$langKey] = $translationDir . '/' . $langKey . '.' . $translationFile;
}
}
parent::writeFile();
// Set only new values in GlobalConfiguration if something changed
$originalAbsPath = $this->originalFileObject->getAbsFile();
$pathSpecsOriginialFile = pathinfo($originalAbsPath);
$locallangXMLOverride = &$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'];
$file = $this->session->getDataByKey('languageFileSelection');
$extensionKey = $this->session->getDataByKey('extkey');
$oldExtensionName = $this->cleanSessionExtKey($extensionKey);
$typo3ExtRelativeFilePath = 'EXT:' . $oldExtensionName . '/' . $file;
$relativeOverridePath = str_replace(
'EXT:' . $oldExtensionName,
'typo3conf/LFEditor/OverrideFiles/' . $oldExtensionName,
$typo3ExtRelativeFilePath
);
if (array_key_exists($typo3ExtRelativeFilePath, $locallangXMLOverride) &&
$locallangXMLOverride[$typo3ExtRelativeFilePath][0] === $relativeOverridePath) {
return;
}
/** @var ConfigurationService $configurationService */
$configurationService = GeneralUtility::makeInstance(ConfigurationService::class);
$additionalConfigurationFilePath = $configurationService->getAdditionalConfigurationFilePath();
try {
// what we need:
$addLine = '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'locallangXMLOverride\'][\''
. $typo3ExtRelativeFilePath . '\'][0] = \'' . $relativeOverridePath . '\';';
Typo3Lib::writeLineToAdditionalConfiguration($addLine, $additionalConfigurationFilePath);
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0]
= $relativeOverridePath;
} catch (\Exception $exception) {
throw new LFException('failure.failure', 0, '(' . $exception->getMessage() . ')');
}
}
/**
* Deletes duplicated language data (data that exist in both files - original file and override file),
* so that only changed data will be saved in override file.
*
* @return void
*/
public function deleteDuplicates() {
$defaultLangData = $this->originalFileObject->getLocalLangData('default');
foreach ($this->localLang as $language => $languageData) {
if (!is_array($languageData)) {
continue;
}
foreach ($languageData as $constKey => $constValue) {
$localLangData = $this->originalFileObject->getLocalLangData($language);
if (array_key_exists($constKey, $localLangData) && SgLib::strCmpIgnoreCR(
$constValue,
$localLangData[$constKey]
)) {
unset($this->localLang[$language][$constKey]);
} elseif ($language !== 'default' && $language !== 'trans-unit') {
// if we are not in default, but we deleted the default key, we have to restore it, else we would
// not write the correct language content in xlf
if (!array_key_exists($constKey, $this->localLang['default'])) {
$this->localLang['default'][$constKey] = $defaultLangData[$constKey] ?? '';
}
}
}
}
foreach ($this->meta as $metaTag => $metaValue) {
if (SgLib::strCmpIgnoreCR($metaValue, $this->originalFileObject->getMetaData($metaTag))) {
unset($this->meta[$metaTag]);
}
}
}
/**
* Checks is there any lang data (constants in $localLang and meta data).
*
* @return bool Returns true if any constant or meta data exist.
*/
protected function langDataExists() {
foreach ($this->meta as $metaTag => $metaValue) {
if (isset($this->meta[$metaTag]) && $metaTag !== '@attributes') {
return TRUE;
}
}
foreach ($this->localLang as $language => $languageData) {
if (!is_array($languageData)) {
continue;
}
foreach ($languageData as $constKey => $constValue) {
if (isset($languageData[$constKey])) {
return TRUE;
}
}
}
return FALSE;
}
/**
* Writes generator meta tag.
*
* @return void
*/
protected function addGeneratorString() {
if ($this->originalFileObject->getMetaData('generator') !== 'LFEditor') {
$this->meta['generator'] = 'LFEditor';
}
}
}