Initial commit - Typo3 11.5.41
This commit is contained in:
49
typo3conf/ext/lfeditor/Classes/Service/AbstractService.php
Normal file
49
typo3conf/ext/lfeditor/Classes/Service/AbstractService.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?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\Session\PhpSession;
|
||||
use TYPO3\CMS\Core\SingletonInterface;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class AbstractService
|
||||
*/
|
||||
abstract class AbstractService implements SingletonInterface {
|
||||
/**
|
||||
* @var PhpSession
|
||||
*/
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->session = GeneralUtility::makeInstance(PhpSession::class);
|
||||
$this->session->setSessionKey('tx_lfeditor_sessionVariables');
|
||||
}
|
||||
}
|
||||
316
typo3conf/ext/lfeditor/Classes/Service/BackupService.php
Normal file
316
typo3conf/ext/lfeditor/Classes/Service/BackupService.php
Normal file
@@ -0,0 +1,316 @@
|
||||
<?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 Exception;
|
||||
use SGalinski\Lfeditor\Exceptions\LFException;
|
||||
use SGalinski\Lfeditor\Utility\Functions;
|
||||
use SGalinski\Lfeditor\Utility\SgLib;
|
||||
use SGalinski\Lfeditor\Utility\Typo3Lib;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class BackupService
|
||||
*/
|
||||
class BackupService extends AbstractService {
|
||||
/**
|
||||
* @var \SGalinski\Lfeditor\Service\FileBackupService $backupObj
|
||||
*/
|
||||
private $backupObj;
|
||||
|
||||
/**
|
||||
* init backup object
|
||||
*
|
||||
* @throws LFException raised if directories cant be created or backup class instantiated
|
||||
* @throws Exception|LFException
|
||||
* @param string $mode workspace
|
||||
* @param boolean|array $infos set to true if you want use information from the file object
|
||||
* @return void
|
||||
*/
|
||||
public function initBackupObject($mode = 'base', $infos = NULL) {
|
||||
$informations = [];
|
||||
$mode = ($mode ?: 'base');
|
||||
/** @var ConfigurationService $confService */
|
||||
$confService = GeneralUtility::makeInstance(ConfigurationService::class);
|
||||
|
||||
// create backup and meta directory
|
||||
$extConfig = $confService->getExtConfig();
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
try {
|
||||
SgLib::createDir($extConfig['pathBackup'], $pathSite);
|
||||
SgLib::createDir(dirname($extConfig['metaFile']), $pathSite);
|
||||
} catch (Exception $e) {
|
||||
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
|
||||
}
|
||||
|
||||
// get information
|
||||
$extPath = '';
|
||||
$langFile = '';
|
||||
if (!is_array($infos)) {
|
||||
// build language file and extension path
|
||||
if ($mode == 'xlf') {
|
||||
try {
|
||||
$typo3RelFile = $confService->getFileObj()->getVar('typo3RelFile');
|
||||
$typo3AbsFile = Typo3Lib::transTypo3File($typo3RelFile, TRUE);
|
||||
} catch (Exception $e) {
|
||||
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
|
||||
}
|
||||
|
||||
$langFile = SgLib::trimPath('EXT:', $typo3RelFile);
|
||||
$langFile = substr($langFile, strpos($langFile, '/') + 1);
|
||||
|
||||
$extPath = SgLib::trimPath(
|
||||
$langFile,
|
||||
SgLib::trimPath(
|
||||
$pathSite,
|
||||
$typo3AbsFile
|
||||
),
|
||||
'/'
|
||||
);
|
||||
} else {
|
||||
$extPath = SgLib::trimPath($pathSite, $confService->getFileObj()->getVar('absPath'), '/');
|
||||
$langFile = $confService->getFileObj()->getVar('relFile');
|
||||
}
|
||||
|
||||
// set data information
|
||||
$informations['localLang'] = $confService->getFileObj()->getLocalLangData();
|
||||
$informations['originLang'] = $confService->getFileObj()->getOriginLangData();
|
||||
$informations['meta'] = $confService->getFileObj()->getMetaData();
|
||||
}
|
||||
|
||||
// set information
|
||||
$informations['workspace'] = $mode;
|
||||
$informations['extPath'] = is_array($infos) ? $infos['extPath'] : $extPath;
|
||||
$informations['langFile'] = is_array($infos) ? $infos['langFile'] : $langFile;
|
||||
|
||||
// create and initialize the backup object
|
||||
try {
|
||||
$this->backupObj = GeneralUtility::makeInstance(FileBackupService::class);
|
||||
$this->backupObj->init('', $extConfig['pathBackup'], $extConfig['metaFile']);
|
||||
$this->backupObj->setVar($informations);
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* executes the deletion of backup files
|
||||
*
|
||||
* @throws LFException raised if a backup file couldnt be deleted
|
||||
* @param array $delFiles files as key and the language file as value
|
||||
* @return void
|
||||
*/
|
||||
public function execBackupDelete($delFiles) {
|
||||
// delete files
|
||||
try {
|
||||
foreach ($delFiles as $filename => $langFile) {
|
||||
$this->backupObj->deleteSpecFile($filename, '', $langFile);
|
||||
}
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* restores a backup file
|
||||
*
|
||||
* @throws LFException raised if some unneeded files couldnt be deleted
|
||||
* @throws Exception|LFException
|
||||
* @return void
|
||||
*/
|
||||
public function execBackupRestore() {
|
||||
/** @var ConfigurationService $confService */
|
||||
$confService = GeneralUtility::makeInstance(ConfigurationService::class);
|
||||
|
||||
// get vars
|
||||
$localLang = [];
|
||||
$meta = [];
|
||||
$origLang = $confService->getFileObj()->getLocalLangData();
|
||||
$origMeta = $confService->getFileObj()->getMetaData();
|
||||
$backupMeta = $this->backupObj->getMetaData();
|
||||
$backupLocalLang = $this->backupObj->getLocalLangData();
|
||||
$backupOriginLang = $this->backupObj->getOriginLangData();
|
||||
|
||||
// get differences between original and backup file
|
||||
$origDiff = Functions::getBackupDiff(1, $origLang, $backupLocalLang);
|
||||
$backupDiff = Functions::getBackupDiff(2, $origLang, $backupLocalLang);
|
||||
|
||||
if (is_countable($origDiff) ? count($origDiff) : 0) {
|
||||
foreach ($origDiff as $langKey => $data) {
|
||||
foreach ($data as $label => $value) {
|
||||
if (isset($backupLocalLang[$langKey][$label])) {
|
||||
$localLang[$langKey][$label] = $value;
|
||||
} else {
|
||||
$localLang[$langKey][$label] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_countable($backupDiff) ? count($backupDiff) : 0) {
|
||||
foreach ($backupDiff as $langKey => $data) {
|
||||
foreach ($data as $label => $value) {
|
||||
$localLang[$langKey][$label] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get differences between original and backup meta
|
||||
SgLib::fixMetaAttributes($origMeta);
|
||||
$origDiff = Functions::getMetaDiff(1, $origMeta, $backupMeta);
|
||||
$backupDiff = Functions::getMetaDiff(2, $origMeta, $backupMeta);
|
||||
|
||||
if (is_countable($origDiff) ? count($origDiff) : 0) {
|
||||
foreach ($origDiff as $label => $value) {
|
||||
if (isset($backupMeta[$label])) {
|
||||
$meta[$label] = $value;
|
||||
} else {
|
||||
$meta[$label] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_countable($backupDiff) ? count($backupDiff) : 0) {
|
||||
foreach ($backupDiff as $label => $value) {
|
||||
$meta[$label] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
// restore origins of languages
|
||||
$deleteFiles = [];
|
||||
foreach ($backupOriginLang as $langKey => $file) {
|
||||
$curFile = $confService->getFileObj()->getOriginLangData($langKey);
|
||||
if ($curFile != $file && $curFile != $confService->getFileObj()->getVar('absFile')) {
|
||||
$deleteFiles[] = $curFile;
|
||||
}
|
||||
$confService->getFileObj()->setOriginLangData($file, $langKey);
|
||||
}
|
||||
|
||||
// write modified language array
|
||||
try {
|
||||
$confService->setExecBackup(0);
|
||||
$confService->execWrite($localLang, $meta, TRUE);
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// delete all old files
|
||||
try {
|
||||
if (count($deleteFiles)) {
|
||||
SgLib::deleteFiles($deleteFiles);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new LFException(
|
||||
'failure.langfile.notDeleted',
|
||||
0,
|
||||
'(' . $e->getMessage() . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* exec the backup of files and deletes automatic old files
|
||||
*
|
||||
* @throws LFException raised if backup file cant written or unneeded files cant deleted
|
||||
* @return boolean
|
||||
*/
|
||||
public function execBackup() {
|
||||
$createdAt = [];
|
||||
/** @var ConfigurationService $confService */
|
||||
$confService = GeneralUtility::makeInstance(ConfigurationService::class);
|
||||
|
||||
// create backup object
|
||||
try {
|
||||
$this->initBackupObject('base', TRUE);
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// write backup file
|
||||
try {
|
||||
$this->backupObj->writeFile();
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// exec automatic deletion of backup files, if anzBackup greater zero
|
||||
$extConfig = $confService->getExtConfig();
|
||||
if ($extConfig['anzBackup'] <= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// get difference information
|
||||
$metaArray = $this->backupObj->getMetaInfos(3);
|
||||
$rows = count($metaArray);
|
||||
$dif = $rows - $extConfig['anzBackup'];
|
||||
|
||||
if ($dif <= 0) {
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// sort metaArray
|
||||
foreach ($metaArray as $key => $row) {
|
||||
$createdAt[$key] = $row['createdAt'];
|
||||
}
|
||||
array_multisort($createdAt, SORT_DESC, $metaArray);
|
||||
|
||||
// get filenames
|
||||
$files = array_keys($metaArray);
|
||||
$numberFiles = count($files);
|
||||
|
||||
// delete files
|
||||
try {
|
||||
for (; $dif > 0; --$dif, --$numberFiles) {
|
||||
$this->backupObj->deleteSpecFile($files[$numberFiles - 1]);
|
||||
}
|
||||
} catch (LFException $e) {
|
||||
try { // delete current written file
|
||||
$this->backupObj->deleteFile();
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileBackupService
|
||||
*/
|
||||
public function getBackupObj() {
|
||||
return $this->backupObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FileBackupService $backupObj
|
||||
*/
|
||||
public function setBackupObj($backupObj) {
|
||||
$this->backupObj = $backupObj;
|
||||
}
|
||||
}
|
||||
843
typo3conf/ext/lfeditor/Classes/Service/ConfigurationService.php
Normal file
843
typo3conf/ext/lfeditor/Classes/Service/ConfigurationService.php
Normal file
@@ -0,0 +1,843 @@
|
||||
<?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 Exception;
|
||||
use SGalinski\Lfeditor\Exceptions\DirectoryAccessRightsException;
|
||||
use SGalinski\Lfeditor\Exceptions\LFException;
|
||||
use SGalinski\Lfeditor\Utility\ExtensionUtility;
|
||||
use SGalinski\Lfeditor\Utility\Functions;
|
||||
use SGalinski\Lfeditor\Utility\SgLib;
|
||||
use SGalinski\Lfeditor\Utility\Typo3Lib;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Cache\CacheManager;
|
||||
use TYPO3\CMS\Core\Core\Environment;
|
||||
use TYPO3\CMS\Core\Localization\Locales;
|
||||
use TYPO3\CMS\Core\Package\PackageInterface;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
|
||||
|
||||
/**
|
||||
* Class ConfigurationService
|
||||
*/
|
||||
class ConfigurationService extends AbstractService {
|
||||
/**
|
||||
* @var array extension configuration
|
||||
* @see prepareConfig()
|
||||
*/
|
||||
protected $extConfig = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $invalidLanguages = '';
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $langArray = [];
|
||||
|
||||
/**
|
||||
* Available editing modes based on extension configuration
|
||||
*
|
||||
* @var array
|
||||
* @see prepareConfig()
|
||||
*/
|
||||
protected $availabledEditingModes = [];
|
||||
|
||||
/**
|
||||
* @var \SGalinski\Lfeditor\Service\FileBaseService
|
||||
*/
|
||||
protected $fileObj;
|
||||
|
||||
/**
|
||||
* @var \SGalinski\Lfeditor\Service\FileBaseService
|
||||
*/
|
||||
protected $convObj;
|
||||
|
||||
/**
|
||||
* @var PackageManager $packageManager
|
||||
*/
|
||||
private $packageManager;
|
||||
|
||||
public function __construct(PackageManager $packageManager) {
|
||||
parent::__construct();
|
||||
$this->packageManager = $packageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* preparation and check of the configuration
|
||||
*
|
||||
* Note that the default value will be set, if a option check fails.
|
||||
*
|
||||
* @return array
|
||||
* @throws DirectoryAccessRightsException
|
||||
*/
|
||||
public function prepareConfig() {
|
||||
if (!empty($this->extConfig)) {
|
||||
return $this->extConfig;
|
||||
}
|
||||
$this->extConfig = ExtensionUtility::getExtensionConfiguration();
|
||||
|
||||
// regular expressions
|
||||
if (!array_key_exists('searchRegex', $this->extConfig)) {
|
||||
$this->extConfig['searchRegex'] = '/^([a-z0-9_]*locallang[a-z0-9_-]*\.(php|xml)|[^\.]*\.xlf)$/i';
|
||||
} elseif (!\preg_match('/^\/.*\/.*$/', $this->extConfig['searchRegex'])) {
|
||||
$this->extConfig['searchRegex'] = '/^([a-z0-9_]*locallang[a-z0-9_-]*\.(php|xml)|[^\.]*\.xlf)$/i';
|
||||
}
|
||||
|
||||
if (!array_key_exists('searchRegex', $this->extConfig)) {
|
||||
$this->extConfig['extIgnore'] = '/^csh_.*$/';
|
||||
} elseif (!\preg_match('/^\/.*\/.*$/', $this->extConfig['extIgnore'])) {
|
||||
$this->extConfig['extIgnore'] = '/^csh_.*$/';
|
||||
}
|
||||
|
||||
if (!array_key_exists('extWhitelist', $this->extConfig)) {
|
||||
$this->extConfig['extWhitelist'] = '';
|
||||
} elseif (!\preg_match('/^\/.*\/.*$/', $this->extConfig['extWhitelist'])) {
|
||||
$this->extConfig['extWhitelist'] = '';
|
||||
}
|
||||
|
||||
$this->extConfig['execBackup'] = TRUE;
|
||||
$this->extConfig['viewSysExt'] = TRUE;
|
||||
$this->extConfig['viewGlobalExt'] = TRUE;
|
||||
$this->extConfig['viewLocalExt'] = TRUE;
|
||||
$this->extConfig['treeHide'] = TRUE;
|
||||
|
||||
$this->extConfig['viewStateExt'] = 1;
|
||||
$this->extConfig['numTextAreaRows'] = 5;
|
||||
$this->extConfig['numSiteConsts'] = 6;
|
||||
$this->extConfig['anzBackup'] = 5;
|
||||
// Options for number of constants presented on EditFile page
|
||||
$this->extConfig['numSiteConstsOptions']
|
||||
= ['100' => 100, '200' => 200, '500' => 500, '1000' => 1000, '1500' => 1500, '2000' => 2000];
|
||||
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
// paths and files (don't need to exist)
|
||||
$this->extConfig['pathBackup'] = Typo3Lib::fixFilePath(
|
||||
$pathSite . '/typo3temp/LFEditor/Backup/'
|
||||
) . '/';
|
||||
$this->extConfig['metaFile'] = Typo3Lib::fixFilePath(
|
||||
$pathSite . '/typo3temp/LFEditor/Backup/Meta.xml'
|
||||
);
|
||||
$this->extConfig['pathOverrideFiles'] = Typo3Lib::fixFilePath(
|
||||
$pathSite . '/typo3conf/LFEditor/OverrideFiles/'
|
||||
);
|
||||
|
||||
// files
|
||||
$this->extConfig['pathCSS'] = 'Resources/Public/StyleSheets/Lfeditor.css';
|
||||
|
||||
// languages (default is forbidden)
|
||||
if (!empty($this->extConfig['viewLanguages'])) {
|
||||
$langs = GeneralUtility::trimExplode(',', $this->extConfig['viewLanguages'], TRUE);
|
||||
unset($this->extConfig['viewLanguages']);
|
||||
|
||||
$availableLanguageKeys = [];
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
$languages = $locales->getLocales();
|
||||
|
||||
foreach ($languages as $language) {
|
||||
$availableLanguageKeys[$language] = TRUE;
|
||||
}
|
||||
|
||||
foreach ($langs as $lang) {
|
||||
if (!isset($availableLanguageKeys[$lang])) {
|
||||
if ($this->invalidLanguages === '') {
|
||||
$this->invalidLanguages = $lang;
|
||||
} else {
|
||||
$this->invalidLanguages .= ', ' . $lang;
|
||||
}
|
||||
}
|
||||
|
||||
if ($lang !== 'default') {
|
||||
$this->extConfig['viewLanguages'][] = $lang;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($this->extConfig['defaultLanguage'])) {
|
||||
$this->extConfig['defaultLanguage'] = 'default';
|
||||
} else {
|
||||
/** @var Locales $locales */
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
$availableLanguageKeys = $locales->getLanguages();
|
||||
if (!isset($availableLanguageKeys[$this->extConfig['defaultLanguage']])) {
|
||||
$this->extConfig['defaultLanguage'] = 'default';
|
||||
}
|
||||
}
|
||||
|
||||
// editing modes
|
||||
$this->availabledEditingModes = [
|
||||
'extension' => LocalizationUtility::translate('select.editingMode.extension', 'lfeditor'),
|
||||
// The l10n mode does not work for TYPO3 9 and the var folder and we do not want to fix this, yet
|
||||
//'l10n' => LocalizationUtility::translate('select.editingMode.l10n', 'lfeditor'),
|
||||
// Note: override should be always available because it is the fallback mode for editors
|
||||
'override' => LocalizationUtility::translate('select.editingMode.override', 'lfeditor'),
|
||||
];
|
||||
|
||||
if (isset($this->extConfig['editModeExtension']) && ((int) $this->extConfig['editModeExtension']) === 0) {
|
||||
unset($this->availabledEditingModes['extension']);
|
||||
}
|
||||
if (isset($this->extConfig['editModeL10n']) && ((int) $this->extConfig['editModeL10n']) === 0) {
|
||||
unset($this->availabledEditingModes['l10n']);
|
||||
}
|
||||
|
||||
if (isset($this->extConfig['pathAdditionalConfiguration']) && $this->extConfig['pathAdditionalConfiguration']) {
|
||||
$pathAdditionalConfiguration = Typo3Lib::fixFilePath($this->extConfig['pathAdditionalConfiguration']);
|
||||
$this->extConfig['pathAdditionalConfiguration'] = $pathSite . $pathAdditionalConfiguration;
|
||||
$additionalConfigurationDirectory = \dirname($this->extConfig['pathAdditionalConfiguration']);
|
||||
if (!\is_writable($additionalConfigurationDirectory)) {
|
||||
$message = 'Directory ' . $additionalConfigurationDirectory . ' is not writable.'
|
||||
. 'This directory should be writable in order to write the language override configuration '
|
||||
. 'file ' . $pathAdditionalConfiguration;
|
||||
throw new DirectoryAccessRightsException($message);
|
||||
}
|
||||
} else {
|
||||
/** @var \TYPO3\CMS\Core\Configuration\ConfigurationManager $configurationManager */
|
||||
$configurationManager = GeneralUtility::makeInstance(
|
||||
\TYPO3\CMS\Core\Configuration\ConfigurationManager::class
|
||||
);
|
||||
$this->extConfig['pathAdditionalConfiguration'] = $configurationManager->getAdditionalConfigurationFileLocation(
|
||||
);
|
||||
}
|
||||
|
||||
return $this->extConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds extensions for the extension menu selector.
|
||||
*
|
||||
* Note: $this->extConfig must be initialized before call of this method (method prepareConfig() must be executed before this method).
|
||||
*
|
||||
* @return array
|
||||
* @throws LFException raised if no extensions are found
|
||||
*/
|
||||
public function menuExtList() {
|
||||
// search extensions
|
||||
$tmpExtList = [];
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
try {
|
||||
// local extensions
|
||||
|
||||
$labelLocal = LocalizationUtility::translate('ext.local', 'lfeditor');
|
||||
$labelSystem = LocalizationUtility::translate('ext.system', 'lfeditor');
|
||||
$tmpExtList[$labelLocal] = [];
|
||||
$tmpExtList[$labelSystem] = [];
|
||||
foreach ($this->packageManager->getAvailablePackages() as $package) {
|
||||
if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
|
||||
// local extensions
|
||||
if ($this->extConfig['viewLocalExt'] && \count(
|
||||
$content = Functions::searchExtensions(
|
||||
$pathSite . Typo3Lib::PATH_LOCAL_EXT,
|
||||
$this->extConfig['viewStateExt'],
|
||||
$this->extConfig['extIgnore'],
|
||||
$this->extConfig['extWhitelist']
|
||||
)
|
||||
)
|
||||
) {
|
||||
$tmpExtList[LocalizationUtility::translate('ext.local', 'lfeditor')] = $content;
|
||||
}
|
||||
|
||||
// global extensions
|
||||
if ($this->extConfig['viewGlobalExt'] && \is_dir(Typo3Lib::PATH_GLOBAL_EXT) && \count(
|
||||
$content = Functions::searchExtensions(
|
||||
$pathSite . Typo3Lib::PATH_GLOBAL_EXT,
|
||||
$this->extConfig['viewStateExt'],
|
||||
$this->extConfig['extIgnore'],
|
||||
$this->extConfig['extWhitelist']
|
||||
)
|
||||
)
|
||||
) {
|
||||
$tmpExtList[LocalizationUtility::translate('ext.global', 'lfeditor')] = $content;
|
||||
}
|
||||
|
||||
// system extensions
|
||||
if ($this->extConfig['viewSysExt'] && \count(
|
||||
$content = Functions::searchExtensions(
|
||||
$pathSite . Typo3Lib::PATH_SYS_EXT,
|
||||
$this->extConfig['viewStateExt'],
|
||||
$this->extConfig['extIgnore'],
|
||||
$this->extConfig['extWhitelist']
|
||||
)
|
||||
)
|
||||
) {
|
||||
$tmpExtList[LocalizationUtility::translate('ext.system', 'lfeditor')] = $content;
|
||||
}
|
||||
} else {
|
||||
// check, if the directory/extension should be saved
|
||||
if ($this->extConfig['extWhitelist'] !== ''
|
||||
&& !preg_match($this->extConfig['extWhitelist'], $package->getPackageKey())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->extConfig['viewLocalExt'] && $this->isPackageLocal($package)) {
|
||||
$tmpExtList[$labelLocal][] = $package->getPackagePath();
|
||||
}
|
||||
|
||||
if ($this->extConfig['viewSysExt'] && $this->isPackageSystemPackage($package)) {
|
||||
$tmpExtList[$labelSystem][] = $package->getPackagePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
sort($tmpExtList[$labelLocal]);
|
||||
sort($tmpExtList[$labelSystem]);
|
||||
} catch (Exception $e) {
|
||||
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
|
||||
}
|
||||
|
||||
// check extension array
|
||||
if (!\count($tmpExtList)) {
|
||||
throw new LFException('failure.search.noExtension');
|
||||
}
|
||||
|
||||
// create list
|
||||
$extList = Functions::prepareExtList($tmpExtList);
|
||||
$extList = \array_merge([$pathSite . 'fileadmin' => 'fileadmin/', ''], $extList);
|
||||
|
||||
foreach ($extList as $extAddress => $extLabel) {
|
||||
unset($extList[$extAddress]);
|
||||
$fixedExtAddress = Typo3Lib::fixFilePath($extAddress);
|
||||
$extList[$fixedExtAddress] = $extLabel;
|
||||
}
|
||||
return $extList;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if package is system package
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @return bool
|
||||
*/
|
||||
private function isPackageLocal(PackageInterface $package): bool {
|
||||
return $package->getPackageMetaData()->getPackageType() === 'typo3-cms-extension';
|
||||
}
|
||||
|
||||
/**
|
||||
* checks if package is system package
|
||||
*
|
||||
* @param PackageInterface $package
|
||||
* @return bool
|
||||
*/
|
||||
private function isPackageSystemPackage(PackageInterface $package): bool {
|
||||
return $package->getPackageMetaData()->isFrameworkType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds language files of extension which address is passed as $extensionAddress parameter.
|
||||
*
|
||||
* @param string $extensionAddress
|
||||
* @return array
|
||||
* @throws LFException
|
||||
*/
|
||||
public function menuLangFileList($extensionAddress) {
|
||||
// check
|
||||
if (empty($extensionAddress)) {
|
||||
throw new LFException('failure.search.noLangFile', 1);
|
||||
}
|
||||
|
||||
// search and prepare files
|
||||
try {
|
||||
/** @var array $files */
|
||||
$files = SgLib::searchFiles(
|
||||
$extensionAddress,
|
||||
$this->extConfig['searchRegex']
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
throw new LFException(
|
||||
'failure.search.noLangFile',
|
||||
1,
|
||||
'(' . $e->getMessage() . ')'
|
||||
);
|
||||
}
|
||||
|
||||
$fileArray = [];
|
||||
if (\count($files)) {
|
||||
foreach ($files as $file) {
|
||||
$filename = \substr($file, \strlen($extensionAddress) + 1);
|
||||
$fileArray[$filename] = $filename;
|
||||
}
|
||||
} else {
|
||||
throw new LFException('failure.search.noLangFile', 1);
|
||||
}
|
||||
|
||||
return $fileArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for the language menu selector
|
||||
*
|
||||
* @param array $langData language data
|
||||
* @param string $default optional default value (if you dont want a default let it empty)
|
||||
* @param BackendUserAuthentication $backendUser
|
||||
* @param bool $translatedLanguagesOnly looks only for translated languages
|
||||
* @return array
|
||||
* @throws LFException
|
||||
*/
|
||||
public function menuLangList(
|
||||
$langData,
|
||||
$default = '',
|
||||
BackendUserAuthentication $backendUser = NULL,
|
||||
$translatedLanguagesOnly = FALSE
|
||||
) {
|
||||
// build languages
|
||||
$languageArray = $this->getLangArray($backendUser);
|
||||
$languageList = [];
|
||||
foreach ($languageArray as $language) {
|
||||
$constCount = 0;
|
||||
if (array_key_exists($language, $langData) && \is_array($langData[$language])) {
|
||||
$constCount = \count($langData[$language]);
|
||||
}
|
||||
if ($translatedLanguagesOnly && $constCount <= 0) {
|
||||
continue;
|
||||
}
|
||||
$languageLabel = $language;
|
||||
if ($language === 'default') {
|
||||
$languageLabel = 'en';
|
||||
}
|
||||
$languageList[$language] = $languageLabel . ' (' . $constCount . ' ' .
|
||||
LocalizationUtility::translate('const.consts', 'lfeditor') . ')';
|
||||
}
|
||||
|
||||
// add default value
|
||||
if (!empty($default)) {
|
||||
$languageList = \array_merge(['###default###' => $default], $languageList);
|
||||
}
|
||||
return $languageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list (array) of constants.
|
||||
*
|
||||
* @param array $langData language data
|
||||
* @param string $default name of default entry
|
||||
* @return array
|
||||
*/
|
||||
public function menuConstList($langData, $default) {
|
||||
// generate constant list
|
||||
$constList = [];
|
||||
$languages = Functions::buildLangArray();
|
||||
foreach ($languages as $language) {
|
||||
if (!\array_key_exists($language, $langData) || !\is_array($langData[$language]) || !\count(
|
||||
$langData[$language]
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var array $constants */
|
||||
$constants = \array_keys($langData[$language]);
|
||||
foreach ($constants as $constant) {
|
||||
$constList[\str_replace('#', '$*-*$', $constant)] = $constant;
|
||||
}
|
||||
}
|
||||
|
||||
// sorting and default entry
|
||||
\asort($constList);
|
||||
$constList = \array_merge(['###default###' => $default], $constList);
|
||||
return $constList;
|
||||
}
|
||||
|
||||
/**
|
||||
* creates and instantiates a file object
|
||||
*
|
||||
* Naming Convention:
|
||||
* File<workspace><filetype>Service
|
||||
*
|
||||
*
|
||||
* @param string $langFile
|
||||
* @param string $extPath
|
||||
* @param bool $flagReadFile
|
||||
* @return void
|
||||
* @throws LFException
|
||||
*/
|
||||
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');
|
||||
}
|
||||
/** @var \SGalinski\Lfeditor\Service\FileService $originalFileObject */
|
||||
$originalFileObject = GeneralUtility::makeInstance($className);
|
||||
$originalFileObject->init($langFile, $extPath, '');
|
||||
|
||||
try {
|
||||
if ($this->session->getDataByKey('editingMode') === 'override') {
|
||||
/** @var FileOverrideService $overrideFileObj */
|
||||
$overrideFileObj = GeneralUtility::makeInstance(FileOverrideService::class);
|
||||
$overrideFileObj->init($originalFileObject, '', '');
|
||||
$this->fileObj = $overrideFileObj;
|
||||
} else {
|
||||
$this->fileObj = $originalFileObject;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new LFException('failure.failure', 0, '(' . $e->getMessage() . ')');
|
||||
}
|
||||
|
||||
if ($flagReadFile) {
|
||||
$this->fileObj->readFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes writing of language files
|
||||
*
|
||||
*
|
||||
* @param array $modArray changes (constants with empty values will be deleted)
|
||||
* @param array $modMetaArray meta changes (indexes with empty values will be deleted)
|
||||
* @param boolean $forceDel set to true if you want delete default constants
|
||||
* @param array|NULL $editedLanguages
|
||||
* @return void
|
||||
* @throws LFException if file could not be written or some param criteria is not correct
|
||||
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execWrite($modArray, $modMetaArray = [], $forceDel = FALSE, $editedLanguages = NULL) {
|
||||
// checks
|
||||
if (!\is_array($modArray)) {
|
||||
throw new LFException('failure.file.notWritten');
|
||||
}
|
||||
|
||||
$fileObject = $this->getFileObj();
|
||||
if ($fileObject === NULL) {
|
||||
$this->initFileObject(
|
||||
$this->session->getDataByKey('languageFileSelection'),
|
||||
$this->session->getDataByKey('extensionSelection')
|
||||
);
|
||||
$fileObject = $this->getFileObj();
|
||||
}
|
||||
|
||||
// execute backup
|
||||
$extConfig = $this->getExtConfig();
|
||||
if ($extConfig['execBackup'] && $this->session->getDataByKey('editingMode') === 'extension') {
|
||||
/** @var BackupService $backupService */
|
||||
$backupService = GeneralUtility::makeInstance(BackupService::class);
|
||||
$backupService->execBackup();
|
||||
}
|
||||
|
||||
if (!$this->session->getDataByKey('defaultLanguagePermission')) {
|
||||
unset($modArray[$this->extConfig['defaultLanguage']]);
|
||||
}
|
||||
// set new language data
|
||||
foreach ($modArray as $langKey => $data) {
|
||||
if (\is_array($data)) {
|
||||
foreach ($data as $const => $value) {
|
||||
$fileObject->setLocalLangData($const, $value, $langKey, $forceDel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set changed meta data
|
||||
foreach ($modMetaArray as $metaIndex => $metaValue) {
|
||||
$fileObject->setMetaData($metaIndex, $metaValue);
|
||||
}
|
||||
|
||||
// write new language data
|
||||
$fileObject->writeFile($editedLanguages);
|
||||
|
||||
// delete possible language files
|
||||
$absFile = $fileObject->getVar('absFile');
|
||||
$originLang = $fileObject->getOriginLangData();
|
||||
$emptyFiles = [];
|
||||
foreach ($originLang as $lang => $origin) {
|
||||
if ($origin === $absFile || !\is_file($origin)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$langData = $fileObject->getLocalLangData($lang);
|
||||
if (\is_array($langData) && !\count($langData)) {
|
||||
$emptyFiles[] = $origin;
|
||||
}
|
||||
}
|
||||
|
||||
// delete all empty language files
|
||||
try {
|
||||
if (\count($emptyFiles)) {
|
||||
SgLib::deleteFiles($emptyFiles);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new LFException('failure.langfile.notDeleted', 0, '(' . $e->getMessage() . ')');
|
||||
}
|
||||
|
||||
/** @var CacheManager $cacheManager */
|
||||
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
|
||||
$cacheManager->getCache('l10n')->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits or merges a language file
|
||||
*
|
||||
* @param array $langModes language shortcuts and their mode (1 = splitNormal, 2 = merge)
|
||||
* @return void
|
||||
* @throws LFException raised if file could not be split or merged (i.e. empty langModes)
|
||||
* @throws Exception|LFException
|
||||
*/
|
||||
public function execSplitFile(array $langModes) {
|
||||
// check
|
||||
if (!\is_array($langModes) || !\count($langModes)) {
|
||||
throw new LFException('failure.langfile.notSplittedOrMerged');
|
||||
}
|
||||
|
||||
// rewrite originLang array
|
||||
$delLangFiles = [];
|
||||
foreach ($langModes as $langKey => $mode) {
|
||||
if ($langKey === 'default') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get origin of this language
|
||||
$origin = $this->fileObj->getOriginLangData($langKey);
|
||||
|
||||
// split or merge
|
||||
if ($mode === 1) {
|
||||
// nothing to do if the file is already a normal splitted file
|
||||
if (Typo3Lib::checkFileLocation($origin) !== 'l10n') {
|
||||
if ($this->fileObj->checkLocalizedFile(\basename($origin), $langKey)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// delete file if was it a l10n file
|
||||
if ($this->fileObj->checkLocalizedFile(\basename($origin), $langKey)) {
|
||||
$delLangFiles[] = $origin;
|
||||
}
|
||||
|
||||
$origin = Typo3Lib::fixFilePath(
|
||||
\dirname($this->fileObj->getVar('absFile')) .
|
||||
'/' . $this->fileObj->nameLocalizedFile($langKey)
|
||||
);
|
||||
} elseif ($mode === 2) {
|
||||
if ($this->fileObj->checkLocalizedFile(\basename($origin), $langKey)) {
|
||||
$delLangFiles[] = $origin;
|
||||
}
|
||||
$origin = $this->fileObj->getVar('absFile');
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
$this->fileObj->setOriginLangData($origin, $langKey);
|
||||
}
|
||||
|
||||
// write new language file
|
||||
$this->execWrite([]);
|
||||
|
||||
// delete old localized files, if single mode was selected
|
||||
try {
|
||||
if (\count($delLangFiles)) {
|
||||
SgLib::deleteFiles($delLangFiles);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new LFException(
|
||||
'failure.langfile.notDeleted',
|
||||
0,
|
||||
'(' . $e->getMessage() . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* converts language files between different formats
|
||||
*
|
||||
* @param string $type new file format
|
||||
* @param string $newFile new relative file
|
||||
* @return void
|
||||
* @throws Exception|LFException
|
||||
* @throws LFException raised if transforming or deletion of old files failed
|
||||
*/
|
||||
public function execTransform($type, $newFile) {
|
||||
// copy current object to convObj
|
||||
$this->convObj = clone $this->fileObj;
|
||||
unset($this->fileObj);
|
||||
|
||||
// init new language file object (dont try to read file)
|
||||
$this->initFileObject($newFile, $this->convObj->getVar('absPath'), FALSE);
|
||||
|
||||
// recreate originLang
|
||||
$origins = $this->convObj->getOriginLangData();
|
||||
foreach ($origins as $langKey => $file) {
|
||||
// localized or merged language origin
|
||||
$newFile = SgLib::setFileExtension($type, $file);
|
||||
if ($this->convObj->getVar('workspace') === 'base') {
|
||||
if ($this->convObj->checkLocalizedFile(\basename($file), $langKey)) {
|
||||
$newFile = \dirname($file) . '/' . $this->fileObj->nameLocalizedFile($langKey);
|
||||
}
|
||||
}
|
||||
$this->fileObj->setOriginLangData(Typo3Lib::fixFilePath($newFile), $langKey);
|
||||
}
|
||||
|
||||
// recreate meta data
|
||||
$meta = $this->convObj->getMetaData();
|
||||
foreach ($meta as $metaIndex => $metaValue) {
|
||||
$this->fileObj->setMetaData($metaIndex, $metaValue);
|
||||
}
|
||||
|
||||
// write new language file
|
||||
$this->extConfig['execBackup'] = 0;
|
||||
$this->execWrite($this->convObj->getLocalLangData());
|
||||
|
||||
// delete all old files
|
||||
try {
|
||||
$delFiles = $this->convObj->getOriginLangData();
|
||||
if (\is_array($delFiles) && \count($delFiles)) {
|
||||
SgLib::deleteFiles($delFiles);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
throw new LFException(
|
||||
'failure.langfile.notDeleted',
|
||||
0,
|
||||
'(' . $e->getMessage() . ')'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*************************
|
||||
* Getters and setters *
|
||||
*************************/
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getExtConfig() {
|
||||
return $this->extConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets 'execBackup' parameter of extension configuration.
|
||||
*
|
||||
* @param int $execBackup
|
||||
*/
|
||||
public function setExecBackup($execBackup) {
|
||||
$this->extConfig['execBackup'] = $execBackup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $extConfig
|
||||
*/
|
||||
public function setExtConfig($extConfig) {
|
||||
$this->extConfig = $extConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getInvalidLanguages() {
|
||||
return $this->invalidLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $invalidLanguages
|
||||
*/
|
||||
public function setInvalidLanguages($invalidLanguages) {
|
||||
$this->invalidLanguages = $invalidLanguages;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableEditingModes() {
|
||||
return $this->availabledEditingModes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FileBaseService
|
||||
*/
|
||||
public function getFileObj() {
|
||||
return $this->fileObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param FileBasePHPService $fileObj
|
||||
*/
|
||||
public function setFileObj($fileObj) {
|
||||
$this->fileObj = $fileObj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns array of system languages.
|
||||
*
|
||||
* @param BackendUserAuthentication $backendUser
|
||||
* @return array
|
||||
* @throws LFException
|
||||
*/
|
||||
public function getLangArray(BackendUserAuthentication $backendUser = NULL) {
|
||||
if ($this->invalidLanguages !== '') {
|
||||
throw new LFException('failure.config.invalidLanguage', 0, $this->invalidLanguages);
|
||||
}
|
||||
|
||||
if (empty($this->langArray)) {
|
||||
$languages = Functions::buildLangArray($this->extConfig['viewLanguages']);
|
||||
$languages = $this->narrowToUserLanguages($languages, $backendUser);
|
||||
|
||||
if (!\in_array('default', $languages, TRUE)) {
|
||||
$languages = \array_merge(['default'], $languages);
|
||||
}
|
||||
if (!\in_array($this->extConfig['defaultLanguage'], $languages, TRUE)) {
|
||||
$languages = \array_merge([$this->extConfig['defaultLanguage']], $languages);
|
||||
}
|
||||
$this->langArray = $languages;
|
||||
}
|
||||
return $this->langArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $langArray
|
||||
*/
|
||||
public function setLangArray($langArray) {
|
||||
$this->langArray = $langArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the path to the configured AdditionalConfiguration.php file
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAdditionalConfigurationFilePath() {
|
||||
return $this->extConfig['pathAdditionalConfiguration'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Narrows $languages to user languages if user is not admin.
|
||||
*
|
||||
* @param array $languages
|
||||
* @param BackendUserAuthentication $backendUser
|
||||
* @return array
|
||||
*/
|
||||
protected function narrowToUserLanguages(array $languages, BackendUserAuthentication $backendUser) {
|
||||
if ($backendUser !== NULL && !$backendUser->isAdmin()) {
|
||||
$sysLanguageService = GeneralUtility::makeInstance(SysLanguageService::class);
|
||||
foreach ($languages as $index => $languageFlag) {
|
||||
$sysLanguageId = $sysLanguageService->getSysLanguageIdByFlag($languageFlag);
|
||||
if ($languageFlag === 'default'
|
||||
|| ($sysLanguageId !== NULL && $backendUser->checkLanguageAccess($sysLanguageId))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
unset($languages[$index]);
|
||||
}
|
||||
}
|
||||
return $languages;
|
||||
}
|
||||
}
|
||||
478
typo3conf/ext/lfeditor/Classes/Service/FileBackupService.php
Normal file
478
typo3conf/ext/lfeditor/Classes/Service/FileBackupService.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?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 Exception;
|
||||
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;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
|
||||
/**
|
||||
* backup class
|
||||
*/
|
||||
class FileBackupService extends FileService {
|
||||
/**
|
||||
* @var string $metaFile
|
||||
*/
|
||||
private $metaFile;
|
||||
|
||||
/**
|
||||
* @var array $metaArray
|
||||
*/
|
||||
private $metaArray = [];
|
||||
|
||||
/**
|
||||
* @var string $extName
|
||||
*/
|
||||
private $extName = '';
|
||||
|
||||
/**
|
||||
* @var string $extPath
|
||||
*/
|
||||
private $extPath = '';
|
||||
|
||||
/**
|
||||
* @var string $langFile
|
||||
*/
|
||||
private $langFile = '';
|
||||
|
||||
/**
|
||||
* extended init
|
||||
*
|
||||
* @throws LFException raised if the meta file cant be correctly read
|
||||
* @param string $file name of the file (can be a path, if you need this (no check))
|
||||
* @param string $path path to the file
|
||||
* @param string $metaFile absolute path to the meta file (includes filename)
|
||||
* @return void
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
// init
|
||||
$this->setVar(['metaFile' => $metaFile]);
|
||||
parent::init($file, $path, $metaFile);
|
||||
|
||||
// read meta file
|
||||
try {
|
||||
if (is_file($this->metaFile)) {
|
||||
$this->readMetaFile();
|
||||
}
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
#####################
|
||||
###### Set/Get ######
|
||||
#####################
|
||||
|
||||
/**
|
||||
* sets information
|
||||
*
|
||||
* structure:
|
||||
* $infos["metaFile"] = absolute path to the meta file (includes filename)
|
||||
* $infos["extPath"] = extension path
|
||||
* $infos["langFile"] = language file
|
||||
*
|
||||
* @param array $informations information (see above)
|
||||
* @return void
|
||||
*/
|
||||
public function setVar($informations) {
|
||||
if (!empty($informations['metaFile'])) {
|
||||
$this->metaFile = Typo3Lib::fixFilePath($informations['metaFile']);
|
||||
}
|
||||
|
||||
if (!empty($informations['extPath'])) {
|
||||
$this->extPath = Typo3Lib::fixFilePath($informations['extPath']);
|
||||
$this->extName = basename($informations['extPath']);
|
||||
}
|
||||
|
||||
if (!empty($informations['langFile'])) {
|
||||
$this->langFile = Typo3Lib::fixFilePath($informations['langFile']);
|
||||
}
|
||||
|
||||
parent::setVar($informations);
|
||||
}
|
||||
|
||||
/**
|
||||
* returns requested information
|
||||
*
|
||||
* @param string $info
|
||||
* @return mixed
|
||||
*/
|
||||
public function getVar($info) {
|
||||
if ($info == 'metaFile') {
|
||||
return $this->metaFile;
|
||||
} elseif ($info == 'extName') {
|
||||
return $this->extName;
|
||||
} elseif ($info == 'extPath') {
|
||||
return $this->extPath;
|
||||
} elseif ($info == 'langFile') {
|
||||
return $this->langFile;
|
||||
} else {
|
||||
return parent::getVar($info);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns meta information about backup files
|
||||
*
|
||||
* Modes:
|
||||
* - 0 => full meta information (default)
|
||||
* - 1 => only meta information of given extension key
|
||||
* - 2 => only meta information of given extension key and workspace
|
||||
* - 3 => only meta information of given extension key, workspace and language file
|
||||
*
|
||||
* @param integer $mode
|
||||
* @param string $extName extension Name (default = $this->extName)
|
||||
* @param string $workspace (default = $this->workspace)
|
||||
* @param string $langFile language file (default = $this->langFile)
|
||||
* @return array
|
||||
*/
|
||||
public function getMetaInfos($mode = 0, $extName = '', $workspace = '', $langFile = '') {
|
||||
$extName = empty($extName) ? $this->extName : $extName;
|
||||
$langFile = empty($langFile) ? $this->langFile : $langFile;
|
||||
$workspace = empty($workspace) ? $this->workspace : $workspace;
|
||||
|
||||
// build return value
|
||||
if (!$mode) {
|
||||
return $this->metaArray;
|
||||
} elseif ($mode == 1) {
|
||||
return $this->metaArray[$extName] ?? [];
|
||||
} elseif ($mode == 2) {
|
||||
return $this->metaArray[$extName][$workspace] ?? [];
|
||||
} elseif ($mode == 3) {
|
||||
return $this->metaArray[$extName][$workspace][$langFile] ?? [];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rewrites current meta information array with the given equivalent
|
||||
*
|
||||
* Modes:
|
||||
* - 0 => full meta information (default)
|
||||
* - 1 => only meta information of given extension key
|
||||
* - 2 => only meta information of given extension key and workspace
|
||||
* - 3 => only meta information of given extension key, workspace and language file
|
||||
*
|
||||
* @param array $metaArray meta information
|
||||
* @param integer $mode
|
||||
* @param string $extName extension Name (default = $this->extName)
|
||||
* @param string $workspace (default = $this->workspace)
|
||||
* @param string $langFile language file (default = $this->langFile)
|
||||
* @return void
|
||||
*/
|
||||
private function setMetaInfos($metaArray, $mode = 0, $extName = '', $workspace = '', $langFile = '') {
|
||||
$extName = empty($extName) ? $this->extName : $extName;
|
||||
$langFile = empty($langFile) ? $this->langFile : $langFile;
|
||||
$workspace = empty($workspace) ? $this->workspace : $workspace;
|
||||
|
||||
// build new meta information array
|
||||
if (is_array($metaArray) && count($metaArray)) {
|
||||
if (!$mode) {
|
||||
$this->metaArray = $metaArray;
|
||||
} elseif ($mode == 1) {
|
||||
$this->metaArray[$extName] = $metaArray;
|
||||
} elseif ($mode == 2) {
|
||||
$this->metaArray[$extName][$workspace] = $metaArray;
|
||||
} elseif ($mode == 3) {
|
||||
$this->metaArray[$extName][$workspace][$langFile] = $metaArray;
|
||||
}
|
||||
} else {
|
||||
if (!$mode) {
|
||||
unset($this->metaArray);
|
||||
} elseif ($mode == 1) {
|
||||
unset($this->metaArray[$extName]);
|
||||
} elseif ($mode == 2) {
|
||||
unset($this->metaArray[$extName][$workspace]);
|
||||
} elseif ($mode == 3) {
|
||||
unset($this->metaArray[$extName][$workspace][$langFile]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
###############################
|
||||
###### Meta FileService Methods ######
|
||||
###############################
|
||||
|
||||
/**
|
||||
* reads the meta information file and parses the content into $this->metaArray
|
||||
*
|
||||
* @throws LFException raised if no meta content was generated
|
||||
* @return void
|
||||
*/
|
||||
private function readMetaFile() {
|
||||
// read file and parse xml to array
|
||||
$metaArray = GeneralUtility::xml2array(file_get_contents($this->metaFile));
|
||||
if (!is_array($metaArray)) {
|
||||
throw new LFException('failure.backup.metaFile.notRead');
|
||||
}
|
||||
|
||||
$this->metaArray = $metaArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* generate meta XML
|
||||
*
|
||||
* @return string meta information (xml)
|
||||
*/
|
||||
private function genMetaXML() {
|
||||
$options = [];
|
||||
// define assocTagNames
|
||||
$options['parentTagMap'] = [
|
||||
'' => 'extKey',
|
||||
'extKey' => 'workspace',
|
||||
'workspace' => 'langFile',
|
||||
'langFile' => 'file',
|
||||
];
|
||||
return GeneralUtility::array2xml($this->getMetaInfos(), '', 0, 'LFBackupMeta', 0, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* writes the meta information file
|
||||
*
|
||||
* @throws LFException raised if the meta file cant be written
|
||||
* @return void
|
||||
*/
|
||||
private function writeMetaFile() {
|
||||
$metaXML = $this->genMetaXML();
|
||||
if (empty($metaXML)) {
|
||||
throw new LFException('failure.backup.metaFile.notWritten');
|
||||
}
|
||||
|
||||
if (!GeneralUtility::writeFile($this->metaFile, $this->getXMLHeader() . $metaXML)) {
|
||||
throw new LFException('failure.backup.metaFile.notWritten');
|
||||
}
|
||||
}
|
||||
|
||||
#################################
|
||||
###### Backup FileService Methods ######
|
||||
#################################
|
||||
|
||||
/**
|
||||
* generates the xml header
|
||||
*
|
||||
* @return string xml header
|
||||
*/
|
||||
private function getXMLHeader() {
|
||||
return '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* removes the meta information entry and the backup file
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $extName (default = $this->extName)
|
||||
* @param string $langFile (default = $this->langFile)
|
||||
* @throws Exception|LFException
|
||||
* @throws LFException
|
||||
* @return void
|
||||
*/
|
||||
public function deleteSpecFile($filename, $extName = '', $langFile = '') {
|
||||
// get needed meta information
|
||||
$extName = empty($extName) ? $this->extName : $extName;
|
||||
$langFile = empty($langFile) ? $this->langFile : $langFile;
|
||||
$metaArray = $this->getMetaInfos(3, $extName, '', $langFile);
|
||||
|
||||
// check backup file
|
||||
if (!isset($metaArray[$filename])) {
|
||||
throw new LFException('failure.backup.notDeleted');
|
||||
}
|
||||
|
||||
// get file
|
||||
$backupPath = $metaArray[$filename]['pathBackup'];
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
$file = GeneralUtility::fixWindowsFilePath($pathSite . $backupPath . '/' . $filename);
|
||||
|
||||
// build new meta information file
|
||||
unset($metaArray[$filename]);
|
||||
if (!count($metaArray)) {
|
||||
$metaArray = NULL;
|
||||
}
|
||||
$this->setMetaInfos($metaArray, 3, $extName, '', $langFile);
|
||||
|
||||
$extMetaArray = $this->getMetaInfos(2, $extName);
|
||||
if (!count($extMetaArray)) {
|
||||
$extMetaArray = NULL;
|
||||
}
|
||||
$this->setMetaInfos($extMetaArray, 2, $extName);
|
||||
|
||||
// write meta information
|
||||
try {
|
||||
$this->writeMetaFile();
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// delete backup file
|
||||
try {
|
||||
SgLib::deleteFiles([$file]);
|
||||
} catch (Exception $e) {
|
||||
throw new LFException(
|
||||
'failure.backup.notDeleted',
|
||||
0,
|
||||
'(' . $e->getMessage() . ')',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* wrapper for deleteSpecFile()
|
||||
*
|
||||
* @throws LFException raised if the backup or meta file cant be written
|
||||
* @return void
|
||||
*/
|
||||
public function deleteFile() {
|
||||
try {
|
||||
$this->deleteSpecFile($this->relFile);
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a backup file
|
||||
*
|
||||
* @throws LFException raised if the backup file can't be read
|
||||
* @return void
|
||||
*/
|
||||
public function readFile() {
|
||||
if (!is_file($this->absFile)) {
|
||||
throw new LFException('failure.backup.notRead');
|
||||
}
|
||||
|
||||
// read file and transform from xml to array
|
||||
$phpArray = GeneralUtility::xml2array(file_get_contents($this->absFile));
|
||||
if (!is_array($phpArray)) {
|
||||
throw new LFException('failure.backup.notRead');
|
||||
}
|
||||
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
// read array
|
||||
$localLang = $originLang = [];
|
||||
foreach ($phpArray['data'] as $langKey => $informations) {
|
||||
// read origin
|
||||
try {
|
||||
$originLang[$langKey] = Typo3Lib::transTypo3File($informations['meta']['origin'], TRUE);
|
||||
} catch (Exception $e) {
|
||||
$originLang[$langKey] = $pathSite . $informations['meta']['origin'];
|
||||
}
|
||||
|
||||
// read data
|
||||
if (is_array($informations['langData'])) {
|
||||
foreach ($informations['langData'] as $const => $value) {
|
||||
$localLang[$langKey][$const] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
if (!count($localLang) || !count($originLang)) {
|
||||
throw new LFException('failure.backup.notRead');
|
||||
}
|
||||
|
||||
$this->localLang = $localLang;
|
||||
$this->originLang = $originLang;
|
||||
$this->meta = $phpArray['meta'];
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the final Content
|
||||
*
|
||||
* @return string prepared content (xml)
|
||||
*/
|
||||
private function prepareBackupContent() {
|
||||
$phpArray = [];
|
||||
$options = [];
|
||||
// set meta
|
||||
$phpArray['meta'] = $this->meta;
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
// set array
|
||||
foreach ($this->originLang as $lang => $origin) {
|
||||
// set origin
|
||||
try {
|
||||
$phpArray['data'][$lang]['meta']['origin'] = Typo3Lib::transTypo3File($origin, FALSE);
|
||||
} catch (Exception $e) {
|
||||
$phpArray['data'][$lang]['meta']['origin'] = substr($origin, strlen($pathSite));
|
||||
}
|
||||
|
||||
// set data
|
||||
if (\array_key_exists($lang, $this->localLang) && \is_array($this->localLang[$lang])) {
|
||||
foreach ($this->localLang[$lang] as $labelKey => $labelVal) {
|
||||
$phpArray['data'][$lang]['langData'][$labelKey] = $labelVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// define assocTagNames
|
||||
$options['parentTagMap'] = [
|
||||
'data' => 'languageKey',
|
||||
'langData' => 'label'
|
||||
];
|
||||
|
||||
// get xml
|
||||
return GeneralUtility::array2xml($phpArray, '', 0, 'LFBackup', 0, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the backup file and writes the new meta information
|
||||
*
|
||||
* @param array | NULL $editedLanguages
|
||||
* @return array raised if meta file cant be written
|
||||
* @throws Exception
|
||||
* @throws LFException
|
||||
* @return array
|
||||
*/
|
||||
protected function prepareFileContents($editedLanguages = NULL) {
|
||||
$backupFiles = [];
|
||||
// get content
|
||||
$xml = $this->prepareBackupContent();
|
||||
|
||||
// get and set name of backup
|
||||
$backupName = 'locallang_' . substr(md5(md5($xml)), 0, 10) . '.bak';
|
||||
$this->setVar(['relFile' => $backupName]);
|
||||
$pathSite = Environment::getPublicPath() . '/';
|
||||
// get new meta information
|
||||
$metaArray = $this->getMetaInfos(3);
|
||||
$metaArray[$this->relFile]['createdAt'] = time();
|
||||
$metaArray[$this->relFile]['pathBackup'] = str_replace($pathSite, '', $this->absPath);
|
||||
$this->setMetaInfos($metaArray, 3);
|
||||
|
||||
// write meta information file
|
||||
try {
|
||||
$this->writeMetaFile();
|
||||
} catch (LFException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$backupFiles[$this->absPath . $this->relFile] = $this->getXMLHeader() . $xml;
|
||||
return $backupFiles;
|
||||
}
|
||||
}
|
||||
256
typo3conf/ext/lfeditor/Classes/Service/FileBasePHPService.php
Normal file
256
typo3conf/ext/lfeditor/Classes/Service/FileBasePHPService.php
Normal file
@@ -0,0 +1,256 @@
|
||||
<?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;
|
||||
|
||||
/**
|
||||
* base workspace class (php)
|
||||
*/
|
||||
class FileBasePHPService extends FileBaseService {
|
||||
/**
|
||||
* extended init
|
||||
*
|
||||
* @param string $file name of the file (can be a path, if you need this (no check))
|
||||
* @param string $path path to the file
|
||||
*
|
||||
* @return void
|
||||
* @throws LFException
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
$this->setVar(['fileType' => 'php']);
|
||||
parent::init($file, $path, $metaFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a language file
|
||||
*
|
||||
* @throws LFException raised if the file does not contain a locallang array
|
||||
* @param string $file language file
|
||||
* @param string $langKey language shortcut (not used)
|
||||
* @return array language content
|
||||
*/
|
||||
protected function readLLFile($file, $langKey) {
|
||||
$LOCAL_LANG = NULL;
|
||||
if (!is_file($file)) {
|
||||
throw new LFException('failure.select.noLangfile');
|
||||
}
|
||||
|
||||
include($file);
|
||||
|
||||
/** @var array $LOCAL_LANG */
|
||||
if (!is_array($LOCAL_LANG) || !count($LOCAL_LANG)) {
|
||||
throw new LFException('failure.search.noFileContent', 0, '(' . $file . ')');
|
||||
}
|
||||
|
||||
/** @var array $LFMETA */
|
||||
if ($langKey == 'default') {
|
||||
$this->meta = $LFMETA;
|
||||
}
|
||||
|
||||
return $LOCAL_LANG;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the given content, if its a localized language file reference
|
||||
*
|
||||
* @param mixed $content language content (only one language)
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (absolute) or a boolean false
|
||||
*/
|
||||
protected function getLocalizedFile($content, $langKey) {
|
||||
if ((string) $content != 'EXT') {
|
||||
return '';
|
||||
}
|
||||
|
||||
return Typo3Lib::fixFilePath(
|
||||
dirname($this->absFile) .
|
||||
'/' . $this->nameLocalizedFile($langKey)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks a filename, if its a localized file
|
||||
*
|
||||
* @param string $filename filename
|
||||
* @param string $langKey language shortcut
|
||||
* @return boolean true(localized) or false
|
||||
*/
|
||||
public function checkLocalizedFile($filename, $langKey) {
|
||||
if (!preg_match('/^.*\.(' . $langKey . ')\.php$/', $filename)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the name of a localized file
|
||||
*
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (only filename)
|
||||
*/
|
||||
public function nameLocalizedFile($langKey) {
|
||||
return SgLib::setFileExtension($langKey . '.php', basename($this->relFile));
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the meta data for writing into a file
|
||||
*
|
||||
* @return string meta data for writing purposes
|
||||
*/
|
||||
private function prepareMeta() {
|
||||
if (!is_array($this->meta) || !count($this->meta)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$metaData = '';
|
||||
foreach ($this->meta as $metaIndex => $value) {
|
||||
$value = preg_replace('/[^\\\]\'/', '\\\'', $value);
|
||||
$metaData .= "\t" . '\'' . $metaIndex . '\' => \'' . $value . '\',' . "\n";
|
||||
}
|
||||
|
||||
return $metaData;
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the header data of a language file
|
||||
*
|
||||
* @return string header data
|
||||
*/
|
||||
private function getHeader() {
|
||||
$extKey = basename($this->absPath);
|
||||
|
||||
$header = '<?php' . "\n";
|
||||
$header .= "/**\n * local language labels of module \"$extKey\"\n";
|
||||
$header .= " *\n * This file is detected by the translation tool\n";
|
||||
$header .= " *\n * Modified/Created by extension 'lfeditor'\n */\n\n";
|
||||
|
||||
return $header;
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the footer data of a language file
|
||||
*
|
||||
* @return string footer data
|
||||
*/
|
||||
private function getFooter() {
|
||||
return '?>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the content of a language file
|
||||
*
|
||||
* @param array $localLang content of the given language
|
||||
* @param string $lang language shortcut
|
||||
* @return string language part of the main file
|
||||
*/
|
||||
private function getLangContent($localLang, $lang) {
|
||||
$content = "\t'$lang' => array (\n";
|
||||
if (is_array($localLang) && count($localLang)) {
|
||||
ksort($localLang);
|
||||
foreach ($localLang as $const => $value) {
|
||||
$value = preg_replace("/([^\\\])'/", "$1\'", $value);
|
||||
$value = str_replace("\r", '', $value);
|
||||
$content .= "\t\t'$const' => '$value',\n";
|
||||
}
|
||||
}
|
||||
$content .= "\t),\n";
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the content of a localized language file
|
||||
*
|
||||
* @param array $localLang content of the given language
|
||||
* @param string $lang language shortcut
|
||||
* @return string language content
|
||||
*/
|
||||
private function getLangContentLoc($localLang, $lang) {
|
||||
$content = '$LOCAL_LANG[\'' . $lang . '\'] = array (' . "\n";
|
||||
if (is_array($localLang) && count($localLang)) {
|
||||
ksort($localLang);
|
||||
foreach ($localLang as $const => $value) {
|
||||
$value = preg_replace("/([^\\\])'/", "$1\'", $value);
|
||||
$value = str_replace("\r", '', $value);
|
||||
$content .= "\t'$const' => '$value',\n";
|
||||
}
|
||||
}
|
||||
$content .= ");\n";
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the final content
|
||||
*
|
||||
* @param array | NULL $editedLanguages
|
||||
* @return array language files as key and content as value
|
||||
*/
|
||||
protected function prepareFileContents($editedLanguages = NULL) {
|
||||
// prepare Content
|
||||
$mainFileContent = '';
|
||||
$languages = SgLib::getSystemLanguages();
|
||||
$languageFiles = [];
|
||||
foreach ($languages as $lang) {
|
||||
// get content of localized and main file
|
||||
if ($this->checkLocalizedFile(basename($this->originLang[$lang]), $lang)) {
|
||||
if (is_array($this->localLang[$lang]) && count($this->localLang[$lang])) {
|
||||
$languageFiles[$this->originLang[$lang]] = $this->getHeader();
|
||||
$languageFiles[$this->originLang[$lang]] .=
|
||||
$this->getLangContentLoc($this->localLang[$lang], $lang);
|
||||
$languageFiles[$this->originLang[$lang]] .= $this->getFooter();
|
||||
$mainFileContent .= "\t'$lang' => 'EXT',\n";
|
||||
} else {
|
||||
$mainFileContent .= "\t'$lang' => '',\n";
|
||||
}
|
||||
} else {
|
||||
$mainFileContent .= $this->getLangContent($this->localLang[$lang], $lang);
|
||||
}
|
||||
}
|
||||
|
||||
// only a localized file?
|
||||
if ($this->checkLocalizedFile(basename($this->absFile), implode('|', SgLib::getSystemLanguages()))) {
|
||||
return $languageFiles;
|
||||
}
|
||||
|
||||
// prepare Content for the main file
|
||||
$languageFiles[$this->absFile] = $this->getHeader();
|
||||
$languageFiles[$this->absFile] .= '$LFMETA = array (' . "\n";
|
||||
$languageFiles[$this->absFile] .= $this->prepareMeta();
|
||||
$languageFiles[$this->absFile] .= ');' . "\n\n";
|
||||
$languageFiles[$this->absFile] .= '$LOCAL_LANG = array (' . "\n";
|
||||
$languageFiles[$this->absFile] .= $mainFileContent;
|
||||
$languageFiles[$this->absFile] .= ');' . "\n";
|
||||
$languageFiles[$this->absFile] .= $this->getFooter();
|
||||
|
||||
return $languageFiles;
|
||||
}
|
||||
}
|
||||
186
typo3conf/ext/lfeditor/Classes/Service/FileBaseService.php
Normal file
186
typo3conf/ext/lfeditor/Classes/Service/FileBaseService.php
Normal file
@@ -0,0 +1,186 @@
|
||||
<?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\Localization\Locales;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\PathUtility;
|
||||
|
||||
/**
|
||||
* base workspace class
|
||||
*/
|
||||
abstract class FileBaseService extends FileService {
|
||||
/**
|
||||
* @param string $content
|
||||
* @param string $lang
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function getLocalizedFile($content, $lang);
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param string $langKey
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function checkLocalizedFile($filename, $langKey);
|
||||
|
||||
/**
|
||||
* @param string $langKey
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function nameLocalizedFile($langKey);
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $langKey
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function readLLFile($file, $langKey);
|
||||
|
||||
/**
|
||||
* extended init
|
||||
*
|
||||
* @param string $file name of the file (can be a path, if you need this (no check))
|
||||
* @param string $path path to the file
|
||||
* @param string $metaFile
|
||||
*
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws LFException
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
$availableLanguages = implode('|', $locales->getLocales());
|
||||
|
||||
// localization files should not be edited
|
||||
if ($this->checkLocalizedFile(basename($file), $availableLanguages)) {
|
||||
throw new LFException('failure.langfile.notSupported');
|
||||
}
|
||||
|
||||
$this->setWorkspace('base');
|
||||
parent::init($file, $path, $metaFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads the absolute language file with all localized sub files
|
||||
*
|
||||
* @throws \Exception
|
||||
* @throws LFException
|
||||
* @return void
|
||||
*/
|
||||
public function readFile() {
|
||||
// read absolute file
|
||||
$localLang = $this->readLLFile($this->absFile, 'default');
|
||||
// loop all languages
|
||||
$languages = SgLib::getSystemLanguages();
|
||||
$originLang = [];
|
||||
foreach ($languages as $lang) {
|
||||
$originLang[$lang] = $this->absFile;
|
||||
if (!\array_key_exists($lang, $localLang)) {
|
||||
continue;
|
||||
}
|
||||
if ($lang === 'default' || (\is_array($localLang[$lang]) && \count($localLang[$lang]))) {
|
||||
if (\is_array($localLang[$lang]) && \count($localLang[$lang])) {
|
||||
\ksort($localLang[$lang]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// get localized file
|
||||
$lFile = $this->getLocalizedFile($localLang[$lang], $lang);
|
||||
if ($lFile && $this->checkLocalizedFile(basename($lFile), $lang)) {
|
||||
$originLang[$lang] = $lFile;
|
||||
$localLang[$lang] = [];
|
||||
|
||||
if (!is_file($lFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the content
|
||||
try {
|
||||
$llang = $this->readLLFile($lFile, $lang);
|
||||
} catch (\Exception $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// merge arrays and save origin of current language
|
||||
ArrayUtility::mergeRecursiveWithOverrule($localLang, $llang);
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
if (!\is_array($localLang)) {
|
||||
throw new LFException('failure.search.noFileContent');
|
||||
}
|
||||
|
||||
// copy all to object variables, if everything was ok
|
||||
$this->localLang = $localLang;
|
||||
$this->originLang = $originLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a localized file is found in labels pack (e.g. a language pack was downloaded in the backend)
|
||||
* or if $sameLocation is set, then checks for a file located in "{language}.locallang.xlf" at the same directory
|
||||
*
|
||||
* @param string $fileRef Absolute file reference to locallang file
|
||||
* @param string $language Language key
|
||||
* @param bool $sameLocation If TRUE, then locallang localization file name will be returned with same directory as $fileRef
|
||||
* @return string|null Absolute path to the language file, or null if error occurred
|
||||
*/
|
||||
protected function getLocalizedFileName($fileRef, $language, $sameLocation = FALSE) {
|
||||
// If $fileRef is already prefixed with "[language key]" then we should return it as is
|
||||
$fileName = PathUtility::basename($fileRef);
|
||||
if (GeneralUtility::isFirstPartOfStr($fileName, $language . '.')) {
|
||||
return GeneralUtility::getFileAbsFileName($fileRef);
|
||||
}
|
||||
|
||||
if ($sameLocation) {
|
||||
return GeneralUtility::getFileAbsFileName(str_replace($fileName, $language . '.' . $fileName, $fileRef));
|
||||
}
|
||||
|
||||
// Analyze file reference
|
||||
$validatedPrefix = Typo3Lib::getLocalizedFilePrefix($fileRef);
|
||||
if ($validatedPrefix) {
|
||||
// Divide file reference into extension key, directory (if any) and base name:
|
||||
[$extensionKey, $file_extPath] = explode('/', substr($fileRef, strlen($validatedPrefix)), 2);
|
||||
$temp = GeneralUtility::revExplode('/', $file_extPath, 2);
|
||||
if (count($temp) === 1) {
|
||||
array_unshift($temp, '');
|
||||
}
|
||||
// Add empty first-entry if not there.
|
||||
[$file_extPath, $file_fileName] = $temp;
|
||||
|
||||
// The filename is prefixed with "[language key]." because it prevents the llxmltranslate tool from detecting it.
|
||||
return Typo3Lib::getLabelsPath() . $language . '/' . $extensionKey . '/' . ($file_extPath ? $file_extPath . '/' : '') . $language . '.' . $file_fileName;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
429
typo3conf/ext/lfeditor/Classes/Service/FileBaseXLFService.php
Normal file
429
typo3conf/ext/lfeditor/Classes/Service/FileBaseXLFService.php
Normal file
@@ -0,0 +1,429 @@
|
||||
<?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\ExtensionUtility;
|
||||
use SGalinski\Lfeditor\Utility\SgLib;
|
||||
use SGalinski\Lfeditor\Utility\Typo3Lib;
|
||||
use TYPO3\CMS\Core\Utility\ArrayUtility;
|
||||
|
||||
/**
|
||||
* base workspace class (xlf)
|
||||
*/
|
||||
class FileBaseXLFService extends FileBaseService {
|
||||
/**
|
||||
* extended init
|
||||
*
|
||||
* @param string $file name of the file (can be a path, if you need this (no check))
|
||||
* @param string $path path to the file
|
||||
* @param string $metaFile
|
||||
* @return void
|
||||
* @throws LFException
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
$this->setFileType('xlf');
|
||||
parent::init($file, $path, $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() {
|
||||
$this->readXlfFile();
|
||||
|
||||
foreach ($this->localLang['default'] as $constant => $value) {
|
||||
if (empty($value)) {
|
||||
$this->localLang['default'][$constant] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a language file
|
||||
*
|
||||
* @param string $file language file
|
||||
* @param string $langKey language shortcut
|
||||
* @return array language content
|
||||
* @throws LFException thrown if no data can be found
|
||||
*/
|
||||
protected function readLLFile($file, $langKey) {
|
||||
if (!\is_file($file)) {
|
||||
throw new LFException('failure.select.noLangfile');
|
||||
}
|
||||
|
||||
// LIBXML_NOCDATA treat CDATA node as text node.
|
||||
// htmlentities in CDATA are not modified,
|
||||
// and those - normaly encoded if XML is valid - outside CDATA will be decoded.
|
||||
|
||||
$xmlContent = \simplexml_load_string(\file_get_contents($file), \SimpleXMLElement::class, LIBXML_NOCDATA);
|
||||
|
||||
// Cast XML to associative array with json_decode/encode.
|
||||
// This way, value is XML are casted to string.
|
||||
$xmlContent = \json_decode(\json_encode($xmlContent), TRUE);
|
||||
|
||||
// check data
|
||||
if (!\is_array($xmlContent['file']['body'])) {
|
||||
throw new LFException('failure.search.noFileContent', 0, '(' . $file . ')');
|
||||
}
|
||||
|
||||
if ($langKey === 'default') {
|
||||
$this->meta = $xmlContent['file']['header'] ?? [];
|
||||
}
|
||||
$this->meta['@attributes'] = $xmlContent['file']['@attributes'];
|
||||
|
||||
return $xmlContent['file']['body'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the trans-unit parts of the xlf language files into flat
|
||||
* source arrays with the target language or the source if the target is en
|
||||
*
|
||||
* @param array $sourceData
|
||||
* @return array
|
||||
*/
|
||||
public function resolveTranslationUnitsArrayIntoFlatArray(array $sourceData) {
|
||||
$flatData = [];
|
||||
|
||||
if (isset($sourceData['trans-unit']['@attributes'])) {
|
||||
$sourceData['trans-unit'] = [$sourceData['trans-unit']];
|
||||
}
|
||||
if (!array_key_exists('trans-unit', $sourceData)) {
|
||||
return $flatData;
|
||||
}
|
||||
|
||||
foreach ((array) $sourceData['trans-unit'] as $data) {
|
||||
if (isset($data['@attributes']['id'])) {
|
||||
$constant = $data['@attributes']['id'];
|
||||
if (array_key_exists('target', $data)) {
|
||||
$flatData[$constant] = $data['target'];
|
||||
} else {
|
||||
$flatData[$constant] = $data['source'];
|
||||
}
|
||||
|
||||
if (empty($flatData[$constant])) {
|
||||
$flatData[$constant] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $flatData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the absolute language file with all localized sub files
|
||||
*
|
||||
* @return void
|
||||
* @throws LFException
|
||||
*/
|
||||
public function readXlfFile() {
|
||||
$localLang = [];
|
||||
// read absolute file
|
||||
$localLang['default'] = $this->readLLFile($this->absFile, 'default');
|
||||
$localLang['default'] = $this->resolveTranslationUnitsArrayIntoFlatArray($localLang['default']);
|
||||
|
||||
// loop all languages
|
||||
$originLang = [];
|
||||
$languages = SgLib::getSystemLanguages();
|
||||
foreach ($languages as $lang) {
|
||||
$originLang[$lang] = $this->absFile;
|
||||
if ($lang === 'default') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// get localized file
|
||||
$content = array_key_exists($lang, $localLang) ? $localLang[$lang] : '';
|
||||
$lFile = $this->getLocalizedFile($content, $lang);
|
||||
if ($this->checkLocalizedFile(\basename($lFile), $lang)) {
|
||||
$originLang[$lang] = $lFile;
|
||||
$localLang[$lang] = [];
|
||||
|
||||
if (!\is_file($lFile)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// read the content
|
||||
$localSubLanguage = $this->readLLFile($lFile, $lang);
|
||||
$localLang[$lang] = $this->resolveTranslationUnitsArrayIntoFlatArray($localSubLanguage);
|
||||
|
||||
// merge arrays and save origin of current language
|
||||
ArrayUtility::mergeRecursiveWithOverrule($localLang, $localSubLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
// copy all to object variables, if everything was ok
|
||||
$this->localLang = $localLang;
|
||||
$this->originLang = $originLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the localLang array to find localized version of the language
|
||||
* (checks l10n directory too)
|
||||
*
|
||||
* @param string $content language content (only one language)
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (absolute)
|
||||
*/
|
||||
protected function getLocalizedFile($content, $langKey) {
|
||||
$file = '';
|
||||
if ($this->session->getDataByKey('editingMode') !== 'extension') {
|
||||
$file = $this->getLocalizedFileName($this->absFile, $langKey);
|
||||
}
|
||||
if (!\is_file($file)) {
|
||||
$file = \dirname($this->absFile) . '/' . $this->nameLocalizedFile($langKey);
|
||||
}
|
||||
return Typo3Lib::fixFilePath($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks a filename if its a localized file reference
|
||||
*
|
||||
* @param string $filename
|
||||
* @param string $langKey language shortcut
|
||||
* @return boolean true(localized) or false
|
||||
*/
|
||||
public function checkLocalizedFile($filename, $langKey) {
|
||||
if (!\preg_match('/^(' . $langKey . ')\..*\.xlf$/', $filename)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the name of a localized file
|
||||
*
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (only filename)
|
||||
*/
|
||||
public function nameLocalizedFile($langKey) {
|
||||
return $langKey . '.' . \basename($this->relFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the xml header
|
||||
*
|
||||
* @return string xml header
|
||||
*/
|
||||
private function getXMLHeader() {
|
||||
return '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* converts the array to a xml string
|
||||
*
|
||||
* @param array $phpArray
|
||||
* @param string $targetLanguage
|
||||
* @param array $enLanguage
|
||||
* @return string xml content
|
||||
*/
|
||||
private function array2xml($phpArray, $targetLanguage, $enLanguage) {
|
||||
$targetLanguage = \htmlspecialchars($targetLanguage);
|
||||
$targetLanguageAttribute = ($targetLanguage !== 'default' ? ' target-language="' . $targetLanguage . '"' : '');
|
||||
|
||||
$lfeditorExtConf = ExtensionUtility::getExtensionConfiguration();
|
||||
$date = ($lfeditorExtConf['changeXlfDate'] ? \gmdate('Y-m-d\TH:i:s\Z') : $this->meta['@attributes']['date']);
|
||||
|
||||
$xmlString = $this->getXMLHeader() . '<xliff version="1.0">
|
||||
<file source-language="en"' . $targetLanguageAttribute . ' datatype="plaintext" original="messages" date="' . $date . '">
|
||||
###HEADER###
|
||||
###BODY###
|
||||
</file>
|
||||
</xliff>';
|
||||
|
||||
$header = '<header/>';
|
||||
if (\is_array($phpArray['header']) && \count($phpArray['header'])) {
|
||||
$header = '<header>' . "\n";
|
||||
foreach ($phpArray['header'] as $tagName => $tagValue) {
|
||||
$tagName = \htmlspecialchars($tagName);
|
||||
$header .= "\t\t\t" . '<' . $tagName . '>' . \htmlspecialchars(implode(' ', $tagValue)) .
|
||||
'</' . $tagName . '>' . "\n";
|
||||
}
|
||||
$header .= "\t\t" . '</header>';
|
||||
}
|
||||
$xmlString = \str_replace('###HEADER###', $header, $xmlString);
|
||||
|
||||
$body = '<body/>';
|
||||
if (\is_array($phpArray['data']) && \count($phpArray['data'])) {
|
||||
$body = '<body>' . "\n";
|
||||
$startCDATA = "<![CDATA[";
|
||||
$endCDATA = "]]>";
|
||||
$approved = ($targetLanguage !== 'default' ? ' approved="yes"' : '');
|
||||
foreach ($phpArray['data'] as $constant => $value) {
|
||||
if (!array_key_exists($constant, $enLanguage)) {
|
||||
continue;
|
||||
}
|
||||
$enValue = $enLanguage[$constant];
|
||||
|
||||
// CDATA markers are stripped when reading the XML file from the disk,
|
||||
// and html entities are not decoded.
|
||||
// So, for simplicity and safety, when writing back the data to the file,
|
||||
// we will always enclose value in CDATA, so html entities are preserved
|
||||
// and XML remains valid.
|
||||
$value = $startCDATA . $value . $endCDATA;
|
||||
$enValue = $startCDATA . $enValue . $endCDATA;
|
||||
|
||||
$body .= "\t\t\t" . '<trans-unit id="' . \htmlspecialchars(
|
||||
$constant
|
||||
) . '"' . $approved . $this->addPreserveSpaceAttribute(
|
||||
$value,
|
||||
$enValue,
|
||||
$targetLanguage
|
||||
) . '>' . "\n";
|
||||
|
||||
if ($targetLanguage !== 'default') {
|
||||
$body .= "\t\t\t\t" . '<source>' . $enValue . '</source>' . "\n";
|
||||
$body .= "\t\t\t\t" . '<target>' . $value . '</target>' . "\n";
|
||||
} else {
|
||||
$body .= "\t\t\t\t" . '<source>' . $value . '</source>' . "\n";
|
||||
}
|
||||
$body .= "\t\t\t" . '</trans-unit>' . "\n";
|
||||
}
|
||||
$body .= "\t\t" . '</body>';
|
||||
}
|
||||
$xmlString = \str_replace('###BODY###', $body, $xmlString);
|
||||
|
||||
return $xmlString;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the content of a language file
|
||||
*
|
||||
* @param array $localLang
|
||||
* @return array new xml array
|
||||
*/
|
||||
private function getLangContent($localLang) {
|
||||
$content = [];
|
||||
if (!\is_array($localLang) || !\count($localLang)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
\ksort($localLang);
|
||||
foreach ($localLang as $const => $value) {
|
||||
$content[$const] = \str_replace("\r", '', $value);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the meta array for nicer saving
|
||||
*
|
||||
* @return array meta content
|
||||
*/
|
||||
private function prepareMeta() {
|
||||
if (\is_array($this->meta) && \count($this->meta)) {
|
||||
foreach ($this->meta as $label => $value) {
|
||||
$this->meta[$label] = \str_replace("\r", '', $value);
|
||||
}
|
||||
}
|
||||
$this->addGeneratorString();
|
||||
|
||||
$metadata = $this->meta;
|
||||
unset($metadata['@attributes']);
|
||||
|
||||
return $metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the final content
|
||||
*
|
||||
* @param array | NULL $editedLanguages
|
||||
* @return array language files as key and content as value
|
||||
*/
|
||||
protected function prepareFileContents($editedLanguages = NULL) {
|
||||
// prepare Content
|
||||
$metaData = $this->prepareMeta();
|
||||
$languages = SgLib::getSystemLanguages();
|
||||
$languageFiles = [];
|
||||
$enLanguage = $this->getLangContent($this->localLang['default']);
|
||||
foreach ($languages as $lang) {
|
||||
if ($lang === 'default') {
|
||||
continue;
|
||||
}
|
||||
// If default language content and $lang language content are not edited, skip this file.
|
||||
if ($editedLanguages !== NULL &&
|
||||
!\in_array('default', $editedLanguages, TRUE) && !\in_array($lang, $editedLanguages, TRUE)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\array_key_exists($lang, $this->localLang) && \is_array($this->localLang[$lang]) && \count(
|
||||
$this->localLang[$lang]
|
||||
)) {
|
||||
$file = $this->originLang[$lang];
|
||||
$data = [
|
||||
'header' => $metaData,
|
||||
'data' => $this->getLangContent($this->localLang[$lang]),
|
||||
];
|
||||
if (array_key_exists($file, $languageFiles)) {
|
||||
$languageFiles[$file] .= $this->array2xml($data, $lang, $enLanguage);
|
||||
} else {
|
||||
$languageFiles[$file] = $this->array2xml($data, $lang, $enLanguage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only a localized file?
|
||||
if ($this->checkLocalizedFile(\basename($this->absFile), \implode('|', SgLib::getSystemLanguages()))) {
|
||||
return $languageFiles;
|
||||
}
|
||||
if ($editedLanguages !== NULL && !\in_array('default', $editedLanguages, TRUE)) {
|
||||
return $languageFiles;
|
||||
}
|
||||
|
||||
// prepare content for the main file
|
||||
$data = [
|
||||
'header' => $metaData,
|
||||
'data' => $enLanguage,
|
||||
];
|
||||
$languageFiles[$this->absFile] = $this->array2xml($data, 'default', $enLanguage);
|
||||
|
||||
return $languageFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* If constant's source (en) or target value contains multiple spaces or LF character,
|
||||
* returns string ' xml:space="preserve"'.
|
||||
*
|
||||
* @param string $value
|
||||
* @param string $enValue
|
||||
* @param string $targetLanguage
|
||||
* @return string
|
||||
*/
|
||||
protected function addPreserveSpaceAttribute($value, $enValue, $targetLanguage) {
|
||||
$valueContainsSpacesOrLF = \strpos($value, ' ') !== FALSE || \strpos($value, "\n") !== FALSE;
|
||||
$enValueContainsSpacesOrLF = FALSE;
|
||||
if ($targetLanguage !== 'default') {
|
||||
$enValueContainsSpacesOrLF = \strpos($enValue, ' ') !== FALSE || \strpos($enValue, "\n") !== FALSE;
|
||||
}
|
||||
return ($valueContainsSpacesOrLF || $enValueContainsSpacesOrLF ? ' xml:space="preserve"' : '');
|
||||
}
|
||||
}
|
||||
358
typo3conf/ext/lfeditor/Classes/Service/FileBaseXMLService.php
Normal file
358
typo3conf/ext/lfeditor/Classes/Service/FileBaseXMLService.php
Normal file
@@ -0,0 +1,358 @@
|
||||
<?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 DOMDocument;
|
||||
use Exception;
|
||||
use SGalinski\Lfeditor\Exceptions\LFException;
|
||||
use SGalinski\Lfeditor\Utility\CdataSupportingSimpleXMLElement;
|
||||
use SGalinski\Lfeditor\Utility\SgLib;
|
||||
use SGalinski\Lfeditor\Utility\Typo3Lib;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* base workspace class (xml)
|
||||
*/
|
||||
class FileBaseXMLService extends FileBaseService {
|
||||
/**
|
||||
* extended init
|
||||
*
|
||||
* @param string $file name of the file (can be a path, if you need this (no check))
|
||||
* @param string $path path to the file
|
||||
*
|
||||
* @param string $metaFile
|
||||
* @return void
|
||||
* @throws LFException
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
$this->setVar(['fileType' => 'xml']);
|
||||
parent::init($file, $path, $metaFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* reads a language file
|
||||
*
|
||||
* @throws LFException raised if the file does not contain a valid llxml or xml is not valid
|
||||
* @param string $file language file
|
||||
* @param string $langKey language shortcut
|
||||
* @return array language content
|
||||
*/
|
||||
protected function readLLFile($file, $langKey) {
|
||||
if (!\is_file($file)) {
|
||||
throw new LFException('failure.select.noLangfile');
|
||||
}
|
||||
|
||||
// read xml into array
|
||||
$xmlContent = GeneralUtility::xml2array(\file_get_contents($file));
|
||||
|
||||
// check data
|
||||
if (!\is_array($xmlContent['data'])) {
|
||||
throw new LFException('failure.search.noFileContent', 0, '(' . $file . ')');
|
||||
}
|
||||
|
||||
// set header data
|
||||
if ($langKey === 'default') {
|
||||
$this->meta = $xmlContent['meta'];
|
||||
}
|
||||
|
||||
return $xmlContent['data'];
|
||||
}
|
||||
|
||||
/**
|
||||
* checks the localLang array to find localized version of the language
|
||||
* (checks l10n directory too)
|
||||
*
|
||||
* @param string $content language content (only one language)
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (absolute)
|
||||
*/
|
||||
protected function getLocalizedFile($content, $langKey) {
|
||||
if ($this->session->getDataByKey('editingMode') === 'l10n') {
|
||||
$file = $this->getLocalizedFileName($this->absFile, $langKey);
|
||||
if (\is_file($file)) {
|
||||
return Typo3Lib::fixFilePath($file);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$file = Typo3Lib::transTypo3File($content, TRUE);
|
||||
} catch (Exception $e) {
|
||||
if (!$file = $this->getLocalizedFileName($this->absFile, $langKey, TRUE)) {
|
||||
return $content;
|
||||
}
|
||||
if (!\is_file($file)) {
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
return Typo3Lib::fixFilePath($file);
|
||||
}
|
||||
|
||||
/**
|
||||
* checks a filename if its a localized file reference
|
||||
*
|
||||
* @param string $filename filename
|
||||
* @param string $langKey language shortcut
|
||||
* @return boolean true(localized) or false
|
||||
*/
|
||||
public function checkLocalizedFile($filename, $langKey) {
|
||||
if (!\preg_match('/^(' . $langKey . ')\..*\.xml$/', $filename)) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* get the name of a localized file
|
||||
*
|
||||
* @param string $langKey language shortcut
|
||||
* @return string localized file (only filename)
|
||||
*/
|
||||
public function nameLocalizedFile($langKey) {
|
||||
return $langKey . '.' . \basename($this->relFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* generates the xml header
|
||||
*
|
||||
* @return string xml header
|
||||
*/
|
||||
private function getXMLHeader() {
|
||||
return '<?xml version="1.0" encoding="utf-8" standalone="yes" ?>' . "\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts php array to xml string with support for CDATA tags.
|
||||
*
|
||||
* @param array $phpArray
|
||||
* @param CdataSupportingSimpleXMLElement $xmlElement
|
||||
* @param array $parentTagMap
|
||||
* @param string $parentTagName
|
||||
* @return void
|
||||
*/
|
||||
protected function arrayToXml(
|
||||
array $phpArray,
|
||||
CdataSupportingSimpleXMLElement $xmlElement,
|
||||
array $parentTagMap = [],
|
||||
$parentTagName = NULL
|
||||
) {
|
||||
foreach ($phpArray as $key => $value) {
|
||||
if (\strcasecmp($key, '@attributes') === 0) {
|
||||
continue;
|
||||
}
|
||||
$tagName = $key;
|
||||
$indexAttributeValue = NULL;
|
||||
if (\count($parentTagMap) !== 0 && !empty($parentTagMap[$parentTagName])) {
|
||||
$tagName = $parentTagMap[$parentTagName];
|
||||
$indexAttributeValue = $key;
|
||||
}
|
||||
// If element is array, check that it is not 'label' tag, just in case
|
||||
if (\is_array($value) && $tagName !== 'label') {
|
||||
if (\is_numeric($tagName)) {
|
||||
$tagName = 'item' . $tagName;
|
||||
}
|
||||
$subNode = $xmlElement->addChild($tagName);
|
||||
if ($indexAttributeValue !== NULL) {
|
||||
$subNode->addAttribute('index', $indexAttributeValue);
|
||||
}
|
||||
$subNode->addAttribute('type', 'array');
|
||||
$this->arrayToXml($value, $subNode, $parentTagMap, $tagName);
|
||||
} else {
|
||||
// CDATA markers are stripped when reading the XML file from the disk,
|
||||
// and html entities are not decoded.
|
||||
// So, for simplicity and safety, when writing back the data to the file,
|
||||
// we will always enclose value in CDATA, so html entities are preserved
|
||||
// and XML remains valid.
|
||||
$simpleSubNode = $xmlElement->addChildCData($tagName, $value);
|
||||
if ($indexAttributeValue !== NULL) {
|
||||
$simpleSubNode->addAttribute('index', $indexAttributeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts php array to formatted xml string with support for CDATA tags.
|
||||
*
|
||||
* @param array $phpArray php structure with data
|
||||
* @param string $firstTag name of first tag
|
||||
* @return string xml content
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function array2xml(array $phpArray, $firstTag) {
|
||||
if (\count($phpArray) === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/** @var CdataSupportingSimpleXMLElement $xmlElement */
|
||||
$xmlElement = new CdataSupportingSimpleXMLElement(
|
||||
$this->getXMLHeader() . '<' . $firstTag . '></' . $firstTag . '>'
|
||||
);
|
||||
$xmlElement->addAttribute('type', 'array');
|
||||
|
||||
$parentTagMap = [
|
||||
'data' => 'languageKey',
|
||||
'languageKey' => 'label',
|
||||
];
|
||||
|
||||
$this->arrayToXml($phpArray, $xmlElement, $parentTagMap);
|
||||
$formattedXml = $this->formatXml($xmlElement);
|
||||
|
||||
return $formattedXml;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the content of a language file
|
||||
*
|
||||
* @param array $localLang content of the given language
|
||||
* @param string $lang shortcut
|
||||
* @return array new xml array
|
||||
*/
|
||||
private function getLangContent($localLang, $lang) {
|
||||
$content = [];
|
||||
$content['data'][$lang] = '';
|
||||
if (!\is_array($localLang) || !\count($localLang)) {
|
||||
return $content;
|
||||
}
|
||||
|
||||
\ksort($localLang);
|
||||
foreach ($localLang as $const => $value) {
|
||||
if ($content['data'][$lang] === '') {
|
||||
$content['data'][$lang] = [];
|
||||
}
|
||||
|
||||
$content['data'][$lang][$const] = \str_replace("\r", '', $value);
|
||||
}
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the meta array for nicer saving
|
||||
*
|
||||
* @return array meta content
|
||||
*/
|
||||
private function prepareMeta() {
|
||||
if (\is_array($this->meta) && \count($this->meta)) {
|
||||
foreach ($this->meta as $label => $value) {
|
||||
$this->meta[$label] = \str_replace("\r", '', $value);
|
||||
}
|
||||
}
|
||||
$this->addGeneratorString();
|
||||
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* prepares the final content
|
||||
*
|
||||
* @param array | NULL $editedLanguages
|
||||
* @return array language files as key and content as value
|
||||
*/
|
||||
protected function prepareFileContents($editedLanguages = NULL) {
|
||||
$mergedFile = TRUE;
|
||||
|
||||
// prepare Content
|
||||
$mainFileContent = ['meta' => $this->prepareMeta()];
|
||||
$languages = SgLib::getSystemLanguages();
|
||||
$languageFiles = [];
|
||||
foreach ($languages as $lang) {
|
||||
// get content of localized and main file
|
||||
if (array_key_exists($lang, $this->originLang) && $this->checkLocalizedFile(\basename($this->originLang[$lang]), $lang)) {
|
||||
$mergedFile = FALSE;
|
||||
if (\is_array($this->localLang[$lang]) && \count($this->localLang[$lang])
|
||||
// check if this language content was edited
|
||||
&& (NULL === $editedLanguages || \in_array($lang, $editedLanguages, TRUE))
|
||||
) {
|
||||
$languageFiles[$this->originLang[$lang]] .=
|
||||
$this->array2xml(
|
||||
$this->getLangContent($this->localLang[$lang], $lang),
|
||||
'T3locallangExt'
|
||||
);
|
||||
|
||||
try {
|
||||
$mainFileContent['data'][$lang] =
|
||||
Typo3Lib::transTypo3File($this->originLang[$lang], FALSE);
|
||||
} catch (Exception $e) {
|
||||
if (!Typo3Lib::checkFileLocation($this->originLang[$lang]) == 'l10n') {
|
||||
$mainFileContent['data'][$lang] = $this->originLang[$lang];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$mainFileContent['data'][$lang] = '';
|
||||
}
|
||||
} elseif (array_key_exists($lang, $this->localLang)) {
|
||||
$mainFileContent = \array_merge_recursive(
|
||||
$mainFileContent,
|
||||
$this->getLangContent($this->localLang[$lang], $lang)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// only a localized file?
|
||||
if ($this->checkLocalizedFile(\basename($this->absFile), \implode('|', SgLib::getSystemLanguages()))) {
|
||||
return $languageFiles;
|
||||
}
|
||||
if (!$mergedFile && $editedLanguages !== NULL && !\in_array('default', $editedLanguages, TRUE)) {
|
||||
return $languageFiles;
|
||||
}
|
||||
|
||||
// prepare Content for the main file
|
||||
$languageFiles[$this->absFile] = $this->array2xml($mainFileContent, 'T3locallang');
|
||||
|
||||
return $languageFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats xml and returns as a string.
|
||||
*
|
||||
* @param \SimpleXMLElement $xmlElement
|
||||
* @return string
|
||||
*/
|
||||
private function formatXml(\SimpleXMLElement $xmlElement) {
|
||||
$dom = new DOMDocument('1.0');
|
||||
$dom->preserveWhiteSpace = FALSE;
|
||||
$dom->formatOutput = TRUE;
|
||||
|
||||
$xmlRaw = $xmlElement->asXML();
|
||||
|
||||
//Search CDATA tags to decode the content inside
|
||||
//Because $xmlElement->asXML() encode html entities
|
||||
$xmlRaw = preg_replace_callback(
|
||||
"#(<\!\[CDATA\[.*\]\]>)#sU",
|
||||
function ($matches) {
|
||||
return htmlspecialchars_decode($matches[1]);
|
||||
},
|
||||
$xmlRaw
|
||||
);
|
||||
$dom->loadXML($xmlRaw);
|
||||
|
||||
$formattedXml = $dom->saveXML();
|
||||
return $formattedXml;
|
||||
}
|
||||
}
|
||||
360
typo3conf/ext/lfeditor/Classes/Service/FileOverrideService.php
Normal file
360
typo3conf/ext/lfeditor/Classes/Service/FileOverrideService.php
Normal file
@@ -0,0 +1,360 @@
|
||||
<?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';
|
||||
}
|
||||
}
|
||||
}
|
||||
487
typo3conf/ext/lfeditor/Classes/Service/FileService.php
Normal file
487
typo3conf/ext/lfeditor/Classes/Service/FileService.php
Normal file
@@ -0,0 +1,487 @@
|
||||
<?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\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* include some general functions only usable for the 'lfeditor' module
|
||||
*/
|
||||
abstract class FileService extends AbstractService {
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $localLang = [];
|
||||
|
||||
/**
|
||||
* Absolute address (origin) of language file
|
||||
* which contains translation for language given as a key of the array.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $originLang = [];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $absPath;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $relFile;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $absFile;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $fileType;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $workspace;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $meta = [];
|
||||
|
||||
/**
|
||||
* @param array|NULL $editedLanguages
|
||||
* @return mixed
|
||||
*/
|
||||
abstract protected function prepareFileContents($editedLanguages);
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
abstract public function readFile();
|
||||
|
||||
/**
|
||||
* sets some variables
|
||||
*
|
||||
* @param string $file filename or relative path from second param to the language file
|
||||
* @param string $path absolute path to the extension or language file
|
||||
* @param string $metaFile absolute path to the meta file (includes filename) DO NOT REMOVE! It's required in some
|
||||
* implementations.
|
||||
* @return void
|
||||
*/
|
||||
public function init($file, $path, $metaFile) {
|
||||
$this->setAbsPath($path);
|
||||
$this->setRelFile($file);
|
||||
$this->setAbsFile($this->absPath . '/' . $this->relFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* sets information
|
||||
*
|
||||
* structure:
|
||||
* $infos["absPath"] = absolute path to an extension or file
|
||||
* $infos["relFile"] = relative path with filename from "absPath"
|
||||
* $infos["workspace"] = workspace (base or xll)
|
||||
* $infos["fileType"] = file type (php or xml)
|
||||
* $infos["localLang"] = language data
|
||||
* $infos["originLang"] = origin language array
|
||||
* $infos["meta"] = meta data
|
||||
*
|
||||
* @param array $informations
|
||||
* @return void
|
||||
*/
|
||||
public function setVar($informations) {
|
||||
// path and file information
|
||||
if (!empty($informations['absPath'])) {
|
||||
$this->absPath = Typo3Lib::fixFilePath($informations['absPath'] . '/');
|
||||
}
|
||||
if (!empty($informations['relFile'])) {
|
||||
$this->relFile = Typo3Lib::fixFilePath($informations['relFile']);
|
||||
}
|
||||
$this->absFile = $this->absPath . $this->relFile;
|
||||
|
||||
// file type and workspace
|
||||
if (!empty($informations['workspace'])) {
|
||||
$this->workspace = $informations['workspace'];
|
||||
}
|
||||
if (!empty($informations['fileType'])) {
|
||||
$this->fileType = $informations['fileType'];
|
||||
}
|
||||
|
||||
// data arrays
|
||||
if (!count($this->localLang) && array_key_exists('localLang', $informations) && is_array($informations['localLang'])) {
|
||||
$this->localLang = $informations['localLang'];
|
||||
}
|
||||
if (!count($this->originLang) && array_key_exists('originLang', $informations) && is_array($informations['originLang'])) {
|
||||
$this->originLang = $informations['originLang'];
|
||||
}
|
||||
if (!count($this->meta) && array_key_exists('meta', $informations) && is_array($informations['meta'])) {
|
||||
$this->meta = $informations['meta'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns requested information
|
||||
*
|
||||
* @param string $info
|
||||
* @return string
|
||||
*/
|
||||
public function getVar($info) {
|
||||
$value = '';
|
||||
if ($info == 'relFile') {
|
||||
$value = $this->relFile;
|
||||
} elseif ($info == 'absPath') {
|
||||
$value = $this->absPath;
|
||||
} elseif ($info == 'absFile') {
|
||||
$value = $this->absFile;
|
||||
} elseif ($info == 'fileType') {
|
||||
$value = $this->fileType;
|
||||
} elseif ($info == 'workspace') {
|
||||
$value = $this->workspace;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns language data
|
||||
*
|
||||
* @param string $langKey valid language key
|
||||
* @return array language data
|
||||
*/
|
||||
public function getLocalLangData($langKey = '') {
|
||||
if (empty($langKey)) {
|
||||
return $this->localLang;
|
||||
}
|
||||
if (array_key_exists($langKey, $this->localLang) && is_array($this->localLang[$langKey])) {
|
||||
return $this->localLang[$langKey];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes or sets constants in local language data
|
||||
*
|
||||
* @param string $constant constant name (if empty an index number will be used)
|
||||
* @param string $value new value (if empty the constant will be deleted)
|
||||
* @param string $langKey language shortcut
|
||||
* @param boolean $forceDel set to true, if you want delete default values too
|
||||
* @return void
|
||||
*/
|
||||
public function setLocalLangData($constant, $value, $langKey, $forceDel = FALSE) {
|
||||
if (!empty($value) || (($langKey === 'default' && !$forceDel))) {
|
||||
if (!array_key_exists($langKey, $this->localLang) || $this->localLang[$langKey] === '') {
|
||||
$this->localLang[$langKey] = [];
|
||||
}
|
||||
$this->localLang[$langKey][$constant] = $value;
|
||||
} elseif (isset($this->localLang[$langKey][$constant])) {
|
||||
if ($this->session->getDataByKey('editingMode') === 'override' &&
|
||||
isset($this->localLang[$langKey][$constant])
|
||||
) {
|
||||
$this->localLang[$langKey][$constant] = "";
|
||||
} else {
|
||||
unset($this->localLang[$langKey][$constant]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns origin
|
||||
*
|
||||
* @param string $langKey valid language key
|
||||
* @return mixed an origin or full array
|
||||
*/
|
||||
public function getOriginLangData($langKey = '') {
|
||||
if (empty($langKey)) {
|
||||
return $this->originLang;
|
||||
} else {
|
||||
return $this->originLang[$langKey] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sets new origin of a given language
|
||||
*
|
||||
* @param string $origin new origin
|
||||
* @param string $langKey language shortcut
|
||||
* @return void
|
||||
*/
|
||||
public function setOriginLangData($origin, $langKey) {
|
||||
if (!empty($origin)) {
|
||||
$this->originLang[$langKey] = $origin;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* returns meta data
|
||||
*
|
||||
* @param string $metaIndex special meta index
|
||||
* @return mixed meta data
|
||||
*/
|
||||
public function getMetaData($metaIndex = '') {
|
||||
if (!empty($metaIndex)) {
|
||||
return $this->meta[$metaIndex] ?? '';
|
||||
} else {
|
||||
return $this->meta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes or sets constants in meta data
|
||||
*
|
||||
* @param string $metaIndex
|
||||
* @param string $value new value (if empty the meta index will be deleted)
|
||||
* @return void
|
||||
*/
|
||||
public function setMetaData($metaIndex, $value) {
|
||||
if (!empty($value)) {
|
||||
$this->meta[$metaIndex] = $value;
|
||||
} elseif (isset($this->meta[$metaIndex])) {
|
||||
unset($this->meta[$metaIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* writes language files
|
||||
*
|
||||
* @param array | NULL $editedLanguages
|
||||
* @throws LFException raised if a file cant be written
|
||||
* @return void
|
||||
*/
|
||||
public function writeFile($editedLanguages = NULL) {
|
||||
// get prepared language files
|
||||
$languageFiles = $this->prepareFileContents($editedLanguages);
|
||||
$this->writeFilesWithContent($languageFiles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given files with the given content.
|
||||
*
|
||||
* Array structure:
|
||||
* array (
|
||||
* '/var/www/file.xlf' => 'My content',
|
||||
* ...
|
||||
* )
|
||||
*
|
||||
* @param array $files
|
||||
* @throws LFException
|
||||
* @return void
|
||||
*/
|
||||
public function writeFilesWithContent(array $files = []) {
|
||||
// check write permissions of all files
|
||||
foreach ($files as $file => $content) {
|
||||
[$extensionNameFromFile, $extPrefixedPath] = Typo3Lib::getExtNameFromOverrideFile($file);
|
||||
if (strpos($extensionNameFromFile, '-') !== FALSE) {
|
||||
$file = str_replace(
|
||||
$extensionNameFromFile,
|
||||
str_replace('-', '_', $extensionNameFromFile),
|
||||
$file
|
||||
);
|
||||
}
|
||||
if (!SgLib::checkWritePerms($file)) {
|
||||
throw new LFException('failure.file.badPermissions');
|
||||
}
|
||||
}
|
||||
|
||||
// write files
|
||||
foreach ($files as $file => $content) {
|
||||
[$extensionNameFromFile, $extPrefixedPath] = Typo3Lib::getExtNameFromOverrideFile($file);
|
||||
if (strpos($extensionNameFromFile, '-') !== FALSE) {
|
||||
$file = str_replace(
|
||||
$extensionNameFromFile,
|
||||
str_replace('-', '_', $extensionNameFromFile),
|
||||
$file
|
||||
);
|
||||
}
|
||||
if (!GeneralUtility::writeFile($file, $content)) {
|
||||
throw new LFException('failure.file.notWritten');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes generator meta tag.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function addGeneratorString() {
|
||||
$this->meta['generator'] = 'LFEditor';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $absFile.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsFile() {
|
||||
return $this->absFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $absFile.
|
||||
*
|
||||
* @param string $absFile
|
||||
* @return void
|
||||
*/
|
||||
public function setAbsFile($absFile) {
|
||||
$this->absFile = $absFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $absPath - absolute path to an extension or file.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getAbsPath() {
|
||||
return $this->absPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $absPath - absolute path to an extension or file.
|
||||
*
|
||||
* @param string $absPath
|
||||
* @return void
|
||||
*/
|
||||
public function setAbsPath($absPath) {
|
||||
$this->absPath = $absPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $fileType
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFileType() {
|
||||
return $this->fileType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $fileType.
|
||||
*
|
||||
* @param string $fileType
|
||||
* @return void
|
||||
*/
|
||||
public function setFileType($fileType) {
|
||||
$this->fileType = $fileType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $locallang
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getLocalLang() {
|
||||
return $this->localLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $locallang.
|
||||
*
|
||||
* @param array $localLang
|
||||
*/
|
||||
public function setLocalLang(array $localLang) {
|
||||
$this->localLang = $localLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns meta data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getMeta() {
|
||||
return $this->meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets meta data.
|
||||
*
|
||||
* @param array $meta
|
||||
*/
|
||||
public function setMeta(array $meta) {
|
||||
$this->meta = $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns $originLang.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getOriginLang() {
|
||||
return $this->originLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets $originLang.
|
||||
*
|
||||
* @param array $originLang
|
||||
*/
|
||||
public function setOriginLang(array $originLang) {
|
||||
$this->originLang = $originLang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns relFile - relative path with filename from "absPath".
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRelFile() {
|
||||
return $this->relFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets relFile - relative path with filename from "absPath".
|
||||
*
|
||||
* @param string $relFile
|
||||
*/
|
||||
public function setRelFile($relFile) {
|
||||
$this->relFile = $relFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns workspace.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getWorkspace() {
|
||||
return $this->workspace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets workspace.
|
||||
*
|
||||
* @param string $workspace
|
||||
*/
|
||||
public function setWorkspace($workspace) {
|
||||
$this->workspace = $workspace;
|
||||
}
|
||||
}
|
||||
125
typo3conf/ext/lfeditor/Classes/Service/LicensingService.php
Normal file
125
typo3conf/ext/lfeditor/Classes/Service/LicensingService.php
Normal file
@@ -0,0 +1,125 @@
|
||||
<?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 Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use SGalinski\Lfeditor\Utility\ExtensionUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Http\NullResponse;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Class SGalinski\SgRoutes\Service\LicensingService
|
||||
*/
|
||||
class LicensingService {
|
||||
/**
|
||||
* Licensing Service Url
|
||||
*/
|
||||
public const URL = 'https://www.sgalinski.de/?eID=sgLicensing';
|
||||
|
||||
/**
|
||||
* Licensing Service Url
|
||||
*/
|
||||
public const EXTENSION_KEY = 'lfeditor';
|
||||
|
||||
/**
|
||||
* @var null|bool $isLicenseKeyValid
|
||||
*/
|
||||
private static $isLicenseKeyValid = NULL;
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public static function checkKey() {
|
||||
if (static::$isLicenseKeyValid === NULL) {
|
||||
static::$isLicenseKeyValid = FALSE;
|
||||
$configuration = ExtensionUtility::getExtensionConfiguration();
|
||||
if (isset($configuration['key']) && $key = \trim($configuration['key'])) {
|
||||
static::$isLicenseKeyValid = (bool) \preg_match('/^([A-Z\d]{6}-?){4}$/', $key);
|
||||
}
|
||||
}
|
||||
|
||||
return static::$isLicenseKeyValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Licensing Service ping
|
||||
*
|
||||
* @param boolean $returnUrl
|
||||
* @return string
|
||||
*/
|
||||
public static function ping($returnUrl = FALSE) {
|
||||
try {
|
||||
$configuration = ExtensionUtility::getExtensionConfiguration();
|
||||
$key = '';
|
||||
if (isset($configuration['key'])) {
|
||||
$key = \trim($configuration['key']);
|
||||
}
|
||||
$params = [
|
||||
'extension' => self::EXTENSION_KEY,
|
||||
'host' => GeneralUtility::getIndpEnv('HTTP_HOST'),
|
||||
'key' => $key
|
||||
];
|
||||
$params = \http_build_query($params);
|
||||
$pingUrl = self::URL;
|
||||
$pingUrl .= $params !== '' ? (\strpos($pingUrl, '?') === FALSE ? '?' : '&') . $params : '';
|
||||
if ($returnUrl) {
|
||||
return $pingUrl;
|
||||
}
|
||||
|
||||
GeneralUtility::getUrl($pingUrl);
|
||||
} catch (\Exception $exception) {
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a random password string based on the configured password policies.
|
||||
*
|
||||
* @param ServerRequestInterface $request
|
||||
* @param ResponseInterface $response
|
||||
* @return ResponseInterface
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function ajaxPing(ServerRequestInterface $request, ResponseInterface $response = NULL) {
|
||||
/** @var BackendUserAuthentication $backendUser */
|
||||
$backendUser = $GLOBALS['BE_USER'];
|
||||
$moduleKey = 'tools_beuser/index.php/user-LfeditorLfeditor_pinged';
|
||||
if ($backendUser && !$backendUser->getModuleData($moduleKey, 'ses')) {
|
||||
$backendUser->pushModuleData($moduleKey, TRUE);
|
||||
self::ping();
|
||||
}
|
||||
|
||||
if ($response === NULL) {
|
||||
$response = new NullResponse();
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
144
typo3conf/ext/lfeditor/Classes/Service/SysLanguageService.php
Normal file
144
typo3conf/ext/lfeditor/Classes/Service/SysLanguageService.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?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 TYPO3\CMS\Core\Database\Connection;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Localization\Locales;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
|
||||
|
||||
/**
|
||||
* Class SysLanguageService works with data from sys_language table.
|
||||
*/
|
||||
class SysLanguageService extends AbstractService {
|
||||
/** @var array */
|
||||
protected $sysLanguageList;
|
||||
|
||||
/** @var array */
|
||||
protected $isoReverseMapping;
|
||||
|
||||
/**
|
||||
* Selects data (uid, title, flag) from sys_language table by list of uid-s ($uids).
|
||||
* If no $uids specified, selects all records.
|
||||
*
|
||||
* @param string $uids Comma separated list of uid-s.
|
||||
* @return array|bool
|
||||
*/
|
||||
public function selectFromSysLanguageByUids($uids = NULL) {
|
||||
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
|
||||
$queryBuilder = $connectionPool->getQueryBuilderForTable('sys_language');
|
||||
$queryBuilder = $queryBuilder->select('uid', 'title', 'flag')
|
||||
->from('sys_language')
|
||||
->groupBy('flag', 'uid');
|
||||
if ($uids !== NULL) {
|
||||
$queryBuilder = $queryBuilder->where(
|
||||
$queryBuilder->expr()->in(
|
||||
'uid',
|
||||
$queryBuilder->createNamedParameter($uids, Connection::PARAM_INT_ARRAY)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$rows = $queryBuilder->execute()->fetchAll();
|
||||
$sysLanguageList = [];
|
||||
foreach ($rows as $row) {
|
||||
$sysLanguageList[$row['flag']]['uid'] = $row['uid'];
|
||||
$sysLanguageList[$row['flag']]['title'] = $row['title'];
|
||||
}
|
||||
|
||||
return $sysLanguageList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialises array of system languages from database.
|
||||
*/
|
||||
public function initSysLanguageList() {
|
||||
if (!isset($this->sysLanguageList)) {
|
||||
$this->sysLanguageList = $this->selectFromSysLanguageByUids();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the iso reverse mapped flag language or the given value if nothing could be mapped.
|
||||
*
|
||||
* @param string $flag language acronym (iso language code, e.g.: 'de', 'da', 'fi'...)
|
||||
* @return string
|
||||
*/
|
||||
public function doIsoReverseMapping($flag) {
|
||||
$isoReverseMapping = $this->getIsoReverseMapping();
|
||||
$mappedFlag = isset($isoReverseMapping[$flag]) ? (string) $isoReverseMapping[$flag] : '';
|
||||
if ($mappedFlag !== '') {
|
||||
$flag = $mappedFlag;
|
||||
}
|
||||
|
||||
return $flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns system language id for given language acronym ($flag).
|
||||
* If that language is not registered in system, function returns NULL.
|
||||
*
|
||||
* @param string $flag language acronym (iso language code, e.g.: 'de', 'da', 'fi'...)
|
||||
* @return int|NULL
|
||||
*/
|
||||
public function getSysLanguageIdByFlag($flag) {
|
||||
$this->initSysLanguageList();
|
||||
$flag = $this->doIsoReverseMapping($flag);
|
||||
|
||||
// chinese seems to have a big mapping issue. In general it seems that the iso handling is currently more
|
||||
// or less simply fucked up in TYPO3. Also it's possible that I don't get the bigger picture here. Who knows...
|
||||
// Also note: Chinese isn't working without a default file in the override mode.
|
||||
if ($flag === 'zh') {
|
||||
$flag = 'cn';
|
||||
}
|
||||
|
||||
if (!empty($this->sysLanguageList[$flag]['uid'])) {
|
||||
return (int) $this->sysLanguageList[$flag]['uid'];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapping between ISO language codes and TYPO3 (old) codes.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getIsoReverseMapping() {
|
||||
if (!empty($this->isoReverseMapping)) {
|
||||
return $this->isoReverseMapping;
|
||||
}
|
||||
|
||||
/** @var Locales $locales */
|
||||
$locales = GeneralUtility::makeInstance(Locales::class);
|
||||
|
||||
$isoMapping = $locales->getIsoMapping();
|
||||
$this->isoReverseMapping = \array_flip($isoMapping);
|
||||
return $this->isoReverseMapping;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user