Commit 9fec2a1b authored by Damjan's avatar Damjan
Browse files

[FEATURE] Implementing override support.

Related: forge.typo3.org/issues/4413
parent d040ff7a
......@@ -54,6 +54,7 @@ class AddConstantController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -27,6 +27,7 @@ namespace SGalinski\Lfeditor\Controller;
***************************************************************/
use SGalinski\Lfeditor\Exceptions\LFException;
use SGalinski\Lfeditor\Service\FileOverrideService;
use SGalinski\Lfeditor\Utility\Functions;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
......@@ -53,7 +54,15 @@ class DeleteConstantController extends AbstractBackendController {
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$fileObject = $this->configurationService->getFileObj();
if ($this->session->getDataByKey('overrideMode')) {
/** @var FileOverrideService $overrideFileObject */
$overrideFileObject = $fileObject;
$overrideFileObject->deleteDuplicates();
}
$langData = $fileObject->getLocalLangData();
// TODO: DeleteConst list contains only override consts.
$constantOptions = $this->configurationService->menuConstList(
$langData, LocalizationUtility::translate('select.nothing', 'lfeditor')
);
......@@ -63,6 +72,7 @@ class DeleteConstantController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -69,6 +69,7 @@ class EditConstantController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -77,6 +77,7 @@ class EditFileController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -74,6 +74,7 @@ class GeneralController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......@@ -113,39 +114,41 @@ class GeneralController extends AbstractBackendController {
];
$this->configurationService->execWrite([], $metaArray);
// split or merge
if (($splitFile == 1 || $splitFile == 2)) {
$langModes = [];
// set vars
if ($splitFile != 1 && $splitFile != 2) {
$splitFile = 0;
}
$langKeys = Functions::buildLangArray();
if(!$this->session->getDataByKey('overrideMode')) {
// split or merge
if (($splitFile == 1 || $splitFile == 2)) {
$langModes = [];
// set vars
if ($splitFile != 1 && $splitFile != 2) {
$splitFile = 0;
}
$langKeys = Functions::buildLangArray();
// generate langModes
foreach ($langKeys as $langKey) {
if (!isset($langModes[$langKey])) {
$langModes[$langKey] = $splitFile;
// generate langModes
foreach ($langKeys as $langKey) {
if (!isset($langModes[$langKey])) {
$langModes[$langKey] = $splitFile;
}
}
}
// exec split or merge
$this->configurationService->execSplitFile($langModes);
// reinitialize file object
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
}
// exec split or merge
$this->configurationService->execSplitFile($langModes);
// reinitialize file object
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
}
if (!empty($transformFile)
&& $this->configurationService->getFileObj()->getVar('fileType') != $transformFile
) {
$newFile = SgLib::setFileExtension(
$transformFile, $this->configurationService->getFileObj()->getVar('relFile')
);
$this->configurationService->execTransform($transformFile, $newFile);
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (!empty($transformFile)
&& $this->configurationService->getFileObj()->getVar('fileType') != $transformFile
) {
$newFile = SgLib::setFileExtension(
$transformFile, $this->configurationService->getFileObj()->getVar('relFile')
);
$this->configurationService->execTransform($transformFile, $newFile);
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
}
}
$this->addFlashMessage(
......@@ -196,7 +199,7 @@ class GeneralController extends AbstractBackendController {
* @param string $language
* @return void
*/
protected function prepareLanguageForTreeAction($language) {
public function prepareLanguageForTreeAction($language) {
$this->session->setDataByKey('languageSelection', $language);
$this->redirect('viewTree', 'ViewTree');
}
......@@ -207,11 +210,22 @@ class GeneralController extends AbstractBackendController {
* @param int $tinyMCE says is TinyMCE insert mode is selected.
* @return void
*/
protected function switchInsertModeAction($tinyMCE = 0) {
public function switchInsertModeAction($tinyMCE = 0) {
$this->session->setDataByKey('tinyMCESelected', $tinyMCE !== 0);
$this->indexAction();
}
/**
* Switches between override mode and normal mode.
*
* @param int $overrideMode 1 - override mode is on. 0 - override mode is off.
* @return void
*/
public function setOverrideModeAction($overrideMode = 0) {
$this->session->setDataByKey('overrideMode', $overrideMode !== 0);
$this->indexAction();
}
/**
* Checks do all language translations originate from same file.
*
......@@ -219,7 +233,7 @@ class GeneralController extends AbstractBackendController {
* @return bool
*/
private function isOriginSameForAllLanguages(array $infoArray) {
foreach($infoArray as $langInfo) {
foreach ($infoArray as $langInfo) {
if ($infoArray['default']['origin'] !== $langInfo['origin']) {
return FALSE;
}
......
......@@ -84,6 +84,7 @@ class ManageBackupsController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -63,6 +63,7 @@ class RenameConstantController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -52,6 +52,7 @@ class SearchConstantController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -70,6 +70,7 @@ class ViewTreeController extends AbstractBackendController {
$this->addLFEFlashMessage($e);
}
$this->setLastCalledControllerActionPair();
$this->view->assign('overrideMode', $this->session->getDataByKey('overrideMode'));
}
/**
......
......@@ -55,12 +55,12 @@ class ConfigurationService extends AbstractService {
protected $langArray = [];
/**
* @var \SGalinski\Lfeditor\Service\FileBasePHPService
* @var \SGalinski\Lfeditor\Service\FileService
*/
protected $fileObj;
/**
* @var \SGalinski\Lfeditor\Service\FileBasePHPService
* @var \SGalinski\Lfeditor\Service\FileBaseService
*/
protected $convObj;
......@@ -100,9 +100,9 @@ class ConfigurationService extends AbstractService {
$this->extConfig['metaFile'] = Typo3Lib::fixFilePath(
PATH_site . '/typo3temp/LFEditor/Backup/Meta.xml'
);
$this->extConfig['pathXLLFiles'] = Typo3Lib::fixFilePath(
PATH_site . '/typo3conf/LFEditor/XLL/'
) . '/';
$this->extConfig['pathOverrideFiles'] = Typo3Lib::fixFilePath(
PATH_site . '/typo3conf/LFEditor/OverrideFiles/'
);
// files
$this->extConfig['pathCSS'] = 'Resources/Public/StyleSheets/Lfeditor.css';
......@@ -322,64 +322,38 @@ class ConfigurationService extends AbstractService {
* Naming Convention:
* File<workspace><filetype>Service
*
* @throws LFException raised if the the object cant be generated or language file not read
* @throws Exception|LFException
*
* @param string $langFile
* @param string $extPath
* @param string $mode
* @param bool $flagReadFile
* @throws LFException
* @return void
*/
public function initFileObject($langFile, $extPath, $mode = 'base', $flagReadFile = TRUE) {
$mode = ($mode ?: 'base');
// xll specific
try {
$typo3RelFile = '';
if ($mode == 'xll') {
try {
$typo3RelFile = Typo3Lib::transTypo3File($extPath . '/' . $langFile, FALSE);
} catch (Exception $e) {
$typo3RelFile = '';
}
$xllFile = Typo3Lib::fixFilePath(
PATH_site . '/' .
$GLOBALS['TYPO3_CONF_VARS']['BE']['XLLfile'][$typo3RelFile]
);
if (is_file($xllFile)) {
$langFile = basename($xllFile);
$extPath = dirname($xllFile);
} else {
$langFile = GeneralUtility::shortMD5(md5(microtime())) . '.' .
SgLib::getFileExtension($langFile);
$extPath = $this->extConfig['pathXLLFiles'];
$flagReadFile = FALSE;
}
}
$fileType = SgLib::getFileExtension($langFile);
} catch (Exception $e) {
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
}
// create file object
$className = __NAMESPACE__ . '\File' . ucfirst($mode) . strtoupper($fileType) . 'Service';
public function initFileObject($langFile, $extPath, $flagReadFile = TRUE) {
$fileType = SgLib::getFileExtension($langFile);
$className = __NAMESPACE__ . '\FileBase' . strtoupper($fileType) . 'Service';
if (!class_exists($className)) {
throw new LFException('failure.langfile.unknownType');
}
$this->fileObj = $this->objectManager->get($className);
/** @var \SGalinski\Lfeditor\Service\FileService $originalFileObject */
$originalFileObject = $this->objectManager->get($className);
$originalFileObject->init($langFile, $extPath);
try {
if ($mode == 'xll') {
$this->fileObj->init($langFile, $extPath, $typo3RelFile);
if ($this->session->getDataByKey('overrideMode')) {
/** @var FileOverrideService $overrideFileObj */
$overrideFileObj = $this->objectManager->get('SGalinski\Lfeditor\Service\FileOverrideService');
$overrideFileObj->init($originalFileObject);
$this->fileObj = $overrideFileObj;
} else {
$this->fileObj->init($langFile, $extPath);
$this->fileObj = $originalFileObject;
}
} catch (Exception $e) {
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
}
if ($flagReadFile) {
$this->fileObj->readFile();
}
} catch (LFException $e) {
throw $e;
if ($flagReadFile) {
$this->fileObj->readFile();
}
}
......@@ -547,7 +521,7 @@ class ConfigurationService extends AbstractService {
unset($this->fileObj);
// init new language file object (dont try to read file)
$this->initFileObject($newFile, $this->convObj->getVar('absPath'), 'base', FALSE);
$this->initFileObject($newFile, $this->convObj->getVar('absPath'), FALSE);
// recreate originLang
$dirNameOfAbsFile = dirname($this->fileObj->getVar('absFile'));
......
......@@ -47,13 +47,13 @@ abstract class FileBaseService extends FileService {
* @param string $langKey
* @return mixed
*/
abstract protected function checkLocalizedFile($filename, $langKey);
abstract public function checkLocalizedFile($filename, $langKey);
/**
* @param string $langKey
* @return mixed
*/
abstract protected function nameLocalizedFile($langKey);
abstract public function nameLocalizedFile($langKey);
/**
* @param string $file
......@@ -84,7 +84,7 @@ abstract class FileBaseService extends FileService {
throw new LFException('failure.langfile.notSupported');
}
$this->setVar(['workspace' => 'base']);
$this->setWorkspace('base');
parent::init($file, $path);
}
......@@ -96,11 +96,7 @@ abstract class FileBaseService extends FileService {
*/
public function readFile() {
// read absolute file
try {
$localLang = $this->readLLFile($this->absFile, 'default');
} catch (LFException $e) {
throw $e;
}
$localLang = $this->readLLFile($this->absFile, 'default');
// loop all languages
$languages = SgLib::getSystemLanguages();
......
......@@ -44,7 +44,7 @@ class FileBaseXLFService extends FileBaseService {
* @return void
*/
public function init($file, $path) {
$this->setVar(['fileType' => 'xlf']);
$this->setFileType('xlf');
parent::init($file, $path);
}
......@@ -55,11 +55,7 @@ class FileBaseXLFService extends FileBaseService {
* @return void
*/
public function readFile() {
try {
$this->readXlfFile();
} catch (LFException $e) {
throw $e;
}
$this->readXlfFile();
// convert all language values from utf-8 to the original charset
if (!Typo3Lib::isTypo3BackendInUtf8Mode()) {
......@@ -85,7 +81,8 @@ class FileBaseXLFService extends FileBaseService {
$xmlContent = json_decode(json_encode($xmlContent), TRUE);
// check data
if (!is_array($xmlContent['file']['body']) || !count($xmlContent['file']['body'])) {
if ((!is_array($xmlContent['file']['body']) || !count($xmlContent['file']['body']))
&& get_class($this) !== 'SGalinski\Lfeditor\Service\FileOverrideService') {
throw new LFException('failure.search.noFileContent', 0, '(' . $file . ')');
}
......@@ -309,9 +306,7 @@ class FileBaseXLFService extends FileBaseService {
$this->meta[$label] = str_replace("\r", '', str_replace("\n", '<br />', $value));
}
}
// add generator string
$this->meta['generator'] = 'LFEditor';
$this->addGeneratorString();
$metadata = $this->meta;
unset($metadata['@attributes']);
......
......@@ -260,9 +260,7 @@ class FileBaseXMLService extends FileBaseService {
$this->meta[$label] = str_replace("\r", '', str_replace("\n", '<br />', $value));
}
}
// add generator string
$this->meta['generator'] = 'LFEditor';
$this->addGeneratorString();
return $this->meta;
}
......
<?php
namespace SGalinski\Lfeditor\Service;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (http://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;
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(FileService $originalFileObject) {
try {
$typo3ExtRelativeFilePath = Typo3Lib::transTypo3File($originalFileObject->getAbsFile(), FALSE);
} catch (\Exception $e) {
$typo3ExtRelativeFilePath = '';
}
$overrideFileAbsolutePath = Typo3Lib::fixFilePath(
PATH_site . '/' .
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0] //TODO: this can support more than 1 override files?
);
if (!is_file($overrideFileAbsolutePath)) {
$extRelativeFilePath = SgLib::trimPath('EXT:', $typo3ExtRelativeFilePath);
$extRelativeFilePath = substr($extRelativeFilePath, 0, strrpos($extRelativeFilePath, '.')) . '.xlf';
/** @var ConfigurationService $configurationService */
$configurationService = $this->objectManager->get('SGalinski\Lfeditor\Service\ConfigurationService');
$overrideFileAbsolutePath = $configurationService->getExtConfig()['pathOverrideFiles']
. $extRelativeFilePath;
}
$this->originalFileObject = $originalFileObject;
parent::init(basename($overrideFileAbsolutePath), dirname($overrideFileAbsolutePath));
}
/**
* Calls the parent function and convert all values from utf-8 to the original charset
*
* @throws LFException raised if the parent read file method fails
* @return void
*/
public function readFile() {
if (is_file($this->absFile)) {
$this->readXlfFile();
if (!Typo3Lib::isTypo3BackendInUtf8Mode()) {
$this->localLang = Typo3Lib::utf8($this->localLang, FALSE, ['default']);
}
}
$this->originalFileObject->readFile();
$this->mergeOriginalWidthOverrideLangData();
}
/**
* 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 (empty($langData)) {
continue;
}
foreach ($langData as $costKey => $constValue) {
if (isset($this->localLang[$lang][$costKey]) && $this->localLang[$lang][$costKey] !== $constValue) {
continue;
}
$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;
}
}
}
/**
* Writes language override files.
*
* @throws LFException raised if a file cant be written
* @return void
*/
public function writeFile() {
$this->deleteDuplicates();
if (!$this->langDataExists() && !is_file($this->absFile)) {
return;
}
try {
SgLib::createDir($this->absPath, PATH_site);
} catch (\Exception $e) {
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
}
parent::writeFile();
// TODO: Delete override file and additional configuration line if there is no content in file?
// Set only new values in GlobalConfiguration if something changed
$typo3ExtRelativeFilePath = Typo3Lib::transTypo3File($this->originalFileObject->getAbsFile(), FALSE);
$relativeOverrideFilePath = SgLib::trimPath(PATH_site, $this->absFile);
//TODO: this can support more than 1 override files?
if ($GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0]
=== $relativeOverrideFilePath
) {
return;
}
try {
$addLine = '$GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'locallangXMLOverride\'][\''
. $typo3ExtRelativeFilePath . '\'][0] = \'' . $relativeOverrideFilePath . '\';';
Typo3Lib::writeLineToAdditionalConfiguration($addLine);
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locallangXMLOverride'][$typo3ExtRelativeFilePath][0]
= $relativeOverrideFilePath;
} catch (\Exception $e) {
throw new LFException('failure.failure', 0, '(' . $e->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() {
foreach ($this->localLang as $lang => $langData) {
foreach ($langData as $constKey => $constVa