Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,236 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* Abstract Backend Controller
*/
abstract class AbstractBackendController extends AbstractController {
/**
* @var BackendUserAuthentication
*/
protected $backendUser;
/**
* @var ?ModuleTemplate
*/
protected $moduleTemplate = NULL;
/**
* Initializes any action
*
* @return void
* @throws \SGalinski\Lfeditor\Exceptions\DirectoryAccessRightsException
*/
public function initializeAction() {
parent::initializeAction();
if (\TYPO3\CMS\Core\Http\ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isBackend()) {
$this->backendUser = $GLOBALS['BE_USER'];
}
$editingMode = $this->session->getDataByKey('editingMode');
$availableEditingModes = $this->configurationService->getAvailableEditingModes();
if ($this->backendUser->isAdmin()) {
if (!\array_key_exists($editingMode, $availableEditingModes)) {
$firstAvailableEditMode = \key($availableEditingModes);
$this->session->setDataByKey('editingMode', $firstAvailableEditMode);
}
$canChangeEditingModes = \count($availableEditingModes) > 0;
} else {
$canChangeEditingModes = \count($availableEditingModes) > 0
&& $this->backendUser->user['lfeditor_change_editing_modes'] !== 0;
if (!$canChangeEditingModes || !\array_key_exists($editingMode, $availableEditingModes)) {
$lastAvailableEditMode = array_key_last($availableEditingModes);
$this->session->setDataByKey('editingMode', $lastAvailableEditMode);
}
}
$this->session->setDataByKey('defaultLanguagePermission', $this->backendUser->checkLanguageAccess(0));
$this->session->setDataByKey('canChangeEditingModes', $canChangeEditingModes);
}
/**
* Saves the the last called controller/action pair into the backend user
* configuration if available
*
* @param bool $saveWithRedirectPair
* @return void
*/
protected function setLastCalledControllerActionPair($saveWithRedirectPair = TRUE) {
if (!$this->backendUser) {
return;
}
$extensionKey = $this->request->getControllerExtensionKey();
$pair = [
'action' => $this->request->getControllerActionName(),
'controller' => $this->request->getControllerName(),
];
$this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPair'] = $pair;
if ($saveWithRedirectPair) {
$this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPairForRedirect'] = $pair;
}
$this->backendUser->writeUC($this->backendUser->uc);
}
/**
* Resets the last called controller/action pair combination from the
* backend user session
*
* @return void
*/
protected function resetLastCalledControllerActionPair() {
if (!$this->backendUser) {
return;
}
$extensionKey = $this->request->getControllerExtensionKey();
$this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPair'] = [];
$this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPairForRedirect'] = [];
$this->backendUser->writeUC($this->backendUser->uc);
if ($this->view instanceof ViewInterface) {
$this->view->assign('lastCalledControllerActionPair', NULL);
}
}
/**
* Returns the last called controller/action pair from the backend user session
*
* @return array
*/
protected function getLastCalledControllerActionPair() {
if (!$this->backendUser) {
return [];
}
$extensionKey = $this->request->getControllerExtensionKey();
$state = $this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPair'];
return (!\is_array($state) ? [] : $state);
}
/**
* Returns the last called controller/action pair from the backend user session
*
* @return array
*/
protected function getLastCalledControllerActionPairForRedirect() {
if (!$this->backendUser) {
return [];
}
$extensionKey = $this->request->getControllerExtensionKey();
return $this->backendUser->uc[$extensionKey . 'State']['LastActionControllerPairForRedirect'] ?? [];
}
/**
* Redirects to the last called controller/action pair saved inside the
* backend user session
*
* @return \TYPO3\CMS\Extbase\Http\ForwardResponse|null
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
protected function redirectToLastCalledControllerActionPair(): ?\TYPO3\CMS\Extbase\Http\ForwardResponse {
$state = $this->getLastCalledControllerActionPairForRedirect();
if (\count($state) && \trim($state['action']) !== '' && \trim($state['controller']) !== '') {
$currentAction = $this->request->getControllerActionName();
$currentController = $this->request->getControllerName();
if (!($currentController === $state['controller'] && $currentAction === $state['action'])) {
$extensionName = $this->request->getControllerExtensionName();
$moduleSignature = $this->request->getPluginName();
$extensionConfig = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['extbase']['extensions'][$extensionName];
$availableControllers = $extensionConfig['modules'][$moduleSignature]['controllers'];
$controllerExists = isset($availableControllers[$state['controller']]);
if ($controllerExists) {
$actionExists = \in_array(
$state['action'],
$availableControllers[$state['controller']]['actions'],
TRUE
);
if ($actionExists) {
if (version_compare(
\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(),
'11.0.0',
'<'
)) {
$this->forward($state['action'], $state['controller']);
}
return (new \TYPO3\CMS\Extbase\Http\ForwardResponse($state['action']))->withControllerName(
$state['controller']
);
}
}
}
}
return NULL;
}
/**
* Sets last called controller-action pair and assigns common view variables.
* This function should be called at the end of actions which render view
* (and does not do redirection or forwarding at the end)
*/
protected function commonViewRenderingActionSettings() {
$this->setLastCalledControllerActionPair();
$this->view->assign('editingMode', $this->session->getDataByKey('editingMode'));
$this->view->assign('editingModeOptions', $this->configurationService->getAvailableEditingModes());
$this->view->assign('adminUser', $this->backendUser->isAdmin());
$this->view->assign('defaultLanguagePermission', $this->session->getDataByKey('defaultLanguagePermission'));
$this->view->assign('canChangeEditingModes', $this->session->getDataByKey('canChangeEditingModes'));
}
/**
* Since we cannot use constructor Injection, we do have to get one dynamically. If we want to use
* our ModuleTemplate, call GetModuleTemplate before, which will init one if there is none for some reason
*/
protected function getModuleTemplate() {
if ($this->moduleTemplate === NULL) {
$moduleTemplateFactory = GeneralUtility::makeInstance(
\TYPO3\CMS\Backend\Template\ModuleTemplateFactory::class
);
$this->moduleTemplate = $moduleTemplateFactory->create($this->request);
}
}
/**
* Use the ModuleTemplateResponse to create a response object for the backend
*
* @return \Psr\Http\Message\ResponseInterface
*/
protected function createBackendResponse(): \Psr\Http\Message\ResponseInterface {
return $this->htmlResponse($this->view->render());
}
}

View File

@@ -0,0 +1,317 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Service\BackupService;
use SGalinski\Lfeditor\Service\ConfigurationService;
use SGalinski\Lfeditor\Session\PhpSession;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* Abstract Controller
*/
abstract class AbstractController extends ActionController {
/**
* @var \SGalinski\Lfeditor\Session\PhpSession
*/
protected $session;
/**
*
* @var ConfigurationService
*/
protected $configurationService;
/**
* Inject the ConfigurationService
*
* @param ConfigurationService $configurationService
*/
public function injectConfigurationService(ConfigurationService $configurationService) {
$this->configurationService = $configurationService;
}
/**
*
* @var BackupService
*/
protected $backupService;
/**
* Inject the BackupService
*
* @param BackupService $backupService
*/
public function injectBackupService(BackupService $backupService) {
$this->backupService = $backupService;
}
/**
* Initializes the actions.
* - Initializes the session object.
* - Fetches configuration.
*
* @return void
* @throws \SGalinski\Lfeditor\Exceptions\DirectoryAccessRightsException
*/
public function initializeAction() {
parent::initializeAction();
if (!($this->session instanceof PhpSession)) {
$this->session = GeneralUtility::makeInstance(PhpSession::class);
$this->session->setSessionKey('tx_lfeditor_sessionVariables');
}
$this->configurationService->prepareConfig();
}
/**
* Saves in session currently selected values of select tags.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $referenceLanguageSelection
* @param string $constantSelection
* @param string $languageSelection
* @param string $constantTypeSelection
* @param string $bottomReferenceLanguageSelection
* @param string $numSiteConstsSelection
* @param string $extkey
* @return void
*/
protected function saveSelectionsInSession(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$referenceLanguageSelection = NULL,
$constantSelection = NULL,
$languageSelection = NULL,
$constantTypeSelection = NULL,
$bottomReferenceLanguageSelection = NULL,
$numSiteConstsSelection = NULL,
$extkey = NULL
) {
/* Extension/language file select box can't be unselected.
Only situation when $extensionSelection === NULL is when the form is submitted by
selection change of some other box. That is because <f:be.menus.actionMenu> with 'optgroup' tags is used */
if ($extensionSelection) {
$this->session->setDataByKey('extensionSelection', $extensionSelection);
}
if ($languageFileSelection) {
$this->session->setDataByKey('languageFileSelection', $languageFileSelection);
}
if ($referenceLanguageSelection) {
$this->session->setDataByKey('referenceLanguageSelection', $referenceLanguageSelection);
}
if ($constantSelection) {
$this->session->setDataByKey('constantSelection', $constantSelection);
}
if ($languageSelection) {
$this->session->setDataByKey('languageSelection', $languageSelection);
}
if ($constantTypeSelection) {
$this->session->setDataByKey('constantTypeSelection', $constantTypeSelection);
}
if ($bottomReferenceLanguageSelection) {
$this->session->setDataByKey('bottomReferenceLanguageSelection', $bottomReferenceLanguageSelection);
}
if ($numSiteConstsSelection) {
$this->session->setDataByKey('numSiteConstsSelection', $numSiteConstsSelection);
}
if ($extkey) {
$this->session->setDataByKey('extkey', $extkey);
}
}
/**
* Assigns view width menu options and default menu selection which is fetched from session.
*
* @param string $menuName Name of the menu, which will be used as prefix of view keys for menu options and menu selection.
* Example: menuName 'extension' will produce view keys 'extensionOptions' and 'extensionSelection'
* @param array $options menu options to be assigned to view
* @return void
*/
protected function assignViewWidthMenuVariables($menuName, $options) {
$this->view->assign($menuName . 'Options', $options);
$selection = $this->checkMenuSelection($menuName, $options);
$this->view->assign($menuName . 'Selection', $selection);
}
/**
* Checks does selection exists in session or among menu options and if it does not,
* first option becomes selected.
*
* @param string $menuName Name of the menu, which will be used as prefix of view keys for menu options and menu selection.
* Example: menuName 'extension' will produce view keys 'extensionOptions' and 'extensionSelection'
* @param array $options menu options to be checked upon.
* @return string
*/
protected function checkMenuSelection($menuName, array $options) {
$selection = $this->session->getDataByKey($menuName . 'Selection');
if (!\array_key_exists($selection, $options)) {
$selection = NULL;
}
if ($selection === NULL && !empty($options)) {
$selection = array_key_first($options);
$this->session->setDataByKey($menuName . 'Selection', $selection);
return $selection;
}
return $selection;
}
/**
* Sets FlashMessage from LFException.
*
* @param LFException $lFException
* @return void
*/
public function addLFEFlashMessage(LFException $lFException) {
if ($lFException->getCode() === 0) {
$this->addFlashMessage(
$lFException->getMessage(),
LocalizationUtility::translate('failure.failure', 'lfeditor'),
AbstractMessage::ERROR
);
} elseif ($lFException->getCode() === 1) {
$this->addFlashMessage(
$lFException->getMessage(),
'',
AbstractMessage::NOTICE
);
}
}
/**
* Prepares language file select options for each extension and sets combined data in view.
*
* @return void
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws LFException
*/
protected function prepareExtensionAndLangFileOptions() {
/** @var CacheManager $cacheManager */
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
$extensions = $cacheManager->getCache('lfeditor_select_options_cache')->get('extensionAndLangFileOptions');
if (!is_array($extensions)) {
$extensions = [];
$extensionOptions = $this->configurationService->menuExtList();
$extensionGroupCount = 0;
foreach ($extensionOptions as $extAddress => $extLabel) {
$extension['extKey'] = $extLabel;
((int) ExtensionManagementUtility::isLoaded(basename($extLabel))) ?
$state = LocalizationUtility::translate('ext.loaded', 'lfeditor') :
$state = LocalizationUtility::translate('ext.notLoaded', 'lfeditor');
$extension['extLabel'] = $extLabel . ' [' . $state . ']';
$extension['languageFileOptions'] = [];
$isExtensionGroupStart = $extAddress === '###extensionGroup###' . $extLabel;
try {
if (!$isExtensionGroupStart) {
$extension['languageFileOptions'] = $this->configurationService->menuLangFileList($extAddress);
if (empty($extension['languageFileOptions'])) {
continue;
}
} elseif (++$extensionGroupCount > 1) {
$extensions[$extAddress . 'EmptySpaceBefore'] = [
'extLabel' => '',
'languageFileOptions' => [],
];
}
} catch (LFException $e) {
continue;
}
$extensions[$extAddress] = $extension;
if ($isExtensionGroupStart) {
$extensions[$extAddress . 'DelimiterAfter'] = [
'extLabel' => '======',
'languageFileOptions' => [],
];
}
}
$cacheManager->getCache('lfeditor_select_options_cache')->set('extensionAndLangFileOptions', $extensions);
}
$this->checkExtensionAndLangFileSelection($extensions);
$extensionSelection = $this->session->getDataByKey('extensionSelection');
$this->view->assign('extensions', $extensions);
$this->view->assign('extensionSelection', $extensionSelection);
$this->view->assign('extensionLabel', $extensions[$extensionSelection]['extLabel'] ?? '');
$this->view->assign('languageFileSelection', $this->session->getDataByKey('languageFileSelection'));
}
/**
* Checks do extensionSelection and languageFileSelection exist in session or among menu options and if it does not,
* first language file and belonging extension become selected and saved to session.
*
* @param array $extensions
* @return void
*/
protected function checkExtensionAndLangFileSelection(array $extensions) {
$extensionSelection = $this->session->getDataByKey('extensionSelection');
$languageFileSelection = $this->session->getDataByKey('languageFileSelection');
$selectFirstLanguageFileAndBelongingExtension = !$extensionSelection || !$languageFileSelection ||
!(array_key_exists($extensionSelection, $extensions) && array_key_exists(
$languageFileSelection,
$extensions[$extensionSelection]['languageFileOptions']
));
if ($selectFirstLanguageFileAndBelongingExtension) {
foreach ($extensions as $extAddress => $extension) {
if (empty($extension['languageFileOptions'])) {
continue;
}
$languageFileSelection = array_key_first($extension['languageFileOptions']);
$this->session->setDataByKey('languageFileSelection', $languageFileSelection);
$this->session->setDataByKey('extensionSelection', $extAddress);
break;
}
}
}
/**
* Clears cache used for storing select options.
* If $identifier is set, it clears only that entry in cache,
* otherwise it clears whole select options cache.
*
* @param string $identifier
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
protected function clearSelectOptionsCache($identifier = NULL) {
/** @var CacheManager $cacheManager */
$cacheManager = GeneralUtility::makeInstance(CacheManager::class);
if ($identifier !== NULL) {
$cacheManager->getCache('lfeditor_select_options_cache')->set($identifier, NULL);
} else {
$cacheManager->getCache('lfeditor_select_options_cache')->flush();
}
}
}

View File

@@ -0,0 +1,176 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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 TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* AddConstant controller. It contains extbase actions for AddConstant page.
*/
class AddConstantController extends AbstractBackendController {
/**
* Opens addConstant view.
* It is called in 2 cases:
* - on selection of addConstant option in main menu,
* - after redirection from action which must not change the view.
*
*/
public function addConstantAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'AddConstant');
$this->prepareExtensionAndLangFileOptions();
$this->prepareAddConstantViewMainSectionContent();
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in addConstant view.
* It is called on change of selection of any select menu in addConstant view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction($extensionSelection = NULL, $languageFileSelection = NULL, $extKey = ''): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('addConstant');
return NULL;
} else {
return $this->redirect('addConstant');
}
}
/**
* Saves the changes made in main section of addConstant view.
*
* @param string $nameOfConstant
* @param array $addConstTextArea
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function addConstantSaveAction($nameOfConstant, array $addConstTextArea): ?\Psr\Http\Message\ResponseInterface {
try {
if (empty($nameOfConstant)) {
throw new LFException('failure.select.noConstDefined');
}
$extConfig = $this->configurationService->getExtConfig();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$constExists = !empty($langData['default'][$nameOfConstant])
|| !empty($langData[$extConfig['defaultLanguage']][$nameOfConstant]);
if ($constExists) {
throw new LFException('failure.langfile.constExists');
}
$newConstLanguages = [];
foreach ($addConstTextArea as $lang => $value) {
$newConstLanguages[$lang][$nameOfConstant] = $value;
}
$this->configurationService->execWrite($newConstLanguages);
$this->session->setDataByKey('constantSelection', $nameOfConstant);
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('addConstant');
return NULL;
} else {
return $this->redirect('addConstant');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('addConstant');
return NULL;
} else {
return $this->redirect('addConstant');
}
}
/**
* Prepares main section content of addConstant view.
*
* @throws LFException
* @return void
*/
protected function prepareAddConstantViewMainSectionContent() {
$extConfig = $this->configurationService->getExtConfig();
$langArray = $this->configurationService->getLangArray($this->backendUser);
$this->view->assign('languages', $langArray);
$this->view->assign('numTextAreaRows', $extConfig['numTextAreaRows']);
}
}

View File

@@ -0,0 +1,189 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Service\FileOverrideService;
use SGalinski\Lfeditor\Utility\Functions;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* DeleteConstant controller. It contains extbase actions for DeleteConstant page.
*/
class DeleteConstantController extends AbstractBackendController {
/**
* Opens deleteConstant view.
* It is called in 2 cases:
* - on selection of deleteConstant option in main menu,
* - after redirection from action which must not change the view.
*
*/
public function deleteConstantAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'DeleteConstant');
$this->prepareExtensionAndLangFileOptions();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$fileObject = $this->configurationService->getFileObj();
if ($this->session->getDataByKey('editingMode') === 'override') {
/** @var FileOverrideService $overrideFileObject */
$overrideFileObject = $fileObject;
$overrideFileObject->deleteDuplicates();
}
$langData = $fileObject->getLocalLangData();
$constantOptions = $this->configurationService->menuConstList(
$langData,
LocalizationUtility::translate('select.nothing', 'lfeditor')
);
$this->assignViewWidthMenuVariables('constant', $constantOptions);
$this->prepareDeleteConstantViewMainSectionContent();
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in deleteConstant view.
* It is called on change of selection of any select menu in deleteConstant view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $constantSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$constantSelection = NULL,
$extKey = ''
): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
$constantSelection,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('deleteConstant');
return NULL;
} else {
return $this->redirect('deleteConstant');
}
}
/**
* Saves the changes made in main section of deleteConstant view.
*
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function deleteConstantSaveAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$constantSelection = $this->session->getDataByKey('constantSelection');
$langArray = Functions::buildLangArray();
// build modArray
$newLang = [];
foreach ($langArray as $lang) {
$newLang[$lang][$constantSelection] = '';
}
$this->configurationService->execWrite($newLang, [], TRUE);
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('deleteConstant');
return NULL;
} else {
return $this->redirect('deleteConstant');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('deleteConstant');
return NULL;
} else {
return $this->redirect('deleteConstant');
}
}
/**
* Prepares main section content of deleteConstant view.
*
* @throws LFException
* @return void
*/
protected function prepareDeleteConstantViewMainSectionContent() {
$constantSelection = $this->session->getDataByKey('constantSelection');
if (empty($constantSelection) || $constantSelection == '###default###') {
throw new LFException('failure.select.noConst', 1);
}
$this->view->assign('constantSelection', $constantSelection);
}
}

View File

@@ -0,0 +1,204 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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 TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* EditConstant controller. It contains extbase actions for EditConstant page.
*/
class EditConstantController extends AbstractBackendController {
/**
* Opens editConstant view.
* It is called in 2 cases:
* - on selection of editConstant option in main menu,
* - after redirection from action which must not change the view.
*
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function editConstantAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'EditConstant');
$this->prepareExtensionAndLangFileOptions();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$constantOptions = $this->configurationService->menuConstList(
$langData,
LocalizationUtility::translate('select.nothing', 'lfeditor')
);
$this->assignViewWidthMenuVariables('constant', $constantOptions);
$this->prepareEditConstantViewMainSectionContent($langData);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in editConstant view.
* It is called on change of selection of any select menu in editConstant view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $constantSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$constantSelection = NULL,
$extKey = ''
): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
$constantSelection,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editConstant');
return NULL;
} else {
return $this->redirect('editConstant');
}
}
/**
* Saves the changes made in main section of editConstant view.
*
* @param array $editConstTextArea
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function editConstantSaveAction(array $editConstTextArea): ?\Psr\Http\Message\ResponseInterface {
try {
$this->configurationService->execWrite($editConstTextArea);
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editConstant');
return NULL;
} else {
return $this->redirect('editConstant');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editConstant');
return NULL;
} else {
return $this->redirect('editConstant');
}
}
/**
* Prepares main section content of editConstant view.
*
* @param array $langData
* @throws LFException
* @return void
*/
protected function prepareEditConstantViewMainSectionContent(array $langData = NULL) {
$extConfig = $this->configurationService->getExtConfig();
$langArray = $this->configurationService->getLangArray($this->backendUser);
$constantSelection = $this->session->getDataByKey('constantSelection');
if (empty($constantSelection) || $constantSelection == '###default###') {
throw new LFException('failure.select.noConst', 1);
}
$languages = [];
foreach ($langArray as $lang) {
if (array_key_exists($lang, $langData) && array_key_exists($constantSelection, $langData[$lang])) {
$languages[$lang] = $langData[$lang][$constantSelection];
} else {
$languages[$lang] = '';
}
}
$this->view->assign('constantSelection', $constantSelection);
$this->view->assign('languages', $languages);
$this->view->assign('numTextAreaRows', $extConfig['numTextAreaRows']);
$this->view->assign('defaultLanguage', $extConfig['defaultLanguage']);
}
/**
* Sets chosen constant and redirects to editConstant view.
*
* @param string $constantKey
* @return void
*/
protected function prepareEditConstantAction($constantKey) {
$this->session->setDataByKey('constantSelection', $constantKey);
$this->redirect('editConstant', 'EditConstant');
}
}

View File

@@ -0,0 +1,365 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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 TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* EditFile controller. It contains extbase actions of EditFile page.
*/
class EditFileController extends AbstractBackendController {
/**
* Opens editFile view.
* It is called in 2 cases:
* - on selection of editFile option in main menu,
* - after redirection from action which must not change the view.
*
* @param integer $buttonType :
* -1 - cancel,
* 0 - no button clicked,
* 1 - back,
* 2 - next,
* 3 - save.
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function editFileAction(int $buttonType = 0): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'EditFile');
$this->prepareExtensionAndLangFileOptions();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$languageOptions = $this->configurationService->menuLangList($langData, '', $this->backendUser);
$this->assignViewWidthMenuVariables('language', $languageOptions);
$referenceLanguageOptions = $this->configurationService->menuLangList(
$langData,
LocalizationUtility::translate('select.nothing', 'lfeditor'),
$this->backendUser
);
$this->assignViewWidthMenuVariables('referenceLanguage', $referenceLanguageOptions);
$bottomReferenceLanguageOptions = $this->configurationService->menuLangList(
$langData,
'',
$this->backendUser,
TRUE
);
$this->assignViewWidthMenuVariables('bottomReferenceLanguage', $bottomReferenceLanguageOptions);
$constantTypeOptions = $this->getConstantTypeOptions();
$this->assignViewWidthMenuVariables('constantType', $constantTypeOptions);
$extConfig = $this->configurationService->getExtConfig();
$this->assignViewWidthMenuVariables('numSiteConsts', $extConfig['numSiteConstsOptions']);
$this->prepareEditFileViewMainSectionContent($langData, $buttonType);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in editFile view.
* It is called on change of selection of any select menu in editFile view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $languageSelection
* @param string $referenceLanguageSelection
* @param string $constantTypeSelection
* @param string $bottomReferenceLanguageSelection
* @param string $numSiteConstsSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$languageSelection = NULL,
$referenceLanguageSelection = NULL,
$constantTypeSelection = NULL,
$bottomReferenceLanguageSelection = NULL,
$numSiteConstsSelection = NULL,
$extKey = ''
): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
$referenceLanguageSelection,
NULL,
$languageSelection,
$constantTypeSelection,
$bottomReferenceLanguageSelection,
$numSiteConstsSelection,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editFile', NULL, NULL, ['buttonType' => 0]);
return NULL;
} else {
return $this->redirect('editFile', NULL, NULL, ['buttonType' => 0]);
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editFile', NULL, NULL, ['buttonType' => 0]);
return NULL;
} else {
return $this->redirect('editFile', NULL, NULL, ['buttonType' => 0]);
}
}
/**
* Prepares main section content of editFile view.
* Structure of the content:
* $constValues[$constant]['edit'],
* $constValues[$constant]['pattern'],
* $constValues[$constant][$extConfig['defaultLanguage']].
*
* @param array $langData
* @param integer $buttonType :
* -1 - cancel,
* 0 - no button clicked,
* 1 - back,
* 2 - next,
* 3 - save.
* @throws LFException
* @return void
*/
protected function prepareEditFileViewMainSectionContent(array $langData, int $buttonType) {
$extConfig = $this->configurationService->getExtConfig();
$numConstantsPerPage = $this->session->getDataByKey('numSiteConstsSelection');
$langList = $this->session->getDataByKey('languageSelection');
$patternList = $this->session->getDataByKey('referenceLanguageSelection');
$constTypeList = $this->session->getDataByKey('constantTypeSelection');
$parallelEdit = $patternList !== '###default###' && $patternList != $langList;
$langEdit = (array_key_exists($langList, $langData) && is_array($langData[$langList])) ? $langData[$langList] : [];
$langPattern = (array_key_exists($patternList, $langData) && is_array($langData[$patternList])) ? $langData[$patternList] : [];
$bottomReferenceLanguageSelection = $this->session->getDataByKey('bottomReferenceLanguageSelection');
$bottomReferenceLanguage = is_array($langData[$bottomReferenceLanguageSelection])
? $langData[$bottomReferenceLanguageSelection] : [];
if (empty($bottomReferenceLanguage)) {
$bottomReferenceLanguage = is_array($langData['default']) ? $langData['default'] : [];
}
$langDataSessionContinued = $buttonType !== 3;
if ($buttonType === 0) {
$this->session->setDataByKey('sessionLangDataConstantsIterator', 0);
$this->session->setDataByKey('numberLastPageConstants', 0);
}
$sessionLangDataConstantsIterator = $this->session->getDataByKey('sessionLangDataConstantsIterator');
$numberLastPageConstants = $this->session->getDataByKey('numberLastPageConstants');
// new translation
if (!$langDataSessionContinued || $buttonType <= 0) {
// adjust number of session constants
if ($constTypeList === 'untranslated' || $constTypeList === 'translated' ||
$constTypeList === 'unknown' || $buttonType <= 0
) {
$sessionLangDataConstantsIterator = 0;
} elseif (!$langDataSessionContinued) { // session written to file
$sessionLangDataConstantsIterator -= $numberLastPageConstants;
}
// delete old data in session
$this->session->setDataByKey('langfileEditNewLangData', NULL);
$this->session->setDataByKey('langfileEditConstantsList', NULL);
// get language data
if ($constTypeList === 'untranslated') {
$myLangData = array_diff_key($bottomReferenceLanguage, $langEdit);
} elseif ($constTypeList === 'unknown') {
$myLangData = array_diff_key($langEdit, $bottomReferenceLanguage);
} elseif ($constTypeList === 'translated') {
$myLangData = array_intersect_key($bottomReferenceLanguage, $langEdit);
} else {
$myLangData = $bottomReferenceLanguage;
}
$this->session->setDataByKey('langfileEditConstantsList', array_keys($myLangData));
} elseif ($buttonType === 1) { // back button
$sessionLangDataConstantsIterator -= ($numConstantsPerPage + $numberLastPageConstants);
}
// get language constants
$langData = $this->session->getDataByKey('langfileEditConstantsList');
$numConsts = is_countable($langData) ? count($langData) : 0;
if (!(is_countable($langData) ? count($langData) : 0)) {
throw new LFException('failure.select.emptyLangDataArray', 1);
}
$langfileEditNewLangData = $this->session->getDataByKey('langfileEditNewLangData');
if ($langfileEditNewLangData === '') {
$langfileEditNewLangData = [];
}
// prepare constant list for this page
$numberLastPageConstants = 0;
$constValues = [];
do {
// check number of session constants
if ($sessionLangDataConstantsIterator >= $numConsts) {
break;
}
++$numberLastPageConstants;
// set constant value (maybe already changed in this session)
$constant = $langData[$sessionLangDataConstantsIterator];
$editLangVal = $langEdit[$constant] ?? '';
if (!isset($langfileEditNewLangData[$langList][$constant])) {
$langfileEditNewLangData[$langList][$constant] = $editLangVal;
} else {
$editLangVal = $langfileEditNewLangData[$langList][$constant];
}
// set constant value (maybe already changed in this session)
$editPatternVal = $langPattern[$constant] ?? '';
if (!isset($langfileEditNewLangData[$patternList][$constant])) {
$langfileEditNewLangData[$patternList][$constant] = $editPatternVal;
} else {
$editPatternVal =
$langfileEditNewLangData[$patternList][$constant];
}
// save information about the constant
$constValues[$constant]['edit'] = $editLangVal;
$constValues[$constant]['pattern'] = $editPatternVal;
$constValues[$constant]['default'] = $bottomReferenceLanguage[$constant];
} while (++$sessionLangDataConstantsIterator % $numConstantsPerPage);
$this->session->setDataByKey('langfileEditNewLangData', $langfileEditNewLangData);
$this->session->setDataByKey('sessionLangDataConstantsIterator', $sessionLangDataConstantsIterator);
$this->session->setDataByKey('numberLastPageConstants', $numberLastPageConstants);
$this->view->assign('numTextAreaRows', $extConfig['numTextAreaRows']);
$this->view->assign('defaultLanguage', $extConfig['defaultLanguage']);
$this->view->assign('parallelEdit', $parallelEdit);
$this->view->assign('displayBackButton', $sessionLangDataConstantsIterator > $numConstantsPerPage);
$this->view->assign('displayNextButton', $sessionLangDataConstantsIterator < $numConsts);
$this->view->assign('constValues', $constValues);
$this->view->assign('curConsts', $sessionLangDataConstantsIterator);
$this->view->assign('totalConsts', $numConsts);
$this->view->assign('numSiteConstsSelection', $numConstantsPerPage);
}
/**
* Used for the constant type selector
*
* @return array
*/
private function getConstantTypeOptions(): array {
$constantTypeOptions = [];
$constantTypeOptions['all'] = LocalizationUtility::translate('const.type.all', 'lfeditor');
$constantTypeOptions['translated'] = LocalizationUtility::translate('const.type.translated', 'lfeditor');
$constantTypeOptions['unknown'] = LocalizationUtility::translate('const.type.unknown', 'lfeditor');
$constantTypeOptions['untranslated'] = LocalizationUtility::translate('const.type.untranslated', 'lfeditor');
return $constantTypeOptions;
}
/**
* Saves the changes made in main section of editFile view.
*
* @param integer $buttonType
* @param array $editFileTextArea editFileTextArea[{languageSelection}][{constKey}]
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function editFileSaveAction(int $buttonType, array $editFileTextArea): ?\Psr\Http\Message\ResponseInterface {
try {
$languageSelection = $this->session->getDataByKey('languageSelection');
$referenceLanguageSelection = $this->session->getDataByKey('referenceLanguageSelection');
$langDataSessionContinued = $buttonType !== 3;
$langfileEditNewLangData = $this->session->getDataByKey('langfileEditNewLangData');
$langfileEditNewLangData[$languageSelection] =
array_merge(
$langfileEditNewLangData[$languageSelection],
$editFileTextArea[$languageSelection]
);
// parallel edit mode?
if ($referenceLanguageSelection !== '###default###' && $referenceLanguageSelection != $languageSelection) {
$langfileEditNewLangData[$referenceLanguageSelection] =
array_merge(
$langfileEditNewLangData[$referenceLanguageSelection],
$editFileTextArea[$referenceLanguageSelection] ?? []
);
}
$this->session->setDataByKey('langfileEditNewLangData', $langfileEditNewLangData);
// write if no session continued
if (!$langDataSessionContinued) {
// Making array of languages that were changed, so only that language files will be edited.
$editedLanguages = [$languageSelection];
if ($languageSelection !== $referenceLanguageSelection) {
$editedLanguages[] = $referenceLanguageSelection;
}
$this->configurationService->execWrite($langfileEditNewLangData, [], FALSE, $editedLanguages);
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
}
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editFile', NULL, NULL, ['buttonType' => $buttonType]);
return NULL;
} else {
return $this->redirect('editFile', NULL, NULL, ['buttonType' => $buttonType]);
}
}
}

View File

@@ -0,0 +1,302 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Functions;
use SGalinski\Lfeditor\Utility\SgLib;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* General controller. It contains extbase actions for general page.
*/
class GeneralController extends AbstractBackendController {
/** Code for normal split operation */
public const NORMAL_SPLIT = 1;
/** Code for merge operation */
public const MERGE = 2;
/**
* Renders last-opened page. This action ai called upon opening LFEditor.
*
* @param bool $doStateRedirect
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function indexAction($doStateRedirect = TRUE): ?\Psr\Http\Message\ResponseInterface {
if ($doStateRedirect) {
$this->redirectToLastCalledControllerActionPair();
}
$this->resetLastCalledControllerActionPair();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('general', 'General');
return NULL;
} else {
return $this->redirect('general', 'General');
}
}
/**
* Opens general view.
* It is called in 2 cases:
* - on selection of general option in main menu,
* - after redirection from action which must not change the view.
*
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function generalAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'General');
$this->prepareExtensionAndLangFileOptions();
if (!$this->session->getDataByKey('languageFileSelection')) {
throw new LFException('failure.select.noLangfile', 1);
}
$this->prepareGeneralViewMainSectionContent();
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in general view.
* It is called on change of selection of any select menu in general view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*
*/
public function changeSelectionAction($extensionSelection = NULL, $languageFileSelection = NULL, $extKey = ''): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('general');
return NULL;
} else {
return $this->redirect('general');
}
}
/**
* Saves the changes made in main section of general view.
*
* @param string $authorName
* @param string $authorEmail
* @param string $metaDescription
* @param string $transformFile
* @param integer $splitFile
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function generalSaveAction(
$authorName = NULL,
$authorEmail = NULL,
$metaDescription = NULL,
$transformFile = NULL,
$splitFile = NULL
): ?\Psr\Http\Message\ResponseInterface {
try {
$metaArray = [
'authorName' => $authorName,
'authorEmail' => $authorEmail,
'description' => $metaDescription
];
$this->configurationService->execWrite([], $metaArray);
// split or merge
if ($transformFile === 'xlf') {
$splitFile = self::NORMAL_SPLIT;
}
if (($splitFile == self::NORMAL_SPLIT || $splitFile == self::MERGE)) {
$langModes = [];
// set vars
if ($splitFile != self::NORMAL_SPLIT && $splitFile != self::MERGE) {
$splitFile = 0;
}
$langKeys = Functions::buildLangArray();
// generate langModes
foreach ($langKeys as $langKey) {
if (!isset($langModes[$langKey])) {
$langModes[$langKey] = $splitFile;
}
}
// exec split or merge
$this->configurationService->execSplitFile($langModes);
// reinitialize file object
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
}
if (!empty($transformFile)
&& $this->configurationService->getFileObj()->getVar('fileType') != $transformFile
) {
$newFile = SgLib::setFileExtension(
$transformFile,
$this->configurationService->getFileObj()->getVar('relFile')
);
$this->configurationService->execTransform($transformFile, $newFile);
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
}
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('general');
return NULL;
} else {
return $this->redirect('general');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('general');
return NULL;
} else {
return $this->redirect('general');
}
}
/**
* Prepares main section content of general view.
*
* @return void
* @throws LFException
*/
protected function prepareGeneralViewMainSectionContent() {
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$extConfig = $this->configurationService->getExtConfig();
$referenceLanguageSelection = $extConfig['defaultLanguage'];
$langArray = $this->configurationService->getLangArray($this->backendUser);
$infoArray = Functions::genGeneralInfoArray(
$referenceLanguageSelection,
$langArray,
$this->configurationService->getFileObj()
);
$description = $infoArray['default']['meta']['description'] ?? '';
$langFileExtension = $this->configurationService->getFileObj()->getVar('fileType');
$preselectMerge = $this->isOriginSameForAllLanguages($infoArray);
$this->view->assign('infos', $infoArray);
$this->view->assign('refLangNumTranslated', $infoArray[$referenceLanguageSelection]['numTranslated']);
$this->view->assign('numTextAreaRows', $extConfig['numTextAreaRows']);
$this->view->assign('metaDescription', $description);
$this->view->assign('langFileExtension', $langFileExtension);
$this->view->assign('preselectMerge', $preselectMerge);
}
/**
* Prepares parameters for redirection to viewTreeAction.
*
* @param string $language
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function goToEditFileAction($language): ?\Psr\Http\Message\ResponseInterface {
$this->session->setDataByKey('languageSelection', $language);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('editFile', 'EditFile');
return NULL;
} else {
return $this->redirect('editFile', 'EditFile');
}
}
/**
* Switches between override mode and normal mode.
*
* @param string $editingMode 'extension', 'l10n', 'override'.
* @return void
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function switchEditingModeAction($editingMode = 'extension') {
$this->session->setDataByKey('editingMode', $editingMode);
$this->indexAction();
}
/**
* Checks do all language translations originate from same file.
*
* @param array $infoArray
* @return bool
*/
private function isOriginSameForAllLanguages(array $infoArray) {
foreach ($infoArray as $langInfo) {
if ($infoArray['default']['origin'] !== $langInfo['origin']) {
return FALSE;
}
}
return TRUE;
}
}

View File

@@ -0,0 +1,500 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Functions;
use SGalinski\Lfeditor\Utility\SgLib;
use SGalinski\Lfeditor\Utility\Typo3Lib;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* ManageBackups controller. It contains extbase actions for ManageBackups page.
*/
class ManageBackupsController extends AbstractBackendController {
/**
* A constant which is used in backup feature to indicate that
* language constant is added after backup file was made.
*/
public const LANG_CONST_ADDED = 2;
/**
* A constant which is used in backup feature to indicate that
* language constant is deleted after backup file was made.
*/
public const LANG_CONST_DELETED = 1;
/**
* A constant which is used in backup feature to indicate that
* language constant isn't deleted nor added after backup file was made. It was just changed.
*/
public const LANG_CONST_NORMAL = 0;
/**
* Opens manageBackups view.
* It is called in 2 cases:
* - on selection of manageBackups option in main menu,
* - after redirection from action which must not change the view.
*
* @param string $fileName
* @param string $langFile language file which was altered.
* @param bool $showDiff
* @throws \Exception
*/
public function manageBackupsAction($fileName = '', $langFile = '', $showDiff = FALSE): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'ManageBackups');
$extensionOptions = $this->configurationService->menuExtList();
$this->assignViewWidthMenuVariables('extension', $extensionOptions);
$this->initializeBackupObject($langFile);
$this->prepareManageBackupsViewMainSectionContent();
if ($showDiff) {
$this->generateDiffContent($fileName);
}
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in manageBackups view.
* It is called on change of selection of any select menu in searchConstant view.
*
* @param string $extensionSelection
* @param string $extKey
*/
public function changeSelectionAction($extensionSelection = NULL, $extKey = ''): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('manageBackups');
return NULL;
} else {
return $this->redirect('manageBackups');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('manageBackups');
return NULL;
} else {
return $this->redirect('manageBackups');
}
}
/**
* Prepares main section content of manageBackupsConstant view.
*
* @throws LFException
*/
protected function prepareManageBackupsViewMainSectionContent() {
$backups = $this->makeBackupsList();
if (empty($backups)) {
$this->addFlashMessage(
LocalizationUtility::translate('failure.backup.noFiles', 'lfeditor'),
'',
$severity = AbstractMessage::NOTICE,
$storeInSession = TRUE
);
return;
}
$recoverLabelThead = strtoupper(
substr(LocalizationUtility::translate('function.backupMgr.recover', 'lfeditor'), 0, 1)
);
$differenceLabelThead = strtoupper(
substr(LocalizationUtility::translate('function.backupMgr.diff.diff', 'lfeditor'), 0, 1)
);
$this->view->assign('backups', $backups);
$this->view->assign('recoverLabelThead', $recoverLabelThead);
$this->view->assign('differenceLabelThead', $differenceLabelThead);
}
/**
* Makes list of backups for use in fluid page.
*
* @return array
*/
protected function makeBackupsList() {
$backups = [];
$metaArray = $this->backupService->getBackupObj()->getMetaInfos(2);
if (is_array($metaArray)) {
$keys = array_keys($metaArray);
foreach ($keys as $langFile) {
foreach ($metaArray[$langFile] as $fileName => $informations) {
$backup = [];
// get path to filename
$backupPath = $informations['pathBackup'];
$pathSite = Environment::getPublicPath() . '/';
$file = Typo3Lib::fixFilePath($pathSite . '/' . $backupPath . '/' . $fileName);
$origFile = Typo3Lib::fixFilePath(
$this->session->getDataByKey('extensionSelection') . '/' . $langFile
);
// check state
if (!is_file($file)) {
$backup['state'] = 'function.backupMgr.missing';
} elseif (!is_file($origFile)) {
$backup['state'] = 'lang.file.missing';
} else {
$backup['state'] = 'function.backupMgr.ok';
}
$backup['date'] = date('Y-m-d H:i:s', $informations['createdAt']);
$backup['langFile'] = $langFile;
$backup['fileName'] = $fileName;
$backups[] = $backup;
}
}
}
return $backups;
}
/**
* Deletes all backups.
*
*/
public function deleteAllBackupAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->initializeBackupObject();
$delFiles = [];
$metaArray = $this->backupService->getBackupObj()->getMetaInfos(2);
foreach ($metaArray as $langFile => $metaPiece) {
$files = array_keys($metaPiece);
foreach ($files as $filename) {
$delFiles[$filename] = $langFile;
}
}
$this->backupService->execBackupDelete($delFiles);
$this->addFlashMessage(
LocalizationUtility::translate('function.backupMgr.success.deleteAll', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('manageBackups');
return NULL;
} else {
return $this->redirect('manageBackups');
}
}
/**
* Deletes backup.
*
* @param string $fileName
* @param string $langFile
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function deleteBackupAction($fileName, $langFile): ?\Psr\Http\Message\ResponseInterface {
try {
$this->initializeBackupObject($langFile);
$delFiles = [];
$delFiles[$fileName] = '';
$this->backupService->execBackupDelete($delFiles);
$this->addFlashMessage(
LocalizationUtility::translate('function.backupMgr.success.delete', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->redirect('manageBackups');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('manageBackups');
return NULL;
} else {
return $this->redirect('manageBackups');
}
}
/**
* Uses backup to recover changes.
*
* @param string $fileName
* @param string $langFile
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function recoverBackupAction($fileName, $langFile): ?\Psr\Http\Message\ResponseInterface {
try {
$this->initializeBackupObject($langFile);
// set backup file
$metaArray = $this->backupService->getBackupObj()->getMetaInfos(3);
$pathSite = Environment::getPublicPath() . '/';
$information = [
'absPath' => $pathSite . $metaArray[$fileName]['pathBackup'],
'relFile' => $fileName,
];
$this->backupService->getBackupObj()->setVar($information);
$this->backupService->getBackupObj()->readFile();
// read original file
$this->configurationService->initFileObject(
$this->backupService->getBackupObj()->getVar('langFile'),
Typo3Lib::fixFilePath($pathSite . '/' . $this->backupService->getBackupObj()->getVar('extPath'))
);
// restore
$this->backupService->execBackupRestore();
$this->addFlashMessage(
LocalizationUtility::translate('function.backupMgr.success.restore', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('manageBackups');
return NULL;
} else {
return $this->redirect('manageBackups');
}
}
/**
* Shows differences between backup and original.
*
* @param string $fileName
* @param string $langFile
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function showDifferenceBackupAction($fileName, $langFile): ?\Psr\Http\Message\ResponseInterface {
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect(
'manageBackups',
NULL,
NULL,
['fileName' => $fileName, 'langFile' => $langFile, 'showDiff' => TRUE]
);
return NULL;
} else {
return $this->redirect(
'manageBackups',
NULL,
NULL,
['fileName' => $fileName, 'langFile' => $langFile, 'showDiff' => TRUE]
);
}
}
/**
* Initializes backup object.
*
* @param string $langFile
* @throws LFException
* @throws \Exception
* @return void
*/
protected function initializeBackupObject($langFile = '') {
$pathSite = Environment::getPublicPath() . '/';
$information = [
'extPath' => SgLib::trimPath($pathSite, $this->session->getDataByKey('extensionSelection')),
'langFile' => $langFile,
];
$this->backupService->initBackupObject('base', $information);
}
/**
* Generates content which illustrates differences between backup and current state.
*
* @param string $fileName
* @return void
*/
protected function generateDiffContent($fileName) {
$localLangDiff = NULL;
$metaDiff = NULL;
$pathSite = Environment::getPublicPath() . '/';
// set backup file
$metaArray = $this->backupService->getBackupObj()->getMetaInfos(3);
$informations = [
'absPath' => Typo3Lib::fixFilePath(
$pathSite . '/' .
$metaArray[$fileName]['pathBackup']
),
'relFile' => $fileName,
];
$this->backupService->getBackupObj()->setVar($informations);
// exec diff
// read original file
$this->configurationService->initFileObject(
$this->backupService->getBackupObj()->getVar('langFile'),
Typo3Lib::fixFilePath($pathSite . '/' . $this->backupService->getBackupObj()->getVar('extPath'))
);
// read backup file
$this->backupService->getBackupObj()->readFile();
// get language data
$originalLocalLang = $this->configurationService->getFileObj()->getLocalLangData();
$backupLocalLang = $this->backupService->getBackupObj()->getLocalLangData();
// get meta data
$origMeta = $this->configurationService->getFileObj()->getMetaData();
$backupMeta = $this->backupService->getBackupObj()->getMetaData();
SgLib::fixMetaAttributes($origMeta);
unset($originalLocalLang['trans-unit']);
$localLangDiff = Functions::getBackupDiff(0, $originalLocalLang, $backupLocalLang);
$metaDiff = Functions::getMetaDiff(0, $origMeta, $backupMeta);
// generate diff
if (is_countable($localLangDiff) ? count($localLangDiff) : 0) {
$this->outputManageBackupsDiff(
$localLangDiff,
$metaDiff,
$originalLocalLang,
$backupLocalLang,
$this->configurationService->getFileObj()->getOriginLangData(),
$this->backupService->getBackupObj()->getOriginLangData(),
$origMeta,
$backupMeta
);
}
}
/**
* Generates output of difference between backup and original.
*
* @param array $diff language content (difference between backup and origin)
* @param array $metaDiff meta content (difference between backup and origin)
* @param array $origLang original language content
* @param array $backupLang backup language content
* @param array $origOriginLang original origins of each language
* @param array $backupOriginLang backup origins of each language
* @param array $origMeta original meta content
* @param array $backupMeta backup meta content
* @return void
*/
protected function outputManageBackupsDiff(
$diff,
$metaDiff,
$origLang,
$backupLang,
$origOriginLang,
$backupOriginLang,
$origMeta,
$backupMeta
) {
$differences = [];
// meta entry
if (count($metaDiff)) {
$difference = [];
$difference['legend'] = LocalizationUtility::translate('function.backupMgr.diff.meta', 'lfeditor');
$difference['constants'] = [];
foreach ($metaDiff as $label => $value) {
$constant = [];
$constant['value'] = $value;
$constant['label'] = $label;
if (!isset($backupMeta[$label])) {
$constant['state'] = self::LANG_CONST_ADDED;
} elseif (!isset($origMeta[$label])) {
$constant['state'] = self::LANG_CONST_DELETED;
} else {
$constant['state'] = self::LANG_CONST_NORMAL;
}
$difference['constants'][] = $constant;
}
$differences[] = $difference;
}
// loop each language entry
foreach ($diff as $langKey => $data) {
$difference = [];
if (!(is_countable($data) ? count($data) : 0) && ($origOriginLang[$langKey] == $backupOriginLang[$langKey])) {
continue;
}
try {
$languageFile = Typo3Lib::transTypo3File($backupOriginLang[$langKey], FALSE);
} catch (\Exception $e) {
$languageFile = $backupOriginLang[$langKey];
}
$difference['legend'] = $langKey . ' (' . $languageFile . ')';
$difference['constants'] = [];
foreach ($data as $label => $value) {
$constant = [];
$constant['value'] = $value;
$constant['label'] = $label;
if (!isset($backupLang[$langKey][$label])) {
$constant['state'] = self::LANG_CONST_ADDED;
} elseif (!isset($origLang[$langKey][$label])) {
$constant['state'] = self::LANG_CONST_DELETED;
} else {
$constant['state'] = self::LANG_CONST_NORMAL;
}
$difference['constants'][] = $constant;
}
$differences[] = $difference;
}
$this->view->assign('differences', $differences);
}
}

View File

@@ -0,0 +1,198 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Functions;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* RenameConstant controller. It contains extbase actions for RenameConstant page.
*/
class RenameConstantController extends AbstractBackendController {
/**
* Opens renameConstant view.
* It is called in 2 cases:
* - on selection of renameConstant option in main menu,
* - after redirection from action which must not change the view.
*
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function renameConstantAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'RenameConstant');
$this->prepareExtensionAndLangFileOptions();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$constantOptions = $this->configurationService->menuConstList(
$langData,
LocalizationUtility::translate('select.nothing', 'lfeditor')
);
$this->assignViewWidthMenuVariables('constant', $constantOptions);
$this->prepareRenameConstantViewMainSectionContent();
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in renameConstant view.
* It is called on change of selection of any select menu in renameConstant view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $constantSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$constantSelection = NULL,
$extKey = ''
): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
$constantSelection,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('renameConstant');
return NULL;
} else {
return $this->redirect('renameConstant');
}
}
/**
* Saves the changes made in main section of renameConstant view.
*
* @param string $newConstantName
*/
public function renameConstantSaveAction($newConstantName): ?\Psr\Http\Message\ResponseInterface {
try {
$oldConstantName = $this->session->getDataByKey('constantSelection');
$extConfig = $this->configurationService->getExtConfig();
if ($oldConstantName === $newConstantName) {
throw new LFException('failure.langfile.noChange');
}
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$constExists = !empty($langData['default'][$newConstantName])
|| !empty($langData[$extConfig['defaultLanguage']][$newConstantName]);
if ($constExists) {
throw new LFException('failure.langfile.constExists');
}
$langArray = Functions::buildLangArray();
$newLang = [];
foreach ($langArray as $lang) {
if (isset($langData[$lang][$oldConstantName])) {
$newLang[$lang][$newConstantName] = $langData[$lang][$oldConstantName];
}
$newLang[$lang][$oldConstantName] = '';
}
$this->configurationService->execWrite($newLang, [], TRUE);
$this->addFlashMessage(
LocalizationUtility::translate('lang.file.write.success', 'lfeditor'),
'',
$severity = AbstractMessage::OK,
$storeInSession = TRUE
);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('renameConstant');
return NULL;
} else {
return $this->redirect('renameConstant');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
* @return \Psr\Http\Message\ResponseInterface|null
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('renameConstant');
return NULL;
} else {
return $this->redirect('renameConstant');
}
}
/**
* Prepares main section content of deleteConstant view.
*
* @throws LFException
* @return void
*/
protected function prepareRenameConstantViewMainSectionContent() {
$constantSelection = $this->session->getDataByKey('constantSelection');
if (empty($constantSelection) || $constantSelection === '###default###') {
throw new LFException('failure.select.noConst', 1);
}
$this->view->assign('constantSelection', $constantSelection);
}
}

View File

@@ -0,0 +1,182 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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 TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
/**
* SearchConstant controller. It contains extbase actions for SearchConstant page.
*/
class SearchConstantController extends AbstractBackendController {
/**
* Opens searchConstant view.
* It is called in 2 cases:
* - on selection of searchConstant option in main menu,
* - after redirection from action which must not change the view.
*
* @param bool $searchDone
* @throws \TYPO3\CMS\Core\Cache\Exception\NoSuchCacheException
*/
public function searchConstantAction($searchDone = FALSE): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'SearchConstant');
$this->prepareExtensionAndLangFileOptions();
$this->prepareSearchConstantViewMainSectionContent($searchDone);
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in searchConstant view.
* It is called on change of selection of any select menu in searchConstant view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction($extensionSelection = NULL, $languageFileSelection = NULL, $extKey = ''): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('searchConstant');
return NULL;
} else {
return $this->redirect('searchConstant');
}
}
/**
* Saves the changes made in main section of searchConstant view.
*
* @param boolean $caseSensitive
* @param string $searchStr
*/
public function searchConstantSearchAction($caseSensitive, $searchStr): ?\Psr\Http\Message\ResponseInterface {
$searchResultArray = [];
try {
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$viewLanguages = $this->configurationService->getLangArray($this->backendUser);
$searchOptions = $caseSensitive ? '' : 'i';
if (!preg_match('/^\/.*\/.*$/', $searchStr) && !empty($searchStr)) {
foreach ($viewLanguages as $langKey) {
if (!array_key_exists($langKey, $langData) || !is_array($langData[$langKey])) {
continue;
}
foreach ($langData[$langKey] as $labelKey => $labelValue) {
$labelKeyOrValueContainSearchStr = preg_match(
'/' . $searchStr . '/' . $searchOptions,
$labelKey
) || preg_match('/' . $searchStr . '/' . $searchOptions, $labelValue);
if ($labelKeyOrValueContainSearchStr) {
$searchResultArray[$langKey][$labelKey] = $labelValue;
}
}
}
if (!count($searchResultArray)) {
throw new LFException('failure.search.noConstants', 1);
}
} else {
throw new LFException('function.const.search.enterSearchStr', 1);
}
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->session->setDataByKey('searchResultArray', $searchResultArray);
$this->session->setDataByKey('searchString', $searchStr);
$this->session->setDataByKey('searchCaseSensitive', $caseSensitive);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('searchConstant', NULL, NULL, ['searchDone' => TRUE]);
return NULL;
} else {
return $this->redirect('searchConstant', NULL, NULL, ['searchDone' => TRUE]);
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('searchConstant', NULL, NULL, ['searchDone' => TRUE]);
return NULL;
} else {
return $this->redirect('searchConstant', NULL, NULL, ['searchDone' => TRUE]);
}
}
/**
* Prepares main section content of searchConstant view.
*
* @param bool $searchDone
* @return void
*/
protected function prepareSearchConstantViewMainSectionContent($searchDone) {
$searchResultArray = [];
if ($searchDone) {
$searchResultArray = $this->session->getDataByKey('searchResultArray');
} else {
$this->session->setDataByKey('searchResultArray', []);
}
$searchString = $this->session->getDataByKey('searchString');
$searchCaseSensitive = $this->session->getDataByKey('searchCaseSensitive');
$this->view->assign('searchResultArray', $searchResultArray);
$this->view->assign('searchString', $searchString);
$this->view->assign('searchCaseSensitive', $searchCaseSensitive);
}
}

View File

@@ -0,0 +1,283 @@
<?php
namespace SGalinski\Lfeditor\Controller;
/***************************************************************
* 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\Functions;
use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* ViewTree controller. It contains extbase actions for ViewTree page.
*/
class ViewTreeController extends AbstractBackendController {
/*********************************************
* Actions called from viewTree view *
*********************************************/
/**
* Opens viewTree view.
* It is called in 2 cases:
* - on selection of viewTree option in main menu,
* - after redirection from action which must not change the view.
*
*/
public function viewTreeAction(): ?\Psr\Http\Message\ResponseInterface {
try {
$this->view->assign('controllerName', 'ViewTree');
$this->prepareExtensionAndLangFileOptions();
$this->configurationService->initFileObject(
$this->session->getDataByKey('languageFileSelection'),
$this->session->getDataByKey('extensionSelection')
);
$languageOptions = $this->configurationService->menuLangList(
$this->configurationService->getFileObj()->getLocalLangData(),
'',
$this->backendUser
);
$this->assignViewWidthMenuVariables('language', $languageOptions);
$referenceLanguageOptions = $this->configurationService->menuLangList(
$this->configurationService->getFileObj()->getLocalLangData(),
LocalizationUtility::translate('select.nothing', 'lfeditor'),
$this->backendUser
);
$this->assignViewWidthMenuVariables('referenceLanguage', $referenceLanguageOptions);
$this->prepareViewTreeViewMainSectionContent();
} catch (LFException $e) {
$this->addLFEFlashMessage($e);
}
$this->commonViewRenderingActionSettings();
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->view->assign('V11', FALSE);
return NULL;
} else {
$this->view->assign('V11', TRUE);
return $this->createBackendResponse();
}
}
/**
* This action saves in session currently selected options from selection menus in viewTree view.
* It is called on change of selection of any select menu in viewTree view.
*
* @param string $extensionSelection
* @param string $languageFileSelection
* @param string $languageSelection
* @param string $referenceLanguageSelection
* @param string $extKey
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function changeSelectionAction(
$extensionSelection = NULL,
$languageFileSelection = NULL,
$languageSelection = NULL,
$referenceLanguageSelection = NULL,
$extKey = ''
): ?\Psr\Http\Message\ResponseInterface {
$this->saveSelectionsInSession(
$extensionSelection,
$languageFileSelection,
$referenceLanguageSelection,
NULL,
NULL,
$languageSelection,
NULL,
NULL,
$extKey
);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('viewTree');
return NULL;
} else {
return $this->redirect('viewTree');
}
}
/**
* Selects explodeToken.
*
* @param string $explodeToken
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function selectExplodeTokenAction($explodeToken): ?\Psr\Http\Message\ResponseInterface {
$this->session->setDataByKey('explodeToken', $explodeToken);
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('viewTree');
return NULL;
} else {
return $this->redirect('viewTree');
}
}
/**
* Clears extensionAndLangFileOptions cache, and in that way refreshes list of language file options in select box.
*
*/
public function refreshLanguageFileListAction(): ?\Psr\Http\Message\ResponseInterface {
$this->clearSelectOptionsCache('extensionAndLangFileOptions');
if (version_compare(\TYPO3\CMS\Core\Utility\VersionNumberUtility::getCurrentTypo3Version(), '11.0.0', '<')) {
$this->redirect('viewTree');
return NULL;
} else {
return $this->redirect('viewTree');
}
}
/**
* Prepares main section content of viewTree view.
*
*/
protected function prepareViewTreeViewMainSectionContent(): void {
$explodeToken = $this->session->getDataByKey('explodeToken');
if ($explodeToken === "") {
$explodeToken = '.';
}
$langData = $this->configurationService->getFileObj()->getLocalLangData();
$refLangSelection = $this->session->getDataByKey('referenceLanguageSelection');
$languageSelection = $this->session->getDataByKey('languageSelection');
$tree = Functions::genTreeInfoArray($langData[$languageSelection] ?? [], $langData[$refLangSelection] ?? [], $explodeToken);
$extConfig = $this->configurationService->getExtConfig();
$treeHide = $extConfig['treeHide'];
$fluidTree = [];
$levelIndex = 0;
$this->addLevelElementsToFluidTree($tree, $levelIndex, NULL, $fluidTree, $treeHide);
$this->view->assign('fluidTree', $fluidTree);
$this->view->assign('treeHide', $treeHide);
$this->view->assign('explodeToken', $explodeToken);
}
/**
* @var integer Width of margin which is added if a branch is missing.
*/
protected $marginLeftSpaceUnit = 18;
/**
* Makes tree structure which contains constants. The structure is optimised for recursive use on fluid pages.
*
* @param array $sourceTree
* @param int $level
* @param string $parentKey
* @param array $fluidTree This structure is used for setting tree-data for use in fluid.
* @param boolean $treeHide Default state of tree (TRUE - hidden, FALSE - opened)
* @throws LFException
* @return void
*/
protected function addLevelElementsToFluidTree(
array $sourceTree,
int $level,
$parentKey,
array &$fluidTree,
$treeHide
) {
if (empty($sourceTree)) {
throw new LFException('failure.select.emptyLanguage', 1);
}
$constKeys = array_keys($sourceTree[$level]);
$index = 0;
foreach ($sourceTree[$level] as $constKey => $treeNode) {
if ($level === 0 || $treeNode['parent'] === $parentKey) {
$fluidTreeElem = [];
$fluidTreeElem['label'] = $treeNode['name'];
$fluidTreeElem['parent'] = $fluidTree; //$treeNode['parent'];//; //
$fluidTreeElem['type'] = $treeNode['type'] ?? '';
$fluidTreeElem['isBottom'] = (!array_key_exists($index + 1, $constKeys) ||
$sourceTree[$level][$constKeys[$index + 1]]['parent'] !== $treeNode['parent']) ? 1 : 0;
$icons = $this->prepareTreeIcons($level, $treeHide, $fluidTreeElem, isset($treeNode['childs']));
$fluidTreeElem['icons'] = $icons;
if (array_key_exists($level + 1, $sourceTree)) {
$this->addLevelElementsToFluidTree(
$sourceTree,
$level + 1,
$constKey,
$fluidTreeElem,
$treeHide
);
}
if ($level > 0) {
$fluidTree['children'][$constKey] = $fluidTreeElem;
} else {
$fluidTree[$constKey] = $fluidTreeElem;
}
}
$index++;
}
}
/**
* Sorts and adds icons in tree structure.
*
* @param int $level Tree level of constant.
* @param boolean $treeHide Indicator which shows should all tree elements be closed (hidden) by default.
* @param array $fluidTreeElem Element of tree which is built for use on fluid page.
* @param boolean $hasChildren Indicator does this tree element have children
* @return array
*/
protected function prepareTreeIcons(int $level, $treeHide, array $fluidTreeElem, $hasChildren): array {
$icons = [];
$leftMargins = [];
$marginLeftSpaceCounter = 0;
for ($iconLevel = $level, $currentFluidTreeElem = $fluidTreeElem;
$currentFluidTreeElem;
$iconLevel--, $currentFluidTreeElem = $currentFluidTreeElem['parent']) {
$iconName = '.png';
if ($iconLevel === $level) {
if ($currentFluidTreeElem['isBottom']) {
$iconName = 'Bottom' . $iconName;
}
if ($hasChildren) {
$iconName = 'tree' . ($treeHide && $level != 0 ? 'Plus' : 'Minus') . $iconName;
} else {
$iconName = 'join' . $iconName;
}
} else {
if (!$currentFluidTreeElem['isBottom']) {
$iconName = 'line' . $iconName;
$leftMargins[] = $marginLeftSpaceCounter;
$marginLeftSpaceCounter = 0;
} else {
$marginLeftSpaceCounter += $this->marginLeftSpaceUnit;
continue;
}
}
$icons[] = ['name' => $iconName];
}
$leftMargins[] = $marginLeftSpaceCounter;
for ($iterator = 0, $iconsSize = count($icons); $iterator < $iconsSize; $iterator++) {
$icons[$iterator]['marginLeft'] = $leftMargins[$iterator];
}
return $icons;
}
}

View File

@@ -0,0 +1,58 @@
<?php
/**
*
* 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!
*/
namespace SGalinski\Lfeditor\Exceptions;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* DirectoryAccessRightsException can be thrown for exceptions relating to file access and write permissions
* in directories
*
* @package SGalinski\Lfeditor\Exceptions
* @author Kevin Ditscheid <kevin.ditscheid@sgalinski.de>
*/
class DirectoryAccessRightsException extends \Exception {
/**
* DirectoryAccessRightsException constructor.
*
* @param string $message
* @param int $code
* @param \Exception|NULL $previous
*/
public function __construct($message = '', $code = 0, $previous = NULL) {
if (\strpos($message, 'LLL:') === 0) {
$locallizedMessage = LocalizationUtility::translate($message, 'lfeditor');
$message = $locallizedMessage ?? $message;
}
if ($message === '') {
$message = 'LFExeption: no error message given !!!';
}
parent::__construct($message, $code, $previous);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace SGalinski\Lfeditor\Exceptions;
/***************************************************************
* 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 TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* adds a new error exception
*/
class LFException extends Exception {
/**
* Constructor
*
* @param string $msg error message (this message would be translated by TYPO3)
* @param integer $severityCode (0 = error (default), 1 = notice)
* @param string $staticMsg static message (appended at the localized string)
*/
public function __construct($msg, $severityCode = 0, $staticMsg = '') {
if (!empty($msg)) {
$msg = LocalizationUtility::translate($msg, 'lfeditor');
}
if (empty($msg)) {
$msg = 'LFExeption: no error message given !!!';
}
parent::__construct($msg . ' ' . $staticMsg, $severityCode);
}
}

View 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');
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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"' : '');
}
}

View 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(
"#(&lt;\!\[CDATA\[.*\]\]&gt;)#sU",
function ($matches) {
return htmlspecialchars_decode($matches[1]);
},
$xmlRaw
);
$dom->loadXML($xmlRaw);
$formattedXml = $dom->saveXML();
return $formattedXml;
}
}

View 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';
}
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace SGalinski\Lfeditor\Session;
/***************************************************************
* 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\SingletonInterface;
/**
* PHP Session handler
*/
class PhpSession implements SingletonInterface {
/**
* @var string
*/
protected $sessionKey;
/**
* Constructor
*/
public function __construct() {
session_start();
$this->sessionKey = uniqid('', TRUE);
}
/**
* @param string $sessionKey
* @return void
*/
public function setSessionKey($sessionKey) {
$this->sessionKey = $sessionKey;
}
/**
* Returns the current session key
*
* @return string
*/
public function getSessionKey() {
return $this->sessionKey;
}
/**
* Exchanges the complete session data
*
* @param mixed $data
* @return void
*/
public function exchangeData($data) {
$this->destroy();
$_SESSION[$this->sessionKey] = $data;
}
/**
* Returns the complete session data
*
* @return mixed
*/
public function getData() {
return $_SESSION[$this->sessionKey];
}
/**
* Sets data inside the session below the given key
*
* @param string $key
* @param mixed $data
* @return void
*/
public function setDataByKey($key, $data) {
$_SESSION[$this->sessionKey][$key] = $data;
}
/**
* Returns data of the session defined by the given key
*
* @param string $key
* @return mixed
*/
public function getDataByKey($key) {
return $_SESSION[$this->sessionKey][$key] ?? '';
}
/**
* Removes data defined by the given key.
*
* @param string $key
* @return void
*/
public function unsetDataByKey($key) {
unset($_SESSION[$this->sessionKey][$key]);
}
/**
* Removes all session data
*
* @return void
*/
public function destroy() {
unset($_SESSION[$this->sessionKey]);
}
}

View File

@@ -0,0 +1,60 @@
<?php
namespace SGalinski\Lfeditor\Utility;
/***************************************************************
* 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!
***************************************************************/
/**
* Class which adds CDATA support to SimpleXMLElement.
*/
class CdataSupportingSimpleXMLElement extends \SimpleXMLElement {
/**
* Add CDATA text in a node
*
* @param string $cdataText The CDATA value to add
* @return void
*/
protected function addCData($cdataText) {
$node = dom_import_simplexml($this);
$no = $node->ownerDocument;
$node->appendChild($no->createCDATASection($cdataText));
}
/**
* Create a child with CDATA value
*
* @param string $name The name of the child element to add.
* @param string $cdataText The CDATA value of the child element.
* @return CdataSupportingSimpleXMLElement
*/
public function addChildCData($name, $cdataText) {
/** @var CdataSupportingSimpleXMLElement $child */
$child = $this->addChild($name);
if ($cdataText !== '') {
$child->addCData($cdataText);
}
return $child;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/**
*
* 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!
*/
namespace SGalinski\Lfeditor\Utility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
/**
* Class ExtensionUtility
*
* @package SGalinski\Lfeditor\Utility
* @author Kevin Ditscheid <kevin.ditscheid@sgalinski.de>
*/
class ExtensionUtility {
/**
* Get the extension configuration
*
* @return array
*/
public static function getExtensionConfiguration(): array {
return $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['lfeditor'] ?? [];
}
}

View File

@@ -0,0 +1,400 @@
<?php
namespace SGalinski\Lfeditor\Utility;
/***************************************************************
* 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\Service\FileBasePHPService;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* contains functions for the 'lfeditor' extension
*/
class Functions {
/**
* Prepares the extension array.
*
* This function creates the surface of the select box and adds
* some additional information to each entry.
*
* Structure of file array:
* $fileArray[textHeader] = further arrays with extension paths
*
* @param array $fileArray see above
* @return array prepared array
*/
public static function prepareExtList($fileArray) {
$myArray = [];
foreach ($fileArray as $header => $extPaths) {
if (!is_array($extPaths) || !count($extPaths)) {
continue;
}
unset($prepArray);
foreach ($extPaths as $extPath) {
$prepArray[$extPath] = basename($extPath);
}
ksort($prepArray);
$myArray = array_merge($myArray, ['###extensionGroup###' . $header => $header], $prepArray);
}
return $myArray;
}
/**
* searches extensions in a given path
*
* Modes for $state:
* 0 - loaded and unloaded
* 1 - only loaded
* 2 - only unloaded
*
* @param string $path path
* @param integer $state optional: extension state to ignore (see above)
* @param string $extIgnoreRegExp optional: directories to ignore (regular expression; pcre with slashes)
* @param string $extWhitelistRegExp optional: keep only those directories (regular expression; pcre with slashes)
* @return array result of the search
* @throws Exception raised, if the given path cant be opened for reading
*/
public static function searchExtensions($path, $state = 0, $extIgnoreRegExp = '', $extWhitelistRegExp = '') {
if (!@$fhd = opendir($path)) {
throw new Exception('cant open "' . $path . '"');
}
$path = rtrim($path, '/');
$extArray = [];
while ($extDir = readdir($fhd)) {
$extDirPath = $path . '/' . $extDir;
// ignore all unless the file is a directory and no point dir
if (!is_dir($extDirPath) || preg_match('/^\.{1,2}$/', $extDir)) {
continue;
}
// check, if the directory/extension should be saved
if (preg_match($extIgnoreRegExp, $extDir)) {
continue;
}
// check, if the directory/extension should be saved
if ($extWhitelistRegExp !== '' && !preg_match($extWhitelistRegExp, $extDir)) {
continue;
}
// state filter
if ($state) {
$extState = (int) ExtensionManagementUtility::isLoaded($extDir);
if (($extState && $state == 2) || (!$extState && $state == 1)) {
continue;
}
}
$extArray[] = $extDirPath;
}
closedir($fhd);
return $extArray;
}
/**
* prepares a given language string for section output
*
* @param string $value language string
* @return string prepared output in sections
*/
public static function prepareSectionName($value) {
return html_entity_decode(LocalizationUtility::translate($value, 'lfeditor'));
}
/**
* checks and returns given languages or TYPO3 language list if the given content was empty
*
* @param array $languages optional: some language shortcuts
* @return array language list
*/
public static function buildLangArray($languages = NULL) {
if (!is_array($languages) || !count($languages)) {
return SgLib::getSystemLanguages();
} else {
return $languages;
}
}
/**
* generates output for a diff between the backup and original file
*
* Note that the generated diff will be an array with a normal structure like
* any language content array.
*
* Modes of diffType:
* - all changes at the original since the backup was done (0)
* - only changes at the original (1)
* - only changes at the backup (2)
*
* @param integer $diffType see above for available modes
* @param array $origLang original language data
* @param array $backupLocalLang backup language data
* @return mixed generated diff
*/
public static function getBackupDiff($diffType, $origLang, $backupLocalLang) {
// get all languages and generate the diff
$langKeys = array_merge(array_keys($origLang), array_keys($backupLocalLang));
$diff = [];
foreach ($langKeys as $langKey) {
// prevent warnings
if (!is_array($origLang[$langKey])) {
$origLang[$langKey] = [];
}
if (!is_array($backupLocalLang[$langKey])) {
$backupLocalLang[$langKey] = [];
}
$origDiff[$langKey] = [];
$backupDiff[$langKey] = [];
// generate diff
if (!$diffType || $diffType == 1) {
$origDiff[$langKey] = array_diff_assoc($origLang[$langKey], $backupLocalLang[$langKey]);
}
if (!$diffType || $diffType == 2) {
$backupDiff[$langKey] = array_diff_assoc(
$backupLocalLang[$langKey],
$origLang[$langKey]
);
}
$diff[$langKey] = array_merge($origDiff[$langKey], $backupDiff[$langKey]);
}
return $diff;
}
/**
* generates output for a meta diff between the backup and original file
*
* Note that the generated diff will be an array with a normal structure like
* any meta content array.
*
* Modes of diffType:
* - all changes at the original since the backup was done (0)
* - only changes at the original (1)
* - only changes at the backup (2)
*
* @param integer $diffType see above for available modes
* @param array $origMeta original meta data
* @param array $backupMeta backup meta data
* @return mixed generated diff
*/
public static function getMetaDiff($diffType, $origMeta, $backupMeta) {
$origDiff = [];
$backupDiff = [];
if (!$diffType || $diffType == 1) {
$origDiff = array_diff_assoc($origMeta, $backupMeta);
}
if (!$diffType || $diffType == 2) {
$backupDiff = array_diff_assoc($backupMeta, $origMeta);
}
if ($diffType == 1) {
return $origDiff;
} elseif ($diffType == 2) {
return $backupDiff;
} else {
return array_merge($origDiff, $backupDiff);
}
}
/**
* generates a general information array
*
* @param string $refLang reference language
* @param array $languages language key array
* @param FileBasePHPService $fileObj file object
* @return array general information array
* @see outputGeneral()
*/
public static function genGeneralInfoArray($refLang, $languages, $fileObj) {
$numTranslated = [];
// reference language data information
$localRefLangData = $fileObj->getLocalLangData($refLang);
// generate needed data
$infos = [];
foreach ($languages as $langKey) {
// get origin data and meta information
$origin = $fileObj->getOriginLangData($langKey);
$infos['default']['meta'] = $fileObj->getMetaData();
// language data
$localLangData = $fileObj->getLocalLangData($langKey);
// detailed constants information
$infos[$langKey]['numUntranslated'] =
count(array_diff_key($localRefLangData, $localLangData));
$infos[$langKey]['numUnknown'] =
count(array_diff_key($localLangData, $localRefLangData));
$infos[$langKey]['numTranslated'] =
count(array_intersect_key($localLangData, $localRefLangData));
// set origin
try {
$infos[$langKey]['origin'] = '[-]';
if (!empty($origin)) {
$infos[$langKey]['origin'] = Typo3Lib::transTypo3File($origin, FALSE);
}
} catch (Exception $e) {
$pathSite = Environment::getPublicPath() . '/';
$infos[$langKey]['origin'] = SgLib::trimPath($pathSite, $origin);
}
}
// Sort by numTranslated DESC
foreach ($infos as $key => $row) {
$numTranslated[$key] = $row['numTranslated'];
}
array_multisort($numTranslated, SORT_DESC, $infos);
return $infos;
}
/**
* generates a tree information array
*
* structure:
* tree[dimension][branch]['name'] = name of constant
* tree[dimension][branch]['type'] = type of constant (0=>normal;1=>untranslated;2=>unknown)
* tree[dimension][branch]['parent'] = parentOfBranch (absConstName)
* tree[dimension][branch]['childs'] = amount of children
*
* @param array $langData language data (only one language)
* @param array $refLang reference data (only reference language)
* @param string $expToken explode token
* @return array tree information array
*/
public static function genTreeInfoArray($langData, $refLang, $expToken) {
// reference language
$refConsts = [];
if (is_array($refLang) && count($refLang)) {
$refConsts = array_keys($refLang);
}
$langConsts = array_merge(array_keys($langData), $refConsts);
// generate tree information array
$curAbsName = '';
$tree = [];
foreach ($langConsts as $constant) {
// add root
$tree[0]['Root']['name'] = 'Root';
// get type
$type = 0; // normal
if (!in_array($constant, $refConsts)) {
$type = 2;
} // unknown
elseif (empty($langData[$constant])) {
$type = 1;
} // untranslated
$branches = explode($expToken, $constant);
$numBranches = is_countable($branches) ? count($branches) : 0;
for ($i = 0, $curDim = 1; $i < $numBranches; ++$i, ++$curDim) {
// get current absolute constant name
if (!$i) {
$curAbsName = $branches[$i];
} else {
$curAbsName .= $expToken . $branches[$i];
}
if (isset($tree[$curDim][$curAbsName]['name'])) {
continue;
}
// add branch
$tree[$curDim][$curAbsName]['name'] = $branches[$i];
$tree[$curDim][$curAbsName]['type'] = $type;
// set parent
if ($i > 0) {
$parentAbsName = substr($curAbsName, 0, strrpos($curAbsName, (string) $expToken));
} else {
$parentAbsName = 'Root';
}
$tree[$curDim][$curAbsName]['parent'] = $parentAbsName;
if (array_key_exists('childs', $tree[$curDim - 1][$parentAbsName])) {
++$tree[$curDim - 1][$parentAbsName]['childs'];
} else {
$tree[$curDim - 1][$parentAbsName]['childs'] = 1;
}
}
}
return $tree;
}
/**
* get best explode token of a given language data
*
* @param string $curToken current token
* @param array $langData some test language data
* @return string new token
*/
public static function getExplodeToken($curToken, $langData) {
$ascii = [];
// get current token
if (!empty($curToken)) {
return $curToken;
}
// return default token, if no test data found
if (!is_array($langData) || !count($langData)) {
return '.';
}
// get ascii codes (possible explode values)
$ascii['.'] = ord('.');
$ascii['_'] = ord('_');
// get best possible character of the default language
$defKeys = array_keys($langData);
$numKeys = count($defKeys);
$maxTestCount = ($numKeys >= 10) ? 10 : $numKeys;
$counts = [];
for ($i = 0; $i < $maxTestCount; ++$i) {
$curCounts = count_chars($defKeys[$i], 1);
foreach ($ascii as $sign) {
$counts[$sign] += $curCounts[$sign];
}
}
// get curToken
foreach ($counts as $sign => $curCounts) {
if ($curCounts > $counts[$curToken]) {
$curToken = $sign;
}
}
return chr($curToken);
}
}

View File

@@ -0,0 +1,349 @@
<?php
namespace SGalinski\Lfeditor\Utility;
/***************************************************************
* 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 TYPO3\CMS\Core\Localization\Locales;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* personal library with lots of useful methods
*/
class SgLib {
#################################
######## string functions #######
#################################
/**
* trims some string from an given path
*
* @param string $replace string part to delete
* @param string $path some path
* @param string $prefix some prefix for the new path
* @return string new path
*/
public static function trimPath($replace, $path, $prefix = '') {
return trim(str_replace($replace, '', $path), '/') . $prefix;
}
#####################################
######## filesystem functions #######
#####################################
/**
* reads the extension of a given filename
*
* @param string $file filename
* @return string extension of a given filename
*/
public static function getFileExtension($file) {
return substr($file, strrpos($file, '.') + 1);
}
/**
* replaces the file extension in a given filename
*
* @param string $type new file extension
* @param string $file filename
* @return string new filename
*/
public static function setFileExtension($type, $file) {
return substr($file, 0, strrpos($file, '.') + 1) . $type;
}
/**
* checks write permission of a given file (checks directory permission if file does not exists)
*
* @param string $file file path
* @return boolean true or false
*/
public static function checkWritePerms($file) {
if (!is_file($file)) {
$file = dirname($file);
}
if (!is_writable($file)) {
return FALSE;
}
return TRUE;
}
/**
* deletes given files
*
* @param array $files files
* @return void
* @throws Exception raised, if some files cant be deleted (thrown after deletion of all)
*/
public static function deleteFiles($files) {
// delete all old files
$error = [];
foreach ($files as $file) {
if (is_file($file)) {
if (!unlink($file)) {
$error[] = $file;
}
}
}
if (count($error)) {
throw new Exception('following files cant be deleted: "' . implode(', ', $error) . '"');
}
}
/**
* Creates a full path (all nonexistent directories will be created)
*
* @param string $path full path
* @param string $protectArea protected path (i.e. /var/www -- needed for basedir restrictions)
* @return void
* @throws Exception raised if some path token cant be created
*/
public static function createDir($path, $protectArea) {
[$extensionNameFromFile, $extPrefixedPath] = Typo3Lib::getExtNameFromOverrideFile($path);
if (strpos($extensionNameFromFile, '-') !== FALSE) {
$path = str_replace(
$extensionNameFromFile,
str_replace('-', '_', $extensionNameFromFile),
$path
);
}
if (!is_dir($path)) {
$pathAsArray = explode('/', self::trimPath($protectArea, $path));
$tmp = '';
foreach ($pathAsArray as $dir) {
$tmp .= $dir . '/';
if (is_dir($protectArea . $tmp)) {
continue;
}
$concurrentDirectory = $protectArea . $tmp;
GeneralUtility::mkdir_deep($concurrentDirectory);
if (!is_dir($concurrentDirectory)) {
throw new Exception('path "' . $protectArea . $tmp . '" can\'t be created.');
}
}
}
}
/**
* deletes a directory (all subdirectories and files will be deleted)
*
* @param string $path full path
* @return void
* @throws Exception raised if a file or directory cant be deleted
*/
public static function deleteDir($path) {
if (!$dh = @opendir($path)) {
throw new Exception('directory "' . $path . '" cant be readed');
}
while ($file = readdir($dh)) {
$myFile = $path . '/' . $file;
// ignore links and point directories
if (preg_match('/\.{1,2}/', $file) || is_link($myFile)) {
continue;
}
if (is_file($myFile)) {
if (!unlink($myFile)) {
throw new Exception('file "' . $myFile . '" cant be deleted');
}
} elseif (is_dir($myFile)) {
SgLib::deleteDir($myFile);
}
}
closedir($dh);
if (!@rmdir($path)) {
throw new Exception('directory "' . $path . '" cant be deleted');
}
}
/**
* searches defined files in a given path recursively
*
* @param string $path search in this path
* @param string $searchRegex optional: regular expression for files
* @param integer $pathDepth optional: current path depth level (max 9)
* @return array
* @throws Exception raised if the search directory cant be read
*/
public static function searchFiles($path, $searchRegex = '', $pathDepth = 0) {
// endless recursion protection
$fileArray = [];
if ($pathDepth >= 9) {
return $fileArray;
}
// open directory
if (!$fhd = @opendir($path)) {
throw new Exception('directory "' . $path . '" cant be read');
}
// iterate through the directory entries
while ($file = readdir($fhd)) {
$filePath = $path . '/' . $file;
// ignore links and special directories (. and ..)
if (preg_match('/^\.{1,2}$/', $file) || is_link($filePath)) {
continue;
}
// if it's a file and not excluded by the search filter, we can add it
// to the file array
if (is_file($filePath)) {
if ($searchRegex == '') {
$fileArray[] = $filePath;
} elseif (preg_match($searchRegex, $file)) {
$fileArray[] = $filePath;
}
continue;
}
// next dir
if (is_dir($filePath)) {
$fileArray = array_merge(
$fileArray,
(array) SgLib::searchFiles($filePath, $searchRegex, $pathDepth + 1)
);
}
}
closedir($fhd);
return $fileArray;
}
/**
* Returns all available system languages defined in TYPO3
*
* @return array
*/
public static function getSystemLanguages() {
/** @var Locales $locales */
$locales = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
$availableLanguageKeys = $locales->getLocales();
foreach ($availableLanguageKeys as $index => $language) {
if ($language === 'default') {
$availableLanguageKeys[$index] = 'default';
break;
}
}
return $availableLanguageKeys;
}
/**
* Convert special HTML characters to HTML entities, but ignores CDATA section.
*
* @param string $value
* @return string
*/
public static function htmlSpecialCharsIgnoringCdata($value) {
$cdataStart = strpos($value, '<![CDATA[');
$cdataEnd = strpos($value, ']]>');
if ($cdataStart !== FALSE && $cdataEnd !== FALSE) {
$cdataEnd += 3;
$valueBefore = substr($value, 0, $cdataStart);
$cdataValue = substr($value, $cdataStart, $cdataEnd - $cdataStart);
$valueAfter = substr($value, $cdataEnd, strlen($value) - $cdataEnd);
$value = htmlspecialchars($valueBefore) . '&lt;![CDATA[' . $cdataValue . ']]&gt;'
. htmlspecialchars($valueAfter);
} else {
$value = htmlspecialchars($value);
}
return $value;
}
/**
* Checks if CDATA tag exists in string.
*
* @param string $value
* @return bool
*/
public static function checkForCdataInString($value) {
$cdataStart = strpos($value, '<![CDATA[');
$cdataEnd = strpos($value, ']]>');
return ($cdataStart !== FALSE && $cdataEnd !== FALSE);
}
/**
* Writes at end of PHP file, just before php closing tag or at the end of file if php closing tag does not exist.
* If specified file does not exist, it will be crated.
*
* @param string $filePath
* @param string $lineToAdd
* @return void
*/
public static function appendToPHPFile($filePath, $lineToAdd) {
if (!is_file($filePath)) {
$emptyPhpFileContent = '<?php' . chr(0x0A) . '?>';
file_put_contents($filePath, $emptyPhpFileContent);
}
$configuration = file_get_contents($filePath);
if ($configuration === FALSE) {
return;
}
$phpEndTagPosition = strrpos($configuration, '?>');
if ($phpEndTagPosition) {
$configuration = substr($configuration, 0, $phpEndTagPosition);
} else {
$configuration .= chr(0x0A);
}
$configuration .= chr(0x09) . $lineToAdd;
if ($phpEndTagPosition) {
$configuration .= chr(0x0A) . '?>';
}
file_put_contents($filePath, $configuration);
}
/**
* Compares two strings ignoring \r character and seeing \n as &lt;br /&gt; in first string.
*
* @param array|string $string1
* @param string $string2
* @return bool
*/
public static function strCmpIgnoreCR($string1, $string2) {
return is_string($string1) && str_replace("\r", '', $string1) === $string2;
}
/**
* Changes meta tag '@attributes' to 'attributes', if it exists.
*
* @param array $origMeta
*/
public static function fixMetaAttributes(array &$origMeta) {
if (isset($origMeta['@attributes'])) {
$origMeta['attributes'] = $origMeta['@attributes'];
unset($origMeta['@attributes']);
}
}
}

View File

@@ -0,0 +1,186 @@
<?php
namespace SGalinski\Lfeditor\Utility;
/***************************************************************
* 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\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\VersionNumberUtility;
/**
* includes special typo3 methods
*/
class Typo3Lib {
/**
* @deprecated since version 5, will be removed as soon as TYPO3 8 support is dropped
*/
public const PATH_LOCAL_EXT = 'typo3conf/ext/';
/**
* @deprecated since version 5, will be removed as soon as TYPO3 8 support is dropped
*/
public const PATH_GLOBAL_EXT = 'typo3/ext/';
/**
* @deprecated since version 5, will be removed as soon as TYPO3 8 support is dropped
*/
public const PATH_SYS_EXT = 'typo3/sysext/';
/**
* @deprecated since version 5, will be removed as soon as TYPO3 8 support is dropped
*/
public const PATH_L10N = 'typo3conf/l10n/';
/**
* checks the file location type
*
* @param string $file
* @return string
*/
public static function checkFileLocation($file) {
$pathExtensions = Environment::getExtensionsPath() . '/';
if (strpos($file, $pathExtensions) !== FALSE) {
return 'local';
}
if (strpos($file, self::PATH_GLOBAL_EXT) !== FALSE) {
trigger_error(
'The typo3/ext folder does not exist anymore, so this functionality will be dropped',
E_USER_DEPRECATED
);
return 'global';
}
$pathSysExtensions = Environment::getFrameworkBasePath() . '/';
if (strpos($file, $pathSysExtensions) !== FALSE) {
return 'system';
}
$pathL10N = Environment::getLabelsPath() . '/';
if (strpos($file, $pathL10N) !== FALSE) {
return 'l10n';
}
return '';
}
/**
* @param string $fileRef Absolute path of the language file
* @return string Absolute prefix to the language file location
*/
public static function getLocalizedFilePrefix($fileRef) {
// Analyze file reference
if (GeneralUtility::isFirstPartOfStr($fileRef, Environment::getFrameworkBasePath() . '/')) {
// Is system
$validatedPrefix = Environment::getFrameworkBasePath() . '/';
} elseif (GeneralUtility::isFirstPartOfStr($fileRef, Environment::getBackendPath() . '/ext/')) {
// Is global
$validatedPrefix = Environment::getBackendPath() . '/ext/';
} elseif (GeneralUtility::isFirstPartOfStr($fileRef, Environment::getExtensionsPath() . '/')) {
// Is local
$validatedPrefix = Environment::getExtensionsPath() . '/';
} else {
$validatedPrefix = '';
}
return $validatedPrefix;
}
/**
* @return string Absolute path to the l10n directory
*/
public static function getLabelsPath() {
return Environment::getLabelsPath() . '/';
}
/**
* converts an absolute or relative typo3 style (EXT:) file path
*
* @param string $file absolute file or an typo3 relative file (EXT:)
* @param boolean $absolute generate to relative(false) or absolute file
* @return string converted file path
*
* @throws \Exception Conversion of file path failed
*/
public static function transTypo3File($file, $absolute) {
$path = GeneralUtility::getFileAbsFileName($file);
if (!$absolute) {
$fileLocation = self::checkFileLocation($path);
if ($fileLocation === 'local') {
$pathToRemove = Environment::getExtensionsPath() . '/';
} elseif ($fileLocation === 'system') {
$pathToRemove = Environment::getFrameworkBasePath() . '/';
} else {
throw new \Exception('Can not convert absolute file "' . $file . '"');
}
$path = 'EXT:' . SgLib::trimPath($pathToRemove, $path);
}
return $path;
}
/**
* converts an absolute or relative typo3 style (EXT:) file path
*
* @param string $file absolute file or an typo3 relative file (EXT:)
* @return array converted file path
*
* @throws \Exception Conversion of file path failed
*/
public static function getExtNameFromOverrideFile($file) {
$absoluteWebPath = Environment::getPublicPath();
$prefixedWithSettingsPath = str_replace($absoluteWebPath, '', $file);
$alreadyExtPrefixedPath = str_replace('/typo3conf/LFEditor/OverrideFiles/', '', $prefixedWithSettingsPath);
$extName = explode('/', $alreadyExtPrefixedPath)[0];
return [$extName, 'EXT:' . $alreadyExtPrefixedPath];
}
/**
* generates portable file paths
*
* @param string $file file
* @return string fixed file
*/
public static function fixFilePath($file) {
return GeneralUtility::fixWindowsFilePath(str_replace('//', '/', $file));
}
/**
* Adds configuration line to AdditionalConfiguration file.
*
* @param string $configLine line to be added.
* @param string $additionalConfigurationFilePath
*
* @return void
*/
public static function writeLineToAdditionalConfiguration($configLine, $additionalConfigurationFilePath) {
SgLib::appendToPHPFile($additionalConfigurationFilePath, $configLine);
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers;
/***************************************************************
* 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\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\ViewHelpers\Be\AbstractBackendViewHelper;
/**
* Abstract view helper
*/
class AbstractViewHelper extends AbstractBackendViewHelper {
/**
* Returns an instance of the page renderer
*
* @return PageRenderer
*/
public function getPageRenderer() {
return GeneralUtility::makeInstance(PageRenderer::class);
}
/**
* Returns the base url of the site
*
* Note: Works only in frontend mode
*
* @return string
*/
public function getBaseUrl() {
if ($GLOBALS['TSFE']->absRefPrefix !== '') {
$baseUrl = $GLOBALS['TSFE']->absRefPrefix;
} else {
$baseUrl = $GLOBALS['TSFE']->baseUrl;
}
return $baseUrl;
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers;
/***************************************************************
* 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!
***************************************************************/
/**
* View helper to add custom css files
*
* Example:
* {namespace lfe=SGalinski\Lfeditor\ViewHelpers}
* <lfe:addCssFile cssFile="{f:uri.resource(path: 'StyleSheets/Frontend.css')}" />
*/
class AddCssFileViewHelper extends AbstractViewHelper {
/**
* Register the ViewHelper arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('cssFile', 'string', 'The css file to include', TRUE);
}
/**
* Adds a custom css file
*
* @return void
*/
public function render() {
$cssFile = (\TYPO3\CMS\Core\Http\ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() ? $this->getBaseUrl() : '') . $this->arguments['cssFile'];
$this->getPageRenderer()->addCssFile($cssFile, 'stylesheet', 'all', '', FALSE);
}
}

View File

@@ -0,0 +1,54 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers;
/***************************************************************
* 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!
***************************************************************/
/**
* View helper to add custom javascript files
*
* Example:
* {namespace lfe=SGalinski\Lfeditor\ViewHelpers}
* <lfe:addJavaScriptFile javaScriptFile="{f:uri.resource(path: 'Scripts/Frontend.js')}" />
*/
class AddJavaScriptFileViewHelper extends AbstractViewHelper {
/**
* Register the ViewHelper arguments
*/
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('javaScriptFile', 'string', 'The JavaScript file to include', TRUE);
}
/**
* Adds a custom javascript file
*
* @return void
*/
public function render() {
$javaScriptFile = (\TYPO3\CMS\Core\Http\ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend() ? $this->getBaseUrl() : '') . $this->arguments['javaScriptFile'];
$this->getPageRenderer()->addJsFile($javaScriptFile);
}
}

View File

@@ -0,0 +1,55 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers\Be\Menus;
/***************************************************************
* 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 TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* Class ActionMenuOptionGroupViewHelper
*/
class ActionMenuOptionGroupViewHelper extends AbstractTagBasedViewHelper {
/**
* Initialize
*
* @return void
*/
public function initializeArguments() {
parent::initializeArguments();
$this->tagName = 'optgroup';
$this->registerUniversalTagAttributes();
$this->registerTagAttribute('label', 'string', 'Specifies a label for an option-group');
$this->registerTagAttribute('disabled', 'string', 'Specifies that an option-group should be disabled');
}
/**
* @return string
*/
public function render() {
$this->tag->setContent($this->renderChildren());
return $this->tag->render();
}
}

View File

@@ -0,0 +1,121 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers\Be\Menus;
/**
*
* 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 TYPO3Fluid\Fluid\Core\Compiler\TemplateCompiler;
use TYPO3Fluid\Fluid\Core\Parser\SyntaxTree\ViewHelperNode;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
* View helper which returns a select box, that can be used to switch between
* multiple actions and controllers and looks similar to TYPO3s funcMenu.
* Note: This view helper is experimental!
*
* = Examples =
*
* <code title="Simple">
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="Overview" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="Create new Blog" controller="Blog" action="new" />
* <f:be.menus.actionMenuItem label="List Posts" controller="Post" action="index" arguments="{blog: blog}" />
* </f:be.menus.actionMenu>
* </code>
* <output>
* Selectbox with the options "Overview", "Create new Blog" and "List Posts"
* </output>
*
* <code title="Localized">
* <f:be.menus.actionMenu>
* <f:be.menus.actionMenuItem label="{f:translate(key:'overview')}" controller="Blog" action="index" />
* <f:be.menus.actionMenuItem label="{f:translate(key:'create_blog')}" controller="Blog" action="new" />
* </f:be.menus.actionMenu>
* </code>
* <output>
* localized selectbox
* <output>
*/
class ActionMenuViewHelper extends AbstractTagBasedViewHelper {
/**
* @var string
*/
protected $tagName = 'select';
/**
* An array of \TYPO3\CMS\Fluid\Core\Parser\SyntaxTree\AbstractNode
*
* @var array
*/
protected $childNodes = [];
/**
* Initialize arguments.
*
* @return void
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments(): void {
parent::initializeArguments();
$this->registerArgument('defaultController', 'string', 'defaultController');
}
/**
* Render FunctionMenu
*
* @return string
*/
public function render(): string {
$this->tag->addAttribute('onchange', 'jumpToUrl(this.options[this.selectedIndex].value, this);');
$options = '';
foreach ($this->childNodes as $childNode) {
$options .= $childNode->evaluate($this->renderingContext);
}
$this->tag->setContent($options);
return $this->tag->render();
}
/**
* @param string $argumentsName
* @param string $closureName
* @param string $initializationPhpCode
* @param ViewHelperNode $node
* @param TemplateCompiler $compiler
* @return string
*/
public function compile(
$argumentsName,
$closureName,
&$initializationPhpCode,
ViewHelperNode $node,
TemplateCompiler $compiler
) {
// @TODO: replace with a true compiling method to make compilable!
$compiler->disable();
/** @noinspection PhpUnreachableStatementInspection */
return '';
}
}

View File

@@ -0,0 +1,90 @@
<?php
namespace SGalinski\Lfeditor\ViewHelpers;
/***************************************************************
* Copyright notice
*
* (c) sgalinski Internet Services (https://www.sgalinski.de)
*
* All rights reserved
*
* This script is part of the AY project. The AY 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\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* View helper to render language labels to
* json array to be used in js applications.
*
* Renders to AY.lang.'extension name' object
*
* Example:
* {namespace rs=SGalinski\RsEvents\ViewHelpers}
* <rs:inlineLanguageLabels labels="label01,label02" />
*/
class InlineLanguageLabelsViewHelper extends AbstractViewHelper {
public function initializeArguments() {
parent::initializeArguments();
$this->registerArgument('labels', 'mixed', 'Comma separated list of label keys to include', FALSE, []);
$this->registerArgument('htmlEscape', 'bool', 'Escape the output', FALSE, FALSE);
}
/**
* Renders the required javascript to make the language labels available
*
* @return string
*/
public function render(): string {
$labels = $this->arguments['labels'];
$htmlEscape = $this->arguments['htmlEscape'];
$controllerContext = $this->renderingContext->getControllerContext();
$extensionName = $controllerContext->getRequest()->getControllerExtensionName();
if (\is_string($labels)) {
$labels = GeneralUtility::trimExplode(',', $labels, TRUE);
}
if (!\is_array($labels)) {
throw new \InvalidArgumentException(
'The argument "labels" was registered with type "array|string", but is of type "' .
\gettype($labels) . '" in view helper "' . \get_class($this) . '".',
1256475113
);
}
$languageArray = [];
foreach ($labels as $key) {
$value = LocalizationUtility::translate($key, $extensionName);
$languageArray[$key] = ($htmlEscape ? \htmlentities($value) : $value);
}
$javascriptCode = '
var AY = AY || {};
AY.lang = AY.lang || {};
AY.lang.' . $extensionName . ' = AY.lang.' . $extensionName . ' || {};
var languageLabels = ' . \json_encode($languageArray) . ';
for (label in languageLabels) {
AY.lang.' . $extensionName . '[label] = languageLabels[label];
}
';
$this->getPageRenderer()->addJsInlineCode($extensionName, $javascriptCode);
return '';
}
}