Commit b00cd210 authored by Stefan Galinski's avatar Stefan Galinski 🎮
Browse files

Merge branch 'release_4.1.0' into 'master'

Release 4.1.0

See merge request !9
parents 534096b7 1e9760db
<?php
namespace SGalinski\SgCookieOptin\Command;
/***************************************************************
* 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\SgCookieOptin\Service\StaticFileGenerationService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Exception\RuntimeException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\Output;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Core\Bootstrap;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class GenerateStaticFilesCommand extends Command {
/**
* Configure the command by defining the name, options and arguments
*/
protected function configure()
{
$this->setHelp('Generates the necessary JavaScript, JSON and CSS files.' . LF . 'If you want to get more detailed information, use the --verbose option.');
$this->setDescription('Generates the necessary JavaScript, JSON and CSS files.')
->addArgument(
'siteRootId',
InputArgument::REQUIRED,
'The site root ID'
);
}
/**
* Executes the command for showing sys_log entries
*
* @param InputInterface $input
* @param OutputInterface $output
* @return int error code
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
try {
$this->io = new SymfonyStyle($input, $output);
$this->io->title($this->getDescription());
Bootstrap::initializeBackendAuthentication();
$siteRootId = (int) $input->getArgument('siteRootId');
$originalRecord = $this->getOriginalRecord($siteRootId);
$service = GeneralUtility::makeInstance(StaticFileGenerationService::class);
$service->generateFiles($siteRootId, $originalRecord);
} catch (\Exception $exception) {
$this->io->writeln('Error!');
$this->io->writeln($exception->getMessage());
return Command::FAILURE;
}
$this->io->writeln('Your files have been generated successfully');
return Command::SUCCESS;
}
/**
* Fetches the original record
*
* @param int $siteRootId
* @return array
*/
protected function getOriginalRecord(int $siteRootId): array {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable(
StaticFileGenerationService::TABLE_NAME
);
$uid = $queryBuilder->select('uid')
->from(StaticFileGenerationService::TABLE_NAME)
->where($queryBuilder->expr()->eq('pid', $siteRootId))
->andWhere($queryBuilder->expr()->eq('l10n_parent', 0))
->execute()
->fetchOne();
if ($uid === FALSE) {
throw new RuntimeException('Unable to generate files. There is no configuration for this site root. #' . $siteRootId);
}
$originalRecord = BackendUtility::getRecord(StaticFileGenerationService::TABLE_NAME, $uid);
if (isset($originalRecord['l10n_parent']) && (int) $originalRecord['l10n_parent'] > 0) {
$originalRecord = BackendUtility::getRecord(StaticFileGenerationService::TABLE_NAME, (int) $originalRecord['l10n_parent']);
}
return $originalRecord;
}
}
......@@ -32,6 +32,7 @@ use SGalinski\SgCookieOptin\Service\JsonImportService;
use SGalinski\SgCookieOptin\Service\LanguageService;
use SGalinski\SgCookieOptin\Service\DemoModeService;
use SGalinski\SgCookieOptin\Service\MinificationService;
use SGalinski\SgCookieOptin\Service\StaticFileGenerationService;
use SGalinski\SgCookieOptin\Service\TemplateService;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Core\Environment;
......@@ -55,20 +56,7 @@ use TYPO3\CMS\Frontend\Page\PageRepository;
* Adds the Cookie Optin JavaScript if it's generated for the current page.
*/
class GenerateFilesAfterTcaSave {
const TABLE_NAME = 'tx_sgcookieoptin_domain_model_optin';
const FOLDER_SITEROOT = 'siteroot-#PID#/';
const TEMPLATE_JAVA_SCRIPT_PATH = 'typo3conf/ext/sg_cookie_optin/Resources/Public/JavaScript/';
const TEMPLATE_JAVA_SCRIPT_NAME = 'cookieOptin.js';
const TEMPLATE_JSON_NAME = 'cookieOptinData--#LANG#.json';
const TEMPLATE_STYLE_SHEET_PATH = 'typo3conf/ext/sg_cookie_optin/Resources/Public/StyleSheets/';
const TEMPLATE_STYLE_SHEET_NAME = 'cookieOptin.css';
/** @var int */
protected $siteRoot = NULL;
/**
* Generates the files out of the TCA data.
......@@ -79,11 +67,12 @@ class GenerateFilesAfterTcaSave {
* @throws \TYPO3\CMS\Core\Error\Http\PageNotFoundException
* @throws \TYPO3\CMS\Core\Error\Http\ServiceUnavailableException
* @throws \TYPO3\CMS\Core\Http\ImmediateResponseException
* @throws \TYPO3\CMS\Core\Exception\SiteNotFoundException
*/
public function processDatamap_afterAllOperations(DataHandler $dataHandler) {
$this->handleFlashMessage($dataHandler);
if (!isset($dataHandler->datamap[self::TABLE_NAME])) {
if (!isset($dataHandler->datamap[StaticFileGenerationService::TABLE_NAME])) {
return;
}
......@@ -94,7 +83,7 @@ class GenerateFilesAfterTcaSave {
}
$originalRecord = [];
foreach ($dataHandler->datamap[self::TABLE_NAME] as $uid => $data) {
foreach ($dataHandler->datamap[StaticFileGenerationService::TABLE_NAME] as $uid => $data) {
if (count($originalRecord) > 0) {
break;
}
......@@ -112,151 +101,19 @@ class GenerateFilesAfterTcaSave {
continue;
}
$originalRecord = BackendUtility::getRecord(self::TABLE_NAME, $uid);
$originalRecord = BackendUtility::getRecord(StaticFileGenerationService::TABLE_NAME, $uid);
if (isset($originalRecord['l10n_parent']) && (int) $originalRecord['l10n_parent'] > 0) {
$originalRecord = BackendUtility::getRecord(self::TABLE_NAME, (int) $originalRecord['l10n_parent']);
$originalRecord = BackendUtility::getRecord(StaticFileGenerationService::TABLE_NAME, (int) $originalRecord['l10n_parent']);
}
}
$this->siteRoot = (int) $dataHandler->getPID(self::TABLE_NAME, $originalRecord['uid']);
$this->siteRoot = (int) $dataHandler->getPID(StaticFileGenerationService::TABLE_NAME, $originalRecord['uid']);
if ($this->siteRoot <= 0) {
return;
}
$folder = ExtensionSettingsService::getSetting(ExtensionSettingsService::SETTING_FOLDER);
if (!$folder) {
return;
}
$folderName = str_replace('#PID#', $this->siteRoot, $folder . self::FOLDER_SITEROOT);
$sitePath = defined('PATH_site') ? PATH_site : Environment::getPublicPath() . '/';
// First remove the folder with all files and then create it again. So no data artifacts are kept.
GeneralUtility::rmdir($sitePath . $folderName, TRUE);
GeneralUtility::mkdir_deep($sitePath . $folderName);
GeneralUtility::fixPermissions($sitePath . $folder, TRUE);
$currentVersion = VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version);
if ($currentVersion < 9000000) {
/** @var TypoScriptFrontendController $typoScriptFrontendController */
$originalTSFE = $typoScriptFrontendController = $GLOBALS['TSFE'];
if (!($typoScriptFrontendController instanceof TypoScriptFrontendController)) {
$typoScriptFrontendController = $GLOBALS['TSFE'] = new TypoScriptFrontendController(
$GLOBALS['TYPO3_CONF_VARS'], $this->siteRoot, 0
);
}
// required in order to generate the menu links later on
if (!is_object($GLOBALS['TT'])) {
$GLOBALS['TT'] = new NullTimeTracker();
}
if ($currentVersion < 8000000) {
// prevents a possible crash
$typoScriptFrontendController->getPageRenderer()->setBackPath('');
}
$typoScriptFrontendController->initFEuser();
$typoScriptFrontendController->initUserGroups();
$typoScriptFrontendController->fetch_the_id();
$typoScriptFrontendController->getPageAndRootline();
$typoScriptFrontendController->initTemplate();
$typoScriptFrontendController->no_cache = TRUE;
if ($currentVersion < 8000000) {
// prevents a possible crash, where the whole backend is loaded within the same frame and the files
// aren't generated.
$typoScriptFrontendController->getConfigArray();
}
$typoScriptFrontendController->settingLanguage();
$typoScriptFrontendController->settingLocale();
$typoScriptFrontendController->convPOSTCharset();
$typoScriptFrontendController->absRefPrefix = '/';
PageGenerator::pagegenInit();
$typoScriptFrontendController->newCObj();
}
$fullData = $this->getFullData($originalRecord, self::TABLE_NAME);
$minifyFiles = (bool) $fullData['minify_generated_data'];
$cssData = [
'color_box' => $fullData['color_box'],
'color_headline' => $fullData['color_headline'],
'color_text' => $fullData['color_text'],
'color_confirmation_background' => $fullData['color_confirmation_background'],
'color_confirmation_text' => $fullData['color_confirmation_text'],
'color_checkbox' => $fullData['color_checkbox'],
'color_checkbox_required' => $fullData['color_checkbox_required'],
'color_button_all' => $fullData['color_button_all'],
'color_button_all_hover' => $fullData['color_button_all_hover'],
'color_button_all_text' => $fullData['color_button_all_text'],
'color_button_specific' => $fullData['color_button_specific'],
'color_button_specific_hover' => $fullData['color_button_specific_hover'],
'color_button_specific_text' => $fullData['color_button_specific_text'],
'color_button_essential' => $fullData['color_button_essential'],
'color_button_essential_hover' => $fullData['color_button_essential_hover'],
'color_button_essential_text' => $fullData['color_button_essential_text'],
'color_button_close' => $fullData['color_button_close'],
'color_button_close_hover' => $fullData['color_button_close_hover'],
'color_button_close_text' => $fullData['color_button_close_text'],
'color_list' => $fullData['color_list'],
'color_list_text' => $fullData['color_list_text'],
'color_table' => $fullData['color_table'],
'color_Table_data_text' => $fullData['color_Table_data_text'],
'color_table_header' => $fullData['color_table_header'],
'color_table_header_text' => $fullData['color_table_header_text'],
'color_full_box' => $fullData['color_full_box'],
'color_full_headline' => $fullData['color_full_headline'],
'color_full_text' => $fullData['color_full_text'],
'color_full_button_close' => $fullData['color_full_button_close'],
'color_full_button_close_hover' => $fullData['color_full_button_close_hover'],
'color_full_button_close_text' => $fullData['color_full_button_close_text'],
'iframe_color_consent_box_background' => $fullData['iframe_color_consent_box_background'],
'iframe_color_button_load_one' => $fullData['iframe_color_button_load_one'],
'iframe_color_button_load_one_hover' => $fullData['iframe_color_button_load_one_hover'],
'iframe_color_button_load_one_text' => $fullData['iframe_color_button_load_one_text'],
'iframe_color_open_settings' => $fullData['iframe_color_open_settings'],
'banner_color_box' => $fullData['banner_color_box'],
'banner_color_text' => $fullData['banner_color_text'],
'banner_color_link_text' => $fullData['banner_color_link_text'],
'banner_color_button_settings' => $fullData['banner_color_button_settings'],
'banner_color_button_settings_hover' => $fullData['banner_color_button_settings_hover'],
'banner_color_button_settings_text' => $fullData['banner_color_button_settings_text'],
'banner_color_button_accept' => $fullData['banner_color_button_accept'],
'banner_color_button_accept_hover' => $fullData['banner_color_button_accept_hover'],
'banner_color_button_accept_text' => $fullData['banner_color_button_accept_text'],
];
$this->createCSSFile($fullData, $folderName, $cssData, $minifyFiles);
$languages = LanguageService::getLanguages($this->siteRoot);
foreach ($languages as $language) {
$languageUid = (int) $language['uid'];
if ($languageUid < 0) {
continue;
}
$locale = isset($language['locale']) ? $language['locale'] : '';
$translatedRecord = $originalRecord;
if ($languageUid > 0) {
$pageRepository = GeneralUtility::makeInstance(PageRepository::class);
$translatedRecord = $pageRepository->getRecordOverlay(self::TABLE_NAME, $originalRecord, $languageUid);
}
$translatedFullData = $this->getFullData($translatedRecord, self::TABLE_NAME, $languageUid);
if (count($translatedFullData) <= 0) {
continue;
}
$this->createJavaScriptFile($folderName, $minifyFiles);
$this->createJsonFile(
$folderName, $fullData, $translatedFullData, $cssData, $minifyFiles, $languageUid, $locale
);
}
GeneralUtility::fixPermissions($sitePath . $folder, TRUE);
// reset the TSFE to it's previous state to not influence remaining code
$GLOBALS['TSFE'] = $originalTSFE;
$service = GeneralUtility::makeInstance(StaticFileGenerationService::class);
$service->generateFiles($this->siteRoot, $originalRecord);
}
/**
......@@ -265,721 +122,9 @@ class GenerateFilesAfterTcaSave {
* @param DataHandler $dataHandler
*/
protected function handleFlashMessage(DataHandler $dataHandler) {
if (isset($dataHandler->cmdmap[self::TABLE_NAME]) || isset($dataHandler->datamap[self::TABLE_NAME])) {
if (isset($dataHandler->cmdmap[StaticFileGenerationService::TABLE_NAME]) || isset($dataHandler->datamap[StaticFileGenerationService::TABLE_NAME])) {
session_start();
$_SESSION['tx_sgcookieoptin']['configurationChanged'] = TRUE;
}
}
/**
* Returns the full data for the given data array.
*
* @param array $data
* @param string $table
* @param int $language
*
* @return array
*/
protected function getFullData(array $data, $table, $language = 0) {
$fullData = [];
$parentUid = (!empty($data['l10n_parent']) ? (int) $data['l10n_parent'] : (int) $data['uid']);
foreach ($data as $fieldName => $value) {
$tcaConfig = $this->getTCAConfigForInlineField($table, $fieldName);
if (count($tcaConfig) <= 0) {
$fullData[$fieldName] = $value;
continue;
}
$tcaConfig = $this->getTCAConfigForInlineField($table, $fieldName);
$foreignTable = $tcaConfig['foreign_table'];
$foreignField = $tcaConfig['foreign_field'];
if (empty($foreignTable) || empty($foreignField)) {
$fullData[$fieldName] = [];
continue;
}
$fullData[$fieldName] = [];
$inlineData = $this->getDataForInlineField($foreignTable, $foreignField, $parentUid, $language);
if (\count($inlineData) > 0) {
foreach ($inlineData as $index => $inlineDataEntry) {
if (!isset($inlineDataEntry['uid'])) {
continue;
}
$fullData[$fieldName][$index] = $this->getFullData($inlineDataEntry, $foreignTable, $language);
}
}
}
return $fullData;
}
/**
* Returns the table of the given inline field from the given table.
*
* @param string $table
* @param string $field
*
* @return array
*/
protected function getTCAConfigForInlineField($table, $field) {
$tableData = $GLOBALS['TCA'][$table];
if (!is_array($tableData)) {
return [];
}
$tableColumn = $tableData['columns'][$field];
if (!is_array($tableColumn)) {
return [];
}
if (!isset($tableColumn['config'])) {
return [];
}
if ($tableColumn['config']['type'] !== 'inline') {
return [];
}
return $tableColumn['config'];
}
/**
* Returns the data for the given field, table configuration.
*
* @param string $table
* @param string $field
* @param int $parentUid
* @param int $language
*
* @return array
*/
protected function getDataForInlineField($table, $field, $parentUid, $language = 0) {
$languageField = $this->getTCALanguageField($table);
if (VersionNumberUtility::convertVersionNumberToInteger(TYPO3_version) <= 9000000) {
/** @var DatabaseConnection $database */
$database = $GLOBALS['TYPO3_DB'];
$rows = $database->exec_SELECTgetRows(
'*', $table, 'deleted=0 AND hidden=0 AND ' . $field . '=' . $parentUid .
($languageField ? ' AND ' . $languageField . '=0' : ''), '', 'sorting ASC'
);
} else {
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$queryBuilder = $connectionPool->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(HiddenRestriction::class))
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder->select('*')
->from($table)
->orderBy('sorting', 'ASC')
->where(
$queryBuilder->expr()->eq(
$field,
$parentUid
)
);
if ($languageField) {
$queryBuilder->andWhere(
$queryBuilder->expr()->eq(
$languageField,
0
)
);
}
$rows = $queryBuilder->execute()->fetchAll();
}
if (!is_array($rows)) {
return [];
}
$translatedRows = [];
$pageRepository = GeneralUtility::makeInstance(PageRepository::class);
foreach ($rows as $row) {
$translatedRows[] = $pageRepository->getRecordOverlay($table, $row, $language);
}
return $translatedRows;
}
/**
* Returns the language field of the given table.
*
* @param string $table
*
* @return string
*/
protected function getTCALanguageField($table) {
$tableData = $GLOBALS['TCA'][$table];
if (!is_array($tableData)) {
return '';
}
if (!isset($tableData['ctrl'])) {
return '';
}
return (isset($tableData['ctrl']['languageField']) ? $tableData['ctrl']['languageField'] : '');
}
/**
* Creates a CSS file out of the given data array.
*
* @param array $data
* @param string $folder
* @param array $cssData
* @param boolean $minifyFile
*
* @return void
*/
protected function createCSSFile(array $data, $folder, array $cssData, $minifyFile = TRUE) {
$sitePath = defined('PATH_site') ? PATH_site : Environment::getPublicPath() . '/';
$content = '/* Base styles: ' . self::TEMPLATE_STYLE_SHEET_NAME . " */\n\n" .
file_get_contents($sitePath . self::TEMPLATE_STYLE_SHEET_PATH . self::TEMPLATE_STYLE_SHEET_NAME);
$templateService = GeneralUtility::makeInstance(TemplateService::class);
$content .= " \n\n" . $templateService->getCSSContent(
TemplateService::TYPE_TEMPLATE, $data['template_selection']
);
if ((boolean) $data['banner_enable']) {
$content .= " \n\n" . $templateService->getCSSContent(
TemplateService::TYPE_BANNER, $data['banner_selection']
);
}
if ((boolean) $data['iframe_enabled']) {
$content .= " \n\n" . $templateService->getCSSContent(
TemplateService::TYPE_IFRAME, $data['iframe_selection']
);
$content .= " \n\n" . $templateService->getCSSContent(
TemplateService::TYPE_IFRAME_REPLACEMENT, $data['iframe_replacement_selection']
);
}
$keys = $data = [];
foreach ($cssData as $key => $value) {
$keys[] = '%23###' . $key . '###';
$data[] = '%23' . ltrim($value, '#');
$keys[] = '###' . $key . '###';
$data[] = $value;
}
$file = $sitePath . $folder . self::TEMPLATE_STYLE_SHEET_NAME;
file_put_contents($file, str_replace($keys, $data, $content));
if ($minifyFile) {
$minificationService = GeneralUtility::makeInstance(MinificationService::class);
$minificationService->minifyCSSFile($file);
}
GeneralUtility::fixPermissions($file);
}
/**
* Creates a html string out of the given scripts.
*
* @param array $scripts
*
* @return string
*/
protected function getActivationHTML(array $scripts) {
$content = '';
foreach ($scripts as $script) {
$htmlContent = trim($script['html']);
if (!$htmlContent) {
continue;
}
$content .= $htmlContent . "\n\n";
}
return $content;
}
/**
* Creates a javascript file out of the given scripts array.
*
* @param string $folder
* @param string $groupName
* @param array $scripts
* @param int $languageUid
* @param bool $minifyFile
*
* @return string
*/
protected function createActivationScriptFile(
$folder, $groupName, array $scripts, $languageUid = 0, $minifyFile = TRUE
) {
$content = '';
foreach ($scripts as $script) {
$scriptContent = trim($script['script']);
if (!$scriptContent) {
continue;
}
$content .= '// Script: ' . $script['title'] . "\n\n" . $scriptContent . "\n\n";
}
if ($content === '') {
return '';
}
$file = $folder . $groupName . '-' . $languageUid . '.js';
$sitePath = defined('PATH_site') ? PATH_site : Environment::getPublicPath() . '/';
$groupFile = $sitePath . $file;
file_put_contents($groupFile, $content);
if ($minifyFile) {
$minificationService = GeneralUtility::makeInstance(MinificationService::class);
$minificationService->minifyJavaScriptFile($groupFile);