First commit

This commit is contained in:
Sebastian Fischer 2024-02-09 17:13:28 +01:00
commit 5319bd35a5
69 changed files with 4228 additions and 0 deletions

26
.gitlab-ci.yml Executable file
View File

@ -0,0 +1,26 @@
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Composer.gitlab-ci.yml
# Publishes a tag/branch to Composer Packages of the current project
publish:
image: curlimages/curl:latest
stage: build
rules:
- if: '$CI_COMMIT_REF_NAME == "develop" || $CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_TAG =~ /^\d+.\d+.\d+/'
variables:
URL: "$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN"
script:
- version=$([[ -z "$CI_COMMIT_TAG" ]] && echo "branch=$CI_COMMIT_REF_NAME" || echo "tag=$CI_COMMIT_TAG")
- insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
- response=$(curl -s -w "\n%{http_code}" $insecure --data $version $URL)
- code=$(echo "$response" | tail -n 1)
- body=$(echo "$response" | head -n 1)
# Output state information
- if [ $code -eq 201 ]; then
echo "Package created - Code $code - $body";
else
echo "Could not create package - Code $code - $body";
exit 1;
fi

View File

@ -0,0 +1,148 @@
<?php
namespace Evoweb\EwBase\Command;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\Table;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ContentElementCommand extends Command
{
protected ConnectionPool $connectionPool;
public function __construct(ConnectionPool $connectionPool)
{
$this->connectionPool = $connectionPool;
parent::__construct();
}
protected function configure()
{
$this
->setAliases(['kc-sitepackage'])
->addOption(
'pageId',
'-p',
InputOption::VALUE_REQUIRED,
'Page id to start query'
);
}
public function execute(InputInterface $input, OutputInterface $output): int
{
$pageIds = $input->getOption('pageId');
$pageIds = GeneralUtility::intExplode(',', $pageIds, true);
foreach ($pageIds as $pageId) {
$treeList = $this->getLocalTreeList($pageId, 999999, 0, '1=1');
$queryBuilder = $this->connectionPool->getQueryBuilderForTable('tt_content');
$queryBuilder
->getRestrictions()
->removeByType(HiddenRestriction::class)
->removeByType(StartTimeRestriction::class)
->removeByType(EndTimeRestriction::class);
$result = $queryBuilder
->select('CType', 'list_type')
->from('tt_content')
->where(
$queryBuilder->expr()->in('pid', explode(',', $treeList))
)
->groupBy('CType', 'list_type')
->executeQuery()
->fetchAllAssociative();
(new Table($output))
->setHeaderTitle('Content in tree ' . $pageId)
->setHeaders(array_keys($result[0]))
->setRows($result)
->render();
$output->writeln($treeList);
}
return 0;
}
/**
* Recursively fetch all descendants of a given page
*/
public function getLocalTreeList(int $id, int $depth, int $begin = 0, string $permClause = ''): string
{
if ($id < 0) {
$id = abs($id);
}
if ($begin == 0) {
$theList = (string)$id;
} else {
$theList = '';
}
if ($id && $depth > 0) {
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
$queryBuilder
->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$queryBuilder
->select('uid')
->from('pages')
->where(
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, Connection::PARAM_INT)),
$queryBuilder->expr()->eq('sys_language_uid', 0)
)
->orderBy('uid');
if ($permClause !== '') {
$queryBuilder->andWhere($this->stripLogicalOperatorPrefix($permClause));
}
$statement = $queryBuilder->executeQuery();
while ($row = $statement->fetchAssociative()) {
if ($begin <= 0) {
$theList .= ',' . $row['uid'];
}
if ($depth > 1) {
$theSubList = $this->getLocalTreeList($row['uid'], $depth - 1, $begin - 1, $permClause);
if (!empty($theList) && !empty($theSubList) && ($theSubList[0] !== ',')) {
$theList .= ',';
}
$theList .= $theSubList;
}
}
}
return $theList;
}
/**
* Removes the prefixes AND/OR from the input string.
*
* This function should be used when you can't guarantee that the string
* that you want to use as a WHERE fragment is not prefixed.
*
* @param string $constraint The where part fragment with a possible leading AND / OR operator
* @return string The modified where part without leading operator
*/
public function stripLogicalOperatorPrefix(string $constraint): string
{
return preg_replace('/^(?:(AND|OR)[[:space:]]*)+/i', '', trim($constraint)) ?: '';
}
}

View File

@ -0,0 +1,203 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Configuration;
/*
* This file is part of TYPO3 CMS-based extension "container" by b13.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*/
use Mfc\OAuth2\ResourceServer\GitLab;
use Mfc\OAuth2\ResourceServer\Registry;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class AdditionalConfiguration
{
protected string $extensionKey = 'ew_base';
protected array $oauthOptions = [
'enabled' => true, // Enable/Disable the provider
'arguments' => [
'appId' => '',
'appSecret' => '',
'projectName' => '',
'gitlabServer' => 'https://github.com',
// User level at which the user will be given admin permissions
'gitlabAdminUserLevel' => 30,
// Groups to assign to the User (comma separated list possible)
'gitlabDefaultGroups' => 1,
// UserConfig db and/or file mount from groups
'gitlabUserOption' => 3,
// Blocks users with flag external from access the backend
'blockExternalUser' => false,
],
];
protected array $developConfig = [
'BE' => [
'debug' => true,
'loginRateLimit' => 10000,
],
'FE' => [
'debug' => true,
'loginRateLimit' => 10000,
],
'GFX' => [
'processor' => 'GraphicsMagick',
'processor_colorspace' => 'RGB',
'processor_path' => '/usr/bin/',
'processor_path_lzw' => '/usr/bin/',
],
'SYS' => [
'devIPmask' => '*',
'displayErrors' => 1,
'systemLogLevel' => 0,
'exceptionalErrors' => 12290,
'errorHandlerErrors' => E_ALL & ~(
E_STRICT | E_NOTICE | E_WARNING | E_COMPILE_WARNING | E_COMPILE_ERROR
| E_CORE_WARNING | E_CORE_ERROR | E_PARSE | E_ERROR
),
],
];
/**
* This is needed to override all mail settings instead of merging them.
*
* @var array|string[]
*/
protected array $mailConfig = [
'transport' => 'smtp',
'transport_smtp_server' => '127.0.0.1:1025',
'defaultMailFromAddress' => 'test@dev.arpa',
];
public function initialize(array $configuration = []): void
{
$this->addContextToSitename();
$this->addContextConfiguration($configuration);
if (Environment::getContext() == 'Development') {
$this->addBaseUrl();
$this->addDebugConfiguration();
}
$this->addFurtherConfigurationFiles();
}
protected function addContextToSitename(): void
{
$GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] .= ' - ' . Environment::getContext();
}
/**
* Set the baseurl on local environments automatically
*/
protected function addBaseUrl(): void
{
if (Environment::isCli()) {
return;
}
$remoteHost = GeneralUtility::getIndpEnv('HTTP_HOST');
ExtensionManagementUtility::addTypoScript(
$this->extensionKey,
'constants',
'
// condition should trigger different cache hashes
[request.getNormalizedParams().getHttpHost() == \'' . $remoteHost . '\']
config.baseURL = ' . $remoteHost . '
[end]
',
'defaultContentRendering'
);
}
protected function addDebugConfiguration(): void
{
$GLOBALS['TYPO3_CONF_VARS'] = $this->arrayMergeRecursive(
$GLOBALS['TYPO3_CONF_VARS'],
$this->developConfig
);
$layoutRootPaths = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['layoutRootPaths'] ?: [];
$partialRootPaths = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['partialRootPaths'] ?: [];
$templateRootPaths = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['templateRootPaths'] ?: [];
$GLOBALS['TYPO3_CONF_VARS']['MAIL'] = $this->mailConfig;
if (!empty($layoutRootPaths)) {
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['layoutRootPaths'] = $layoutRootPaths;
}
if (!empty($partialRootPaths)) {
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['partialRootPaths'] = $partialRootPaths;
}
if (!empty($templateRootPaths)) {
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['templateRootPaths'] = $templateRootPaths;
}
}
protected function addContextConfiguration(array $configuration): void
{
$context = (string)Environment::getContext();
if (isset($configuration[$context]) && is_array($configuration[$context])) {
$GLOBALS['TYPO3_CONF_VARS'] = $this->arrayMergeRecursive(
$GLOBALS['TYPO3_CONF_VARS'],
$configuration[$context]
);
}
}
/**
* Loads additional configuration two levels up relative to instance
* or relative to ADDITIONAL_CONFIG_FILE configuration
*/
protected function addFurtherConfigurationFiles(): void
{
$paths = [
Environment::getPublicPath() . '/../../../AdditionalConfiguration.php',
Environment::getPublicPath() . '/' . ($_SERVER['ADDITIONAL_CONFIG_FILE'] ?? time() . '.php'),
];
foreach ($paths as $path) {
$path = realpath($path);
if ($path && @file_exists($path)) {
/** @noinspection */
require_once $path;
}
}
}
public function addGitlabLogin(array $options = []): void
{
if (!empty($options)) {
$this->oauthOptions['arguments']['gitlabAdminUserLevel'] = GitLab::USER_LEVEL_DEVELOPER;
Registry::addServer(
'gitlab',
'Login mit GitLab',
GitLab::class,
$this->arrayMergeRecursive($this->oauthOptions, $options)
);
}
}
protected function arrayMergeRecursive(array $array1, array $array2): array
{
$merged = $array1;
foreach ($array2 as $key => $value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = $this->arrayMergeRecursive($merged[$key], $value);
} elseif (is_numeric($key)) {
if (!in_array($value, $merged)) {
$merged[] = $value;
}
} else {
$merged[$key] = $value;
}
}
return $merged;
}
}

View File

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\DataProcessing;
/*
* This file is part of TYPO3 CMS-based extension "container" by b13.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*/
use B13\Container\DataProcessing\ContainerProcessor as BaseContainerProcessor;
use B13\Container\Domain\Model\Container;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\RecordsContentObject;
class ContainerProcessor extends BaseContainerProcessor
{
protected function processColPos(
ContentObjectRenderer $cObj,
Container $container,
int $colPos,
string $as,
array $processedData,
array $processorConfiguration
): array {
$children = $container->getChildrenByColPos($colPos);
if (!$processorConfiguration['doNotProcessChildren']) {
$contentRecordRenderer = new RecordsContentObject();
$conf = [
'tables' => 'tt_content'
];
foreach ($children as &$child) {
if ($child['l18n_parent'] > 0) {
$conf['source'] = $child['l18n_parent'];
} else {
$conf['source'] = $child['uid'];
}
if ($child['t3ver_oid'] > 0) {
$conf['source'] = $child['t3ver_oid'];
}
$child['renderedContent'] = $cObj->render($contentRecordRenderer, $conf);
/** @var ContentObjectRenderer $recordContentObjectRenderer */
$recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$recordContentObjectRenderer->start($child, 'tt_content');
$child = $this->contentDataProcessor->process($recordContentObjectRenderer, $processorConfiguration, $child);
}
}
$processedData[$as] = $children;
return $processedData;
}
}

View File

@ -0,0 +1,99 @@
<?php
namespace Evoweb\EwBase\EventListener;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Core\Page\Event\BeforeStylesheetsRenderingEvent;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
class CssMerger
{
public function __construct(protected ExtensionConfiguration $extensionConfiguration)
{
}
public function __invoke(BeforeStylesheetsRenderingEvent $event): void
{
if ($event->isInline() && $event->isPriority()) {
$assetCollector = $event->getAssetCollector();
$styles = [];
$styles = $this->getStylesheetFileContent($assetCollector, $styles);
$styles = $this->getStylesheetInlineContent($assetCollector, $styles);
$styles = array_unique($styles);
$styles = trim(implode(LF, $styles));
if (empty($styles)) {
return;
}
$inlineCssStyles = $this->extensionConfiguration->get('ew_base', 'inlineCssStyles');
if ($inlineCssStyles) {
$assetCollector->addInlineStyleSheet('ew_base', $styles, [], ['priority' => true]);
} else {
$temporaryFile = GeneralUtility::writeStyleSheetContentToTemporaryFile($styles);
$assetCollector->addStyleSheet('combined_styles', $temporaryFile);
}
}
}
protected function getStylesheetFileContent(AssetCollector $assetCollector, array $styles): array
{
$styleSheetsFiles = $assetCollector->getStyleSheets(true);
foreach ($styleSheetsFiles as $identifier => $asset) {
if (!str_contains($asset['source'], '//') && $content = $this->getFileContent($asset['source'])) {
$styles[] = $content;
$assetCollector->removeStyleSheet($identifier);
}
}
$styleSheetsFiles = $assetCollector->getStyleSheets(false);
foreach ($styleSheetsFiles as $identifier => $asset) {
if (!str_contains($asset['source'], '//') && $content = $this->getFileContent($asset['source'])) {
$styles[] = $content;
$assetCollector->removeStyleSheet($identifier);
}
}
return $styles;
}
protected function getStylesheetInlineContent(AssetCollector $assetCollector, array $styles): array
{
$styleSheetsInline = $assetCollector->getInlineStyleSheets(false);
foreach ($styleSheetsInline as $identifier => $asset) {
$styles[] = $asset['source'];
$assetCollector->removeInlineStyleSheet($identifier);
}
return $styles;
}
protected function getFileContent(string $filePath): string
{
$absoluteFilePath = GeneralUtility::getFileAbsFileName($filePath);
if (!$absoluteFilePath || !@file_exists($absoluteFilePath)) {
return '';
}
$content = (string)file_get_contents($absoluteFilePath);
$content = preg_replace('@# sourceMappingURL=.*.css.map@', '', $content);
$relativeFilePath = dirname(PathUtility::getAbsoluteWebPath($absoluteFilePath));
return str_replace(
[
'url(..',
'url("..',
'url(\'..',
],
[
'url(' . $relativeFilePath . '/..',
'url("' . $relativeFilePath . '/..',
'url(\'' . $relativeFilePath . '/..',
],
$content
);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Evoweb\EwBase\EventListener;
use TYPO3\CMS\Backend\ViEvoweb\Event\IsContentUsedOnPageLayoutEvent;
class IsContentUsedOnPageLayout
{
public function __invoke(IsContentUsedOnPageLayoutEvent $event): void
{
$event->setUsed($event->isRecordUsed() || $this->findCTypeBegin($event->getRecord()['CType']));
}
public function findCTypeBegin($cType): bool
{
$found = false;
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) {
if (strpos($cType, $search) !== false) {
$found = true;
break;
}
}
return $found;
}
}

View File

@ -0,0 +1,96 @@
<?php
namespace Evoweb\EwBase\EventListener;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Core\Page\Event\BeforeJavaScriptsRenderingEvent;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
class JsMerger
{
public function __invoke(BeforeJavaScriptsRenderingEvent $event): void
{
if ($event->isInline() && $event->isPriority()) {
$assetCollector = $event->getAssetCollector();
$scripts = [];
$scripts = $this->getJavaScriptFileContent($assetCollector, $scripts);
$scripts = $this->getJavaScriptInlineContent($assetCollector, $scripts);
$scripts = array_unique($scripts);
$scripts = trim(implode(LF, $scripts));
if (!empty($scripts)) {
$temporaryFile = GeneralUtility::writeJavaScriptContentToTemporaryFile($scripts);
$assetCollector->addJavaScript('combined_scripts', $temporaryFile);
}
}
}
protected function getJavaScriptFileContent(AssetCollector $assetCollector, array $scripts): array
{
$javaScriptFiles = $assetCollector->getJavaScripts(true);
foreach ($javaScriptFiles as $identifier => $asset) {
if ($asset['options']['useNonce'] ?? false) {
continue;
}
if (!str_contains($asset['source'], '//') && $content = $this->getFileContent($asset['source'])) {
$scripts[] = rtrim($content, ';') . ';';
$assetCollector->removeJavaScript($identifier);
}
}
$javaScriptFiles = $assetCollector->getJavaScripts(false);
foreach ($javaScriptFiles as $identifier => $asset) {
if ($asset['options']['useNonce'] ?? false) {
continue;
}
if (!str_contains($asset['source'], '//') && $content = $this->getFileContent($asset['source'])) {
$scripts[] = rtrim($content, ';') . ';';
$assetCollector->removeJavaScript($identifier);
}
}
return $scripts;
}
protected function getJavaScriptInlineContent(AssetCollector $assetCollector, array $scripts): array
{
$javaScriptInline = $assetCollector->getInlineJavaScripts(false);
foreach ($javaScriptInline as $identifier => $asset) {
if ($asset['options']['useNonce'] ?? false) {
continue;
}
$scripts[] = $asset['source'];
$assetCollector->removeInlineJavaScript($identifier);
}
return $scripts;
}
protected function getFileContent(string $filePath): string
{
$absoluteFilePath = GeneralUtility::getFileAbsFileName($filePath);
if (!$absoluteFilePath || !@file_exists($absoluteFilePath)) {
return '';
}
$content = (string)file_get_contents($absoluteFilePath);
$content = preg_replace('@# sourceMappingURL=.*.js.map@', '', $content);
$relativeFilePath = dirname(PathUtility::getAbsoluteWebPath($absoluteFilePath));
return str_replace(
[
'url(..',
'url("..',
'url(\'..',
],
[
'url(' . $relativeFilePath . '/..',
'url("' . $relativeFilePath . '/..',
'url(\'' . $relativeFilePath . '/..',
],
$content
);
}
}

View File

@ -0,0 +1,304 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Form\Element;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Backend\Form\Element\AbstractFormElement;
use TYPO3\CMS\Backend\Form\NodeFactory;
use TYPO3\CMS\Core\Imaging\ImageManipulation\Area;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3Fluid\Fluid\ViEvoweb\TemplateView;
use TYPO3Fluid\Fluid\ViEvoweb\ViewInterface;
class PickColorFromImage extends AbstractFormElement
{
/**
* Default element configuration
*
* @var array
*/
protected static array $defaultConfig = [
'imageField' => 'image',
'cropVariants' => [
'default' => [
'title' =>
'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.default',
'allowedAspectRatios' => [
'16:9' => [
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9',
'value' => 16 / 9,
],
'3:2' => [
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.3_2',
'value' => 3 / 2,
],
'4:3' => [
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3',
'value' => 4 / 3,
],
'1:1' => [
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1',
'value' => 1.0,
],
'NaN' => [
'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free',
'value' => 0.0,
],
],
'selectedRatio' => 'NaN',
'cropArea' => [
'x' => 0.0,
'y' => 0.0,
'width' => 1.0,
'height' => 1.0,
],
],
],
];
/**
* Default field information enabled for this element.
*
* @var array
*/
protected $defaultFieldInformation = [
'tcaDescription' => [
'renderType' => 'tcaDescription',
],
];
/**
* Default field wizards enabled for this element.
*
* @var array
*/
protected $defaultFieldWizard = [
'localizationStateSelector' => [
'renderType' => 'localizationStateSelector',
],
'otherLanguageContent' => [
'renderType' => 'otherLanguageContent',
'after' => [
'localizationStateSelector',
],
],
'defaultLanguageDifferences' => [
'renderType' => 'defaultLanguageDifferences',
'after' => [
'otherLanguageContent',
],
],
];
protected ViewInterface $templateView;
public function __construct(NodeFactory $nodeFactory, array $data)
{
parent::__construct($nodeFactory, $data);
// Would be great, if we could inject the view here, but since the constructor is in the interface, we can't
// @todo: It's unfortunate we're using Typo3Fluid TemplateView directly here. We can't
// inject BackendViewFactory here since __construct() is polluted by NodeInterface.
// Remove __construct() from NodeInterface to have DI, then use BackendViewFactory here.
$view = GeneralUtility::makeInstance(TemplateView::class);
$templatePaths = $view->getRenderingContext()->getTemplatePaths();
$templatePaths
->setTemplateRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Templates')]);
$templatePaths
->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:ew_base/Resources/Private/Partials')]);
$this->templateView = $view;
}
public function render(): array
{
$resultArray = $this->initializeResultArray();
$parameterArray = $this->data['parameterArray'];
$config = $this->populateConfiguration($parameterArray['fieldConf']['config']);
$fieldId = StringUtility::getUniqueId('formengine-color-');
$file = $this->getFileObject($this->data['databaseRow'], $config['imageField']);
if (!$file) {
$languageService = $this->getLanguageService();
$label = $languageService->sL(
'LLL:EXT:ew_base/Resources/Private/Language/locallang_db.xlf:imageField.empty'
);
$fieldLabel = $this->data['processedTca']['columns'][$config['imageField']]['label'];
$resultArray['html'] = '
<div class="form-control-wrap">
<div class="form-wizards-wrap">
<div class="form-wizards-element">
<typo3-formengine-element-pick-color recordFieldId="' . htmlspecialchars($fieldId) . '">
' . sprintf($label, '<strong>' . $fieldLabel . '</strong>') . '
</typo3-formengine-element-pick-color>
</div>
</div>
</div>
';
// Early return in case we do not find a file
return $resultArray;
}
$fieldInformationResult = $this->renderFieldInformation();
$fieldInformationHtml = $fieldInformationResult['html'];
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
$fieldControlResult = $this->renderFieldControl();
$fieldControlHtml = $fieldControlResult['html'];
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
$fieldWizardResult = $this->renderFieldWizard();
$fieldWizardHtml = $fieldWizardResult['html'];
$resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
$width = $this->formMaxWidth(
MathUtility::forceIntegerInRange(
$config['size'] ?? $this->defaultInputWidth,
$this->minimumInputWidth,
$this->maxInputWidth
)
);
$arguments = [
'fieldInformation' => $fieldInformationHtml,
'fieldControl' => $fieldControlHtml,
'fieldWizard' => $fieldWizardHtml,
'isAllowedFileExtension' => in_array(
strtolower($file->getExtension()),
GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])),
true
),
'image' => $file,
'formEngine' => [
'field' => [
'value' => $parameterArray['itemFormElValue'],
'name' => $parameterArray['itemFormElName'],
],
'validation' => '[]',
],
'config' => $config,
'width' => $width,
];
if ($arguments['isAllowedFileExtension']) {
$resultArray['stylesheetFiles'][] =
'EXT:ew_base/Resources/Public/JavaScript/form-engine/element/pick-color-from-image.css';
$resultArray['javaScriptModules'][] = JavaScriptModuleInstruction::create(
'@evoweb/ew-base/form-engine/element/pick-color-from-image.js',
'PickColorFromImage'
)->instance(
$fieldId,
$parameterArray['itemFormElValue'],
($parameterArray['fieldConf']['config']['readOnly'] ?? false)
);
$arguments['formEngine']['field']['id'] = $fieldId;
if ($config['required'] ?? false) {
$arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]);
}
}
$this->templateView->assignMultiple($arguments);
$resultArray['html'] = $this->templateView->render('Form/ImageManipulationElement');
return $resultArray;
}
protected function getFileObject(array $row, string $fieldName): ?File
{
$file = null;
$fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null;
if (is_array($fileUid) && isset($fileUid[0]['uid'])) {
$fileUid = $fileUid[0]['uid'];
}
if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
try {
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$fileReference = $resourceFactory->getFileReferenceObject($fileUid);
$file = $fileReference->getOriginalFile();
} catch (FileDoesNotExistException | \InvalidArgumentException) {
}
}
return $file;
}
protected function populateConfiguration(array $baseConfiguration): array
{
$defaultConfig = self::$defaultConfig;
$config = array_replace_recursive($defaultConfig, $baseConfiguration);
$imageConfig = $this->data['processedTca']['columns'][$config['imageField']];
$config['cropVariants'] = $imageConfig['config']['cropVariants'] ?? $defaultConfig['cropVariants'];
if (!is_array($config['cropVariants'])) {
throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267);
}
$cropVariants = [];
foreach ($config['cropVariants'] as $id => $cropVariant) {
// Filter allowed aspect ratios
$cropVariant['allowedAspectRatios'] = array_filter(
$cropVariant['allowedAspectRatios'] ?? [],
static function ($aspectRatio) {
return !(bool)($aspectRatio['disabled'] ?? false);
}
);
// Ignore disabled crop variants
if (!empty($cropVariant['disabled'])) {
continue;
}
if (empty($cropVariant['allowedAspectRatios'])) {
throw new InvalidConfigurationException(
'Crop variants configuration ' . $id . ' contains no allowed aspect ratios',
1620147893
);
}
// Enforce a crop area (default is full image)
if (empty($cropVariant['cropArea'])) {
$cropVariant['cropArea'] = Area::createEmpty()->asArray();
}
$cropVariants[$id] = $cropVariant;
}
$config['cropVariants'] = $cropVariants;
$config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
return $config;
}
protected function processConfiguration(array $config, string &$elementValue, File $file): array
{
$cropVariantCollection = CropVariantCollection::create($elementValue, $config['cropVariants']);
if (empty($config['readOnly']) && !empty($file->getProperty('width'))) {
$cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file);
$elementValue = (string)$cropVariantCollection;
}
$config['cropVariants'] = $cropVariantCollection->asArray();
$config['allowedExtensions'] = implode(
', ',
GeneralUtility::trimExplode(',', $config['allowedExtensions'], true)
);
return $config;
}
}

View File

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Form\FormDataProvider;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class UsercentricsDatabaseEditRow extends SiteDatabaseEditRow
{
/**
* First level of ['customData']['siteData'] to ['databaseRow']
*
* @param array $result
* @return array
* @throws \RuntimeException
*/
public function addData(array $result): array
{
if ($result['command'] !== 'edit' || $result['tableName'] !== 'site_usercentrics') {
return $result;
}
$tableName = $result['tableName'];
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class, $this->siteConfiguration);
if (in_array($tableName, ['site_usercentrics'], true)) {
$rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']);
try {
$rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId);
$parentFieldName = $result['inlineParentFieldName'];
if (!isset($rowData[$parentFieldName])) {
throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092);
}
$rowData = $rowData[$parentFieldName][$result['vanillaUid']];
$result['databaseRow']['uid'] = $result['vanillaUid'];
} catch (SiteNotFoundException $e) {
$rowData = [];
}
} else {
return $result;
}
foreach ($rowData as $fieldName => $value) {
// Flat values only - databaseRow has no "tree"
if (!is_array($value)) {
$result['databaseRow'][$fieldName] = $value;
}
}
// All "records" are always on pid 0
$result['databaseRow']['pid'] = 0;
return $result;
}
}

View File

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace Evoweb\EwBase\Form\FormDataProvider;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline;
/**
* Special data provider for the sites configuration module.
*
* Handle inline children of 'site'
*/
class UsercentricsTcaInline extends SiteTcaInline
{
/**
* Resolve inline fields
*
* @param array $result
* @return array
*/
public function addData(array $result): array
{
$result = $this->addInlineFirstPid($result);
foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
if (!$this->isInlineField($fieldConfig)) {
continue;
}
$childTableName = $fieldConfig['config']['foreign_table'] ?? '';
if (!in_array($childTableName, ['site_usercentrics'], true)) {
continue;
}
$result['processedTca']['columns'][$fieldName]['children'] = [];
$result = $this->resolveSiteRelatedChildren($result, $fieldName);
if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) {
throw new \RuntimeException('selectorOrUniqueConfiguration not implemented in sites module', 1624313533);
}
}
return $result;
}
}

View File

@ -0,0 +1,34 @@
<?php
namespace Evoweb\EwBase\Hooks;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
class PageLayoutView
{
public function contentIsUsed(array $params): bool
{
return $params['used'] || $this->findCTypeBegin($params['record']['CType']);
}
public function findCTypeBegin($cType): bool
{
$found = false;
foreach ($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['ContentUsedStrings'] ?? [] as $search) {
if (str_contains($cType, $search)) {
$found = true;
break;
}
}
return $found;
}
}

View File

@ -0,0 +1,102 @@
<?php
namespace Evoweb\EwBase\Hooks;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Exception;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class UsercentricsPostRenderHook
{
private array $siteArguments = [
'id' => '',
'version' => 'loader',
'useBlocker' => false,
];
public function executePostRenderHook(array $params): void
{
$pageUid = $this->getRequest()->getAttribute('frontend.controller')->page['uid'] ?? 0;
if ($pageUid < 1) {
return;
}
$siteArguments = $this->getCurrentSiteArguments($pageUid);
$id = $siteArguments['id'] ?? '';
if ($id !== '') {
$extensionConfig = $this->getExtensionConfiguration();
$preload = [];
$usercentrics = [];
// CMP 1, 2, ...
$version = $siteArguments['version'] ?? '';
if ($config = $extensionConfig[$version] ?? false) {
$preload[] = $config['preload'] ?? '';
$usercentrics[] = $config['template'] ?? '';
}
// Blocker
if (
($siteArguments['useBlocker'] ?? false)
&& $config = $extensionConfig['block'] ?? false
) {
$preload[] = $config['preload'] ?? '';
$usercentrics[] = $config['template'] ?? '';
}
$params['titleTag'] .= str_replace('###ID###', $id, implode(LF, array_merge($preload, $usercentrics)));
}
}
protected function getCurrentSiteArguments(int $pageUid): array
{
$siteArguments = $this->siteArguments;
try {
/** @var SiteFinder $siteFinder */
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
$site = $siteFinder->getSiteByPageId($pageUid);
foreach ($site->getAttribute('usercentrics') as $usercentrics) {
if (str_contains($usercentrics['applicationContext'] ?? '', (string)Environment::getContext())) {
$siteArguments = $usercentrics;
break;
}
}
} catch (Exception) {
}
return is_array($siteArguments) ? $siteArguments : [];
}
protected function getExtensionConfiguration(): array
{
/** @var ExtensionConfiguration $extensionConfiguration */
$extensionConfiguration = GeneralUtility::makeInstance(ExtensionConfiguration::class);
try {
$configuration = $extensionConfiguration->get('ew_base', 'userCentrics');
} catch (Exception) {
$configuration = [];
}
return is_array($configuration) ? $configuration : [];
}
protected function getRequest(): ServerRequestInterface
{
return $GLOBALS['TYPO3_REQUEST'];
}
}

View File

@ -0,0 +1,98 @@
<?php
namespace Evoweb\EwBase\ToolbarItems;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Toolbar\RequestAwareToolbarItemInterface;
use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface;
use TYPO3\CMS\Backend\View\BackendViewFactory;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class ReleaseToolbarItem implements ToolbarItemInterface, RequestAwareToolbarItemInterface
{
private ServerRequestInterface $request;
public function __construct(
private readonly BackendViewFactory $backendViewFactory,
) {
}
public function setRequest(ServerRequestInterface $request): void
{
$this->request = $request;
}
public function checkAccess(): bool
{
$result = false;
$backendUser = $this->getBackendUser();
if ($backendUser->isAdmin()) {
$result = true;
}
if ($backendUser->getTSConfig()['options.']['showSystemInformation'] ?? false) {
$result = true;
}
$release = $this->getRelease();
if (empty($release)) {
$result = false;
}
return $result;
}
public function getItem(): string
{
$view = $this->backendViewFactory->create($this->request, ['typo3/cms-backend', 'evoweb/ew-base']);
return $view->render('ToolbarItems/ShowReleaseToolbarItem');
}
public function hasDropDown(): bool
{
return true;
}
public function getDropDown(): string
{
$view = $this->backendViewFactory->create($this->request, ['typo3/cms-backend', 'evoweb/ew-base']);
$view->assignMultiple([
'release' => $this->getRelease(),
]);
return $view->render('ToolbarItems/ShowReleaseDropDown');
}
public function getAdditionalAttributes(): array
{
return [];
}
public function getIndex(): int
{
return 20;
}
protected function getRelease(): array
{
$release = GeneralUtility::getUrl(Environment::getProjectPath() . '/release');
return $release ? [
'release' => trim($release),
'isTag' => str_contains($release, '.'),
] : [];
}
protected function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
}

View File

@ -0,0 +1,101 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Updates;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Attribute\UpgradeWizard;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
/**
* Migrate gridelements elements to container elements
*/
#[UpgradeWizard('gridelementsToContainer')]
class GridelementsToContainerMigration implements UpgradeWizardInterface
{
private const TABLE_NAME = 'tt_content';
protected LoggerInterface $logger;
public function __construct(
protected GridelementsToContainerService $service,
protected LogManager $logManager,
) {
$this->logger = $logManager->getLogger(self::class);
}
public function getIdentifier(): string
{
return 'gridelementsToContainer';
}
public function getTitle(): string
{
return 'Migrate gridelements to container';
}
public function getDescription(): string
{
return 'Migrates content elements that are type gridelements to corresponding container elements';
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}
public function updateNecessary(): bool
{
return $this->hasRecordsToUpdate();
}
public function executeUpdate(): bool
{
$updateSuccessful = true;
try {
$this->service->migrate();
} catch (\Exception $exception) {
$this->logger->log(LogLevel::ERROR, $exception->getMessage());
$updateSuccessful = false;
}
return $updateSuccessful;
}
protected function hasRecordsToUpdate(): bool
{
return (bool)$this->getPreparedQueryBuilder()->count('uid')->executeQuery()->fetchOne();
}
protected function getPreparedQueryBuilder(): QueryBuilder
{
$queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME);
$queryBuilder->getRestrictions()
->removeByType(HiddenRestriction::class)
->removeByType(StartTimeRestriction::class)
->removeByType(EndTimeRestriction::class);
$queryBuilder
->from(self::TABLE_NAME)
->where(
$queryBuilder->expr()->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1'))
);
return $queryBuilder;
}
protected function getConnectionPool(): ConnectionPool
{
return GeneralUtility::makeInstance(ConnectionPool::class);
}
}

View File

@ -0,0 +1,268 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Updates;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['migrationResolveContainer'] = [];
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['migrationColPosOffset'] = 0;
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['migrationMapping'] = [
'130' => [
'CType' => 'three-col-columns',
],
'140' => [
'CType' => [
'search' => 'pi_flexform/type',
'matches' => [
0 => 'two-col-columns-11',
1 => 'two-col-columns-12',
2 => 'two-col-columns-21',
],
],
'map' => [
'pi_flexform/row_class' => 'frame_class',
]
],
];
*/
class GridelementsToContainerService
{
private const TABLE_NAME = 'tt_content';
protected array $resolveContainer = [];
protected int $colPosOffset = 0;
protected array $configuration = [];
public function __construct(
protected ConnectionPool $connectionPool,
protected DataHandler $dataHandler,
protected FlexFormService $flexFormService,
) {
$config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base'];
$this->resolveContainer = $config['migrationResolveContainer'] ?? [];
$this->colPosOffset = $config['migrationColPosOffset'] ?? 0;
$this->configuration = $config['migrationMapping'] ?? [];
}
public function migrate(): void
{
$this->initializeDataHandler();
// Move children out of container and remove container
foreach ($this->resolveContainer as $containerId) {
$this->resolveContainers((string)$containerId);
}
$this->migrateConfiguredContainer();
}
protected function initializeDataHandler(): void
{
$backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class);
$backendUser->user = [
'uid' => 0,
'admin' => 1,
];
$this->dataHandler->start([], [], $backendUser);
}
protected function resolveContainers(string $layout): void
{
$containers = $this->getGridElementsByLayout($layout);
foreach ($containers as $container) {
$this->processContainerResolve($container);
}
}
protected function processContainerResolve(array $container): void
{
$children = $this->getGridContainerChildren($container['uid']);
// move first child after container
$moveAfterThis = $container;
foreach ($children as $child) {
[$moveAfterThis, $container] = $this->processContainerResolveChild($child, $moveAfterThis, $container);
}
$this->deleteElement($container['uid']);
}
protected function processContainerResolveChild(array $child, array $moveAfterThis, array $container): array
{
$this->updateElement(
$child['uid'],
[
'tx_gridelements_container' => 0,
'colPos' => $container['colPos'],
'header' => $child['header'] ?: $container['header']
]
);
$this->moveElementAfterElement($child['uid'], $moveAfterThis['uid']);
// use this child to move the next child after
$moveAfterThis = $child;
// empty container header so only the first child gets the header
$container['header'] = '';
return [$moveAfterThis, $container];
}
protected function deleteElement(int $uid): void
{
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->update(self::TABLE_NAME, ['delete' => 1], ['uid' => $uid]);
}
protected function moveElementAfterElement(int $elementToMove, int $elementToMoveAfter): void
{
$this->dataHandler->moveRecord(self::TABLE_NAME, $elementToMove, $elementToMoveAfter * -1);
}
protected function migrateConfiguredContainer(): void
{
array_walk($this->configuration, function($config, $key) {
$containers = $this->getGridElementsByLayout((string)$key);
foreach ($containers as $container) {
$container['pi_flexform'] = $this->flexFormService->convertFlexFormContentToArray(
$container['pi_flexform']
);
$this->processContainerMigration($container, $config);
}
});
}
protected function processContainerMigration(array $container, array $config): void
{
$children = $this->getGridContainerChildren($container['uid']);
foreach ($children as $child) {
$this->processContainerMigrationChild($child, $container);
}
$data = [
'CType' => $this->getCType($container, $config),
'tx_gridelements_backend_layout' => ''
];
if (isset($config['map']) && is_array($config['map']) && !empty($container['pi_flexform'])) {
$data = $this->addMappedValues($data, $container, $config['map']);
}
$this->updateElement($container['uid'], $data);
}
protected function processContainerMigrationChild(array $child, array $container): void
{
$this->updateElement(
$child['uid'],
[
'hidden' => $child['hidden'] ?: $container['hidden'],
'colPos' => $child['tx_gridelements_columns'] + $this->colPosOffset,
'tx_container_parent' => $child['tx_gridelements_container'],
'tx_gridelements_columns' => 0,
'tx_gridelements_container' => 0,
]
);
}
protected function getCType(array $container, array $config): string
{
if (is_array($config['CType'])) {
$value = ArrayUtility::getValueByPath($container, $config['CType']['search']);
$result = $config['CType']['matches'][$value] ?? null;
} else {
$result = $config['CType'] ?? null;
}
if (empty($result)) {
throw new \Exception('CType must always be set');
}
return $result;
}
protected function addMappedValues(array $data, array $container, array $config): array
{
foreach ($config as $from => $to) {
try {
$value = ArrayUtility::getValueByPath($container, $from);
if (empty($container[$to]) && !empty($value)) {
$data[$to] = $value;
}
} catch (\throwable) {}
}
return $data;
}
protected function updateElement(int $uid, array $changes): void
{
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->update(self::TABLE_NAME, $changes, ['uid' => $uid]);
}
protected function getGridContainerChildren(int $containerUid): array
{
$queryBuilder = $this->getQueryBuilderForTable();
return $queryBuilder
->select('*')
->where(
$queryBuilder->expr()->eq(
'tx_gridelements_container',
$queryBuilder->createNamedParameter($containerUid, \PDO::PARAM_INT)
)
)
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
}
protected function getGridElementsByLayout(string $layout): array
{
$queryBuilder = $this->getQueryBuilderForTable();
$expr = $queryBuilder->expr();
return $queryBuilder
->select('*')
->where(
$expr->eq('CType', $queryBuilder->createNamedParameter('gridelements_pi1')),
$expr->eq('tx_gridelements_backend_layout', $queryBuilder->createNamedParameter($layout))
)
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
}
protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()
->removeByType(HiddenRestriction::class)
->removeByType(StartTimeRestriction::class)
->removeByType(EndTimeRestriction::class);
$queryBuilder->from($table);
return $queryBuilder;
}
}

View File

@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Updates;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Attribute\UpgradeWizard;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
/**
* Migrate parent child elements to container elements
*/
#[UpgradeWizard('parentChildToContainer')]
class ParentChildToContainerMigration implements UpgradeWizardInterface
{
private const TABLE_NAME = 'tt_content';
protected LoggerInterface $logger;
public function __construct(
protected ParentChildToContainerService $service,
protected LogManager $logManager,
) {
$this->logger = $logManager->getLogger(self::class);
}
public function getIdentifier(): string
{
return 'parentChildToContainer';
}
public function getTitle(): string
{
return 'Migrate parent/child to container';
}
public function getDescription(): string
{
return 'Migrates content elements that are type parent/child to corresponding container elements';
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}
public function updateNecessary(): bool
{
return $this->hasRecordsToUpdate();
}
public function executeUpdate(): bool
{
$updateSuccessful = true;
try {
$this->service->migrate();
} catch (\Exception $exception) {
$this->logger->log(LogLevel::ERROR, $exception->getMessage());
$updateSuccessful = false;
}
return $updateSuccessful;
}
protected function hasRecordsToUpdate(): bool
{
$queryBuilder = $this->getPreparedQueryBuilder();
$tableColumns = $queryBuilder
->getConnection()
->createSchemaManager()
->listTableColumns(self::TABLE_NAME);
return isset($tableColumns['parent'])
&& $queryBuilder
->count('uid')
->executeQuery()
->fetchOne() > 0;
}
protected function getPreparedQueryBuilder(): QueryBuilder
{
$queryBuilder = $this->getConnectionPool()->getQueryBuilderForTable(self::TABLE_NAME);
$queryBuilder->getRestrictions()
->removeByType(HiddenRestriction::class)
->removeByType(StartTimeRestriction::class)
->removeByType(EndTimeRestriction::class);
$queryBuilder
->from(self::TABLE_NAME)
->where(
// check if there are still records that have parent instead of tx_container_parent
$queryBuilder->expr()->gt('parent', 0)
);
return $queryBuilder;
}
protected function getConnectionPool(): ConnectionPool
{
return GeneralUtility::makeInstance(ConnectionPool::class);
}
}

View File

@ -0,0 +1,168 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\Updates;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['childParentColPosOffset'] = 0;
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['childParentMigrationMapping'] = [
'downloads' => [ 'CType' => 'container-downloads' ],
];
*/
class ParentChildToContainerService
{
private const TABLE_NAME = 'tt_content';
protected int $colPosOffset = 0;
protected array $configuration = [];
public function __construct(
protected ConnectionPool $connectionPool,
protected DataHandler $dataHandler,
protected FlexFormService $flexFormService,
) {
$config =& $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base'];
$this->colPosOffset = $config['childParentColPosOffset'] ?? 0;
$this->configuration = $config['childParentMigrationMapping'] ?? [];
}
public function migrate(): void
{
$this->initializeDataHandler();
$this->migrateConfiguredContainer();
}
protected function initializeDataHandler(): void
{
$backendUser = GeneralUtility::makeInstance(BackendUserAuthentication::class);
$backendUser->user = [
'uid' => 0,
'admin' => 1,
];
$this->dataHandler->start([], [], $backendUser);
}
protected function migrateConfiguredContainer(): void
{
array_walk($this->configuration, function($config, $key) {
$parents = $this->getParentsByCType((string)$key);
foreach ($parents as $parent) {
$this->processParentMigration($parent, $config);
}
});
}
protected function processParentMigration(array $parent, array $config): void
{
$children = $this->getChildren($parent['uid']);
foreach ($children as $child) {
$this->processChildMigration($child, $parent);
}
$this->updateElement(
$parent['uid'],
[
'CType' => $this->getCType($parent, $config),
'children' => 0,
]
);
}
protected function processChildMigration(array $child, array $parent): void
{
$this->updateElement(
$child['uid'],
[
'tx_container_parent' => $parent['uid'],
'colPos' => $child['colPos'] + $this->colPosOffset,
'parent' => 0,
]
);
}
protected function getCType(array $container, array $config): string
{
if (is_array($config['CType'])) {
$value = ArrayUtility::getValueByPath($container, $config['CType']['search']);
$result = $config['CType']['matches'][$value] ?? null;
} else {
$result = $config['CType'] ?? null;
}
if (empty($result)) {
throw new \Exception('CType must always be set');
}
return $result;
}
protected function updateElement(int $uid, array $changes): void
{
$this->connectionPool
->getConnectionForTable(self::TABLE_NAME)
->update(self::TABLE_NAME, $changes, ['uid' => $uid]);
}
protected function getChildren(int $parentUid): array
{
$queryBuilder = $this->getQueryBuilderForTable();
return $queryBuilder
->select('*')
->where(
$queryBuilder->expr()->eq(
'parent',
$queryBuilder->createNamedParameter($parentUid, \PDO::PARAM_INT)
)
)
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
}
protected function getParentsByCType(string $cType): array
{
$queryBuilder = $this->getQueryBuilderForTable();
$expr = $queryBuilder->expr();
return $queryBuilder
->select('*')
->where(
$expr->eq('CType', $queryBuilder->createNamedParameter($cType))
)
->orderBy('sorting')
->executeQuery()
->fetchAllAssociative();
}
protected function getQueryBuilderForTable(string $table = 'tt_content'): QueryBuilder
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable($table);
$queryBuilder->getRestrictions()
->removeByType(HiddenRestriction::class)
->removeByType(StartTimeRestriction::class)
->removeByType(EndTimeRestriction::class);
$queryBuilder->from($table);
return $queryBuilder;
}
}

25
Classes/User/AssetPath.php Executable file
View File

@ -0,0 +1,25 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\User;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
/**
* EXAMPLE:
* page.headerData.10 = USER
* page.headerData.10.userFunc = Evoweb\EwBase\User\AssetPath->getAbsolutePublicPath
* page.headerData.10.file = EXT:site_package/Resources/Public/manifest.json
* page.headerData.10.stdWrap.wrap = <link rel="manifest" href="|"/>
*/
class AssetPath
{
public function getAbsolutePublicPath(string $content, array $conf): string
{
return $content
. GeneralUtility::getIndpEnv('TYPO3_SITE_URL')
. ltrim(PathUtility::getPublicResourceWebPath($conf['file']), '/');
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Array;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class AddViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('array', 'array', 'Array to add value to');
$this->registerArgument('key', 'string', 'Key to add value by', true);
$this->registerArgument('value', 'mixed', 'Value to add');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return array
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$array = $arguments['array'] ?: [];
$key = $arguments['key'];
$value = !is_null($arguments['value']) ? $arguments['value'] : $renderChildrenClosure();
return array_merge($array, [$key => $value]);
}
}

View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
namespace Evoweb\EwBase\ViewHelpers\Be;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Type\Bitmask\Permission;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class ThumbnailViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* Initializes the arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('row', 'array', 'content data', true);
$this->registerArgument('tableName', 'string', 'table name', true);
$this->registerArgument('fieldName', 'string', 'field name', true);
}
/**
* Render a constant
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string Value of constant
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$row = $arguments['row'];
$tableName = $arguments['tableName'];
$fieldName = $arguments['fieldName'];
return self::linkEditContent(
BackendUtility::thumbCode(row: $row, table: $tableName, field: $fieldName, linkInfoPopup: false),
$row
);
}
protected static function linkEditContent(string $linkText, $row): string
{
if (empty($linkText)) {
return $linkText;
}
$backendUser = self::getBackendUser();
if (
$backendUser->check('tables_modify', 'tt_content')
&& $backendUser->recordEditAccessInternals('tt_content', $row)
&& (
new Permission(
$backendUser->calcPerms(BackendUtility::getRecord('pages', $row['pid']) ?? [])
)
)->editContentPermissionIsGranted()
) {
$urlParameters = [
'edit' => [
'tt_content' => [
$row['uid'] => 'edit',
],
],
'returnUrl' => self::getRequest()->getAttribute('normalizedParams')->getRequestUri()
. '#element-tt_content-' . $row['uid'],
];
/** @var UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$url = (string)$uriBuilder->buildUriFromRoute('record_edit', $urlParameters);
return '<a href="' . htmlspecialchars($url) . '" title="'
. htmlspecialchars(self::getLanguageService()->sL(
'LLL:EXT:typo3/sysext/backend/Resources/Private/Language/locallang_layout.xlf:edit'
))
. '">' . $linkText . '</a>';
}
return $linkText;
}
protected static function getBackendUser(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
protected static function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
protected static function getRequest(): ServerRequestInterface
{
return $GLOBALS['TYPO3_REQUEST'];
}
}

View File

@ -0,0 +1,41 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Condition;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* ### Condition: Array contains element
*
* Condition ViewHelper which renders the `then` child if provided
* string $haystack contains provided string $needle.
*/
class InArrayViewHelper extends AbstractConditionViewHelper
{
/**
* Initialize arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('haystack', 'array', 'haystack', true);
$this->registerArgument('needle', 'mixed', 'needle', true);
}
/**
* @param array $arguments
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
$array = $arguments['haystack']->toArray();
return in_array($arguments['needle'], $array);
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Condition;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
/**
* ### Condition: String contains substring
*
* Condition ViewHelper which renders the `then` child if provided
* string $haystack contains provided string $needle.
*/
class StringContainsViewHelper extends AbstractConditionViewHelper
{
/**
* Initialize arguments
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('haystack', 'string', 'haystack', true);
$this->registerArgument('needle', 'string', 'need', true);
}
/**
* @param array $arguments
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
return false !== strpos($arguments['haystack'], $arguments['needle']);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Context;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
class DevelopmentViewHelper extends AbstractConditionViewHelper
{
/**
* @param array $arguments
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
return Environment::getContext()->isDevelopment();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Context;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
class ProductionViewHelper extends AbstractConditionViewHelper
{
/**
* @param array $arguments
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
return Environment::getContext()->isProduction();
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Context;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Core\Environment;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractConditionViewHelper;
class StagingViewHelper extends AbstractConditionViewHelper
{
/**
* @param array $arguments
* @return bool
*/
protected static function evaluateCondition($arguments = null)
{
return 'Production/Staging' === (string)Environment::getContext();
}
}

View File

@ -0,0 +1,77 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
/**
* = Examples =
*
* <code title="Simple Loop">
* <ewb:fixFlexformForExtbase data="{data}">....</ewb:fixFlexformForExtbase>
* </code>
* <output>
* fixed flexform data for extbase controller
* </output>
*
* @api
*/
class FixFlexformForExtbaseViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var boolean
*/
protected $escapeOutput = false;
public function initializeArguments(): void
{
parent::initializeArguments();
$this->registerArgument('data', 'array', 'The data array of content element', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return array
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$templateVariableContainer = $renderingContext->getVariableProvider();
/** @var FlexFormTools $flexFormTools */
$flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$data = $arguments['data'];
if (is_array($data['pi_flexform'])) {
$data['pi_flexform'] = $flexFormTools->flexArray2Xml($data['pi_flexform']);
}
$templateVariableContainer->add('data', $data);
$output = $renderChildrenClosure();
$templateVariableContainer->remove('data');
return $output;
}
}

View File

@ -0,0 +1,80 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* = Examples =
*
* <code title="Simple Loop">
* <ewb:flexForm data="{data}">....</ewb:flexForm>
* </code>
* <output>
* fixed flexform data for extbase controller
* </output>
*
* @api
*/
class FlexFormViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var boolean
*/
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('data', 'array', 'Array to get flex form data from', true);
$this->registerArgument('fieldName', 'string', 'Field name', false, 'pi_flexform');
$this->registerArgument('as', 'string', 'Name of the variable to assign', false, 'flexFormData');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return array
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$data = [];
if (is_array($arguments['data'])) {
if (isset($arguments['data'][$arguments['fieldName']])) {
$data = is_array($arguments['data'][$arguments['fieldName']]) ?
$arguments['data'][$arguments['fieldName']] :
GeneralUtility::xml2array($arguments['data'][$arguments['fieldName']]);
$data = $data['data'] ?? $data;
}
}
$templateVariableContainer = $renderingContext->getVariableProvider();
$templateVariableContainer->add($arguments['as'], $data);
$content = $renderChildrenClosure();
$templateVariableContainer->remove($arguments['as']);
return $content;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class HashViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var boolean
*/
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('action', 'string', 'Target action');
$this->registerArgument('arguments', 'array', 'Arguments for the controller action, associative array');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$result = '';
if (
$arguments['action'] !== null
&& $arguments['arguments'] !== null
&& isset($arguments['arguments']['user'])
) {
$result = \TYPO3\CMS\Core\Utility\GeneralUtility::hmac(
$arguments['action'] . '::' . $arguments['arguments']['user']
);
}
return $result;
}
}

View File

@ -0,0 +1,56 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Iterator;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class AddViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('array', 'array', 'Array to add value to');
$this->registerArgument('key', 'string', 'Key to add value by', true);
$this->registerArgument('value', 'mixed', 'Value to add');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return array
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$array = $arguments['array'] ?: [];
$key = $arguments['key'];
$value = $arguments['value'] ?: $renderChildrenClosure();
return array_merge($array, [$key => $value]);
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace Evoweb\EwBase\ViewHelpers\Iterator;
/*
* This file is part of the FluidTYPO3/Vhs project under GPLv2 or later.
*
* For the full copyright and license information, please read the
* LICENSE.md file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Explode ViewHelper
*
* Explodes a string by $glue.
*
* {data.header -> ewb:iterator.explode(glue: 'constant:LF')}
*
* <ewb:iterator.explode content="{data.header}" as="as" glue="constant:LF">
* <span>{as}</span>
* </ewb:iterator.explode>
*/
class ExplodeViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
protected static string $method = 'explode';
public function initializeArguments()
{
$this->registerArgument(
'as',
'string',
'Template variable name to assign; if not specified the ViewHelper returns the variable instead.'
);
$this->registerArgument('content', 'string', 'String to be exploded by glue');
$this->registerArgument(
'glue',
'string',
'String used as glue in the string to be exploded. Use glue value of "constant:NAMEOFCONSTANT" ' .
'(fx "constant:LF" for linefeed as glue)',
false,
','
);
}
/**
* Render method
*
* @return string|array
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$content = $arguments['content'] ?? $renderChildrenClosure();
$glue = static::resolveGlue($arguments);
$content = call_user_func_array(static::$method, [$glue, $content]);
$as = $arguments['as'];
if (true === empty($as)) {
$output = $content;
} else {
$templateVariableContainer = $renderingContext->getVariableProvider();
$templateVariableContainer->add($as, $content);
$output = $renderChildrenClosure();
$templateVariableContainer->remove($as);
}
return $output;
}
protected static function resolveGlue(array $arguments): string
{
$glue = $arguments['glue'];
if (false !== strpos($glue, ':') && 1 < strlen($glue)) {
// glue contains a special type identifier, resolve the actual glue
list ($type, $value) = explode(':', $glue);
switch ($type) {
case 'constant':
$glue = constant($value);
break;
default:
$glue = $value;
}
}
return $glue;
}
}

View File

@ -0,0 +1,67 @@
<?php
declare(strict_types=1);
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* = Examples =
*
* <code title="Simple Loop">
* <ewb:publicPath path="{data}">....</ewb:publicPath>
* </code>
* <output>
* fixed flexform data for extbase controller
* </output>
*
* @api
*/
class PublicPathViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var boolean
*/
protected $escapeOutput = false;
protected static ?array $frontendGroupIds = null;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('path', 'string', 'Extension resource path', true);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return bool
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$path = (string)$arguments['path'];
return PathUtility::getPublicResourceWebPath($path);
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithContentArgumentAndRenderStatic;
/**
* Variable assigning ViewHelper
*
* Assigns one template variable which will exist also
* after the ViewHelper is done rendering, i.e. adds
* template variables.
*
* If you require a variable assignment which does not
* exist in the template after a piece of Fluid code
* is rendered, consider using ``f:alias`` ViewHelper instead.
*
* Usages:
*
* ::
*
* {f:variable(name: 'myvariable', value: 'some value')}
* <f:variable name="myvariable">some value</f:variable>
* {oldvariable -> f:format.htmlspecialchars() -> f:variable(name: 'newvariable')}
* <f:variable name="myvariable"><f:format.htmlspecialchars>{oldvariable}</f:format.htmlspecialchars></f:variable>
*
* @see \TYPO3Fluid\Fluid\ViewHelpers\IfViewHelper
* @api
*/
class ReplaceViewHelper extends AbstractViewHelper
{
use CompileWithContentArgumentAndRenderStatic;
public function initializeArguments()
{
$this->registerArgument('value', 'string', 'String to replace in');
$this->registerArgument('search', 'string', 'Search string');
$this->registerArgument('replace', 'string', 'Replace value');
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return null
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$content = $arguments['value'];
if ($content === null) {
$content = $renderChildrenClosure();
}
return str_replace($arguments['search'], $arguments['replace'], $content);
}
}

View File

@ -0,0 +1,105 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Type\Icon\IconState;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
/**
* Displays icon identified by icon identifier.
*
* Examples
* ========
*
* Default::
*
* <core:icon identifier="actions-menu" />
*
* Output::
*
* <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-menu" data-identifier="actions-menu">
* <span class="icon-markup">
* <img src="/typo3/sysext/core/Resources/Public/Icons/T3Icons/actions/actions-menu.svg"
* width="16" height="16">
* </span>
* </span>
*
* Inline::
*
* <core:icon identifier="actions-menu" alternativeMarkupIdentifier="inline" />
*
* Output::
*
* <span class="t3js-icon icon icon-size-small icon-state-default icon-actions-menu" data-identifier="actions-menu">
* <span class="icon-markup">
* <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g class="icon-color"><path
* d="M9 12v2H7v-2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM9
* 7v2H7V7h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM9
* 2v2H7V2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM4
* 7v2H2V7h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM4
* 2v2H2V2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM4
* 12v2H2v-2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM14
* 7v2h-2V7h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM14
* 2v2h-2V2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5zM14
* 12v2h-2v-2h2m.5-1h-3c-.3 0-.5.2-.5.5v3c0 .3.2.5.5.5h3c.3 0 .5-.2.5-.5v-3c0-.3-.2-.5-.5-.5z"/>
* </g></svg>
* </span>
* </span>
*/
class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* ViewHelper returns HTML, thus we need to disable output escaping
*
* @var bool
*/
protected $escapeOutput = false;
public function initializeArguments(): void
{
$this->registerArgument('identifier', 'string', 'Identifier of the icon as registered in the Icon Registry.', true);
$this->registerArgument('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, Icon::SIZE_SMALL);
$this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.', false);
$this->registerArgument('state', 'string', 'Sets the state of the icon. All values of the Icons.states enum are allowed, these are: "default" and "disabled".', false, IconState::STATE_DEFAULT);
$this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.', false);
}
/**
* Prints icon html for $identifier key
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$identifier = $arguments['identifier'];
$size = $arguments['size'];
$overlay = $arguments['overlay'];
$state = IconState::cast($arguments['state']);
$alternativeMarkupIdentifier = $arguments['alternativeMarkupIdentifier'];
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
return $iconFactory->getIcon($identifier, $size, $overlay, $state)->getMarkup($alternativeMarkupIdentifier);
}
}

View File

@ -0,0 +1,58 @@
<?php
namespace Evoweb\EwBase\ViewHelpers;
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
class TrimViewHelper extends AbstractViewHelper
{
use CompileWithRenderStatic;
/**
* @var bool
*/
protected $escapeOutput = false;
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('content', 'string', 'Content to be trimmed');
$this->registerArgument('characters', 'string', 'Characters to be removed', false);
}
/**
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
*
* @return string
*/
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$content = $arguments['content'] ? $arguments['content'] : $renderChildrenClosure();
$characters = $arguments['characters'] ? $arguments['characters'] : null;
if ($characters !== null) {
$content = trim($content, $characters);
} else {
$content = trim($content);
}
return $content;
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Evoweb\EwBase\Xclass;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow;
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class SiteDatabaseEditRow extends BaseSiteDatabaseEditRow
{
/**
* First level of ['customData']['siteData'] to ['databaseRow']
*
* @param array $result
* @return array
* @throws \RuntimeException
*/
public function addData(array $result): array
{
if ($result['command'] !== 'edit' || !empty($result['databaseRow'])) {
return $result;
}
$tableName = $result['tableName'];
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class, $this->siteConfiguration);
if ($tableName === 'site') {
$rootPageId = (int)$result['vanillaUid'];
$rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId);
$result['databaseRow']['uid'] = $rowData['rootPageId'];
$result['databaseRow']['identifier'] = $result['customData']['siteIdentifier'];
} elseif (in_array($tableName, ['site_errorhandling', 'site_language', 'site_route', 'site_base_variant'], true)) {
$rootPageId = (int)($result['inlineTopMostParentUid'] ?? $result['inlineParentUid']);
try {
$rowData = $this->getRawConfigurationForSiteWithRootPageId($siteFinder, $rootPageId);
$parentFieldName = $result['inlineParentFieldName'];
if (!isset($rowData[$parentFieldName])) {
throw new \RuntimeException('Field "' . $parentFieldName . '" not found', 1520886092);
}
$rowData = $rowData[$parentFieldName][$result['vanillaUid']];
$result['databaseRow']['uid'] = $result['vanillaUid'];
} catch (SiteNotFoundException $e) {
$rowData = [];
}
} else {
return $result;
}
foreach ($rowData as $fieldName => $value) {
// Flat values only - databaseRow has no "tree"
if (!is_array($value)) {
$result['databaseRow'][$fieldName] = $value;
}
}
// All "records" are always on pid 0
$result['databaseRow']['pid'] = 0;
return $result;
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace Evoweb\EwBase\Xclass;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline as BaseSiteTcaInline;
class SiteTcaInline extends BaseSiteTcaInline
{
/**
* Resolve inline fields
*
* @param array $result
* @return array
*/
public function addData(array $result): array
{
$result = $this->addInlineFirstPid($result);
foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) {
if (!$this->isInlineField($fieldConfig)) {
continue;
}
$childTableName = $fieldConfig['config']['foreign_table'] ?? '';
if (!in_array($childTableName, ['site_errorhandling', 'site_route', 'site_base_variant'], true)) {
return $result;
}
$result['processedTca']['columns'][$fieldName]['children'] = [];
$result = $this->resolveSiteRelatedChildren($result, $fieldName);
if (!empty($result['processedTca']['columns'][$fieldName]['config']['selectorOrUniqueConfiguration'])) {
throw new \RuntimeException('selectorOrUniqueConfiguration not implemented in sites module', 1624313533);
}
}
return $result;
}
}

19
Configuration/Icons.php Executable file
View File

@ -0,0 +1,19 @@
<?php
use TYPO3\CMS\Core\Imaging\IconProvider\BitmapIconProvider;
use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider;
return [
'ew-base-extension' => [
'provider' => SvgIconProvider::class,
'source' => 'EXT:ew_base/Resources/Public/Icons/Extension.svg',
],
'ew-base-extension-small' => [
'provider' => SvgIconProvider::class,
'source' => 'EXT:ew_base/Resources/Public/Icons/Extension_16.svg',
],
'ew-usercentrics' => [
'provider' => BitmapIconProvider::class,
'source' => 'EXT:ew_base/Resources/Public/Icons/usercentrics.svg',
],
];

View File

@ -0,0 +1,16 @@
<?php
return [
'dependencies' => [
'backend',
'core',
],
'tags' => [
'backend.form',
],
'imports' => [
'@evoweb/ew-base/' => [
'path' => 'EXT:ew_base/Resources/Public/JavaScript/',
],
],
];

45
Configuration/Services.yaml Executable file
View File

@ -0,0 +1,45 @@
services:
_defaults:
autowire: true
autoconfigure: true
public: false
Evoweb\EwBase\:
resource: '../Classes/*'
Evoweb\EwBase\Command\ContentElementCommand:
tags:
- name: 'console.command'
command: 'ew-base:contentelement'
description: 'Command to list all content elements for a given subtree'
schedulable: false
Evoweb\EwBase\EventListener\CssMerger:
tags: ['event.listener']
Evoweb\EwBase\EventListener\JsMerger:
tags: ['event.listener']
Evoweb\EwBase\EventListener\IsContentUsedOnPageLayout:
tags: ['event.listener']
Evoweb\EwBase\ToolbarItems\ReleaseToolbarItem:
tags: ['backend.toolbar.item']
Evoweb\EwBase\Hooks\UsercentricsPostRenderHook:
public: true
Evoweb\EwBase\Updates\GridelementsToContainerMigration:
public: true
Evoweb\EwBase\Updates\GridelementsToContainerService:
public: true
Evoweb\EwBase\Updates\ParentChildToContainerMigration:
public: true
Evoweb\EwBase\Updates\ParentChildToContainerService:
public: true
Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow:
public: true

View File

@ -0,0 +1,22 @@
<?php
$GLOBALS['SiteConfiguration']['site']['columns']['usercentrics'] = [
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site.usercentrics',
'description' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site.usercentrics.description',
'config' => [
'type' => 'inline',
'foreign_table' => 'site_usercentrics',
'maxitems' => 1,
'appearance' => [
'enabledControls' => [
'info' => false,
],
],
],
];
$GLOBALS['SiteConfiguration']['site']['types']['0']['showitem'] = str_replace(
' routes',
' routes, usercentrics,',
$GLOBALS['SiteConfiguration']['site']['types']['0']['showitem']
);

View File

@ -0,0 +1,78 @@
<?php
return [
'ctrl' => [
'label' => 'id',
'title' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.ctrl.title',
'typeicon_classes' => [
'default' => 'ew-usercentrics',
],
],
'columns' => [
'id' => [
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.id',
'config' => [
'type' => 'input',
'size' => 12,
'max' => 12,
],
],
'version' => [
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.version',
'config' => [
'type' => 'select',
'renderType' => 'selectSingle',
'minitems' => 0,
'maxitems' => 1,
'size' => 1,
'items' => [
[
0 => 'CMP 2',
1 => 'loader',
],
[
0 => 'CMP 1',
1 => 'main',
],
]
],
],
'applicationContext' => [
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.applicationContext',
'description' => 'Values are separated by line breaks',
'config' => [
'type' => 'text',
'eval' => 'trim',
],
],
'useBlocker' => [
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_siteconfiguration.xlf:site_usercentrics.column.useBlocker',
'config' => [
'type' => 'check',
'renderType' => 'checkboxToggle',
'default' => false,
'items' => [
[
0 => '',
1 => true,
],
]
],
],
],
'types' => [
'1' => [
'showitem' => '
--palette--;;usercentrics,
',
],
],
'palettes' => [
'usercentrics' => [
'showitem' => '
id, version, --linebreak--,
applicationContext, useBlocker,
'
]
]
];

View File

@ -0,0 +1,35 @@
<?php
use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
$newColumns = [
'sectionIndex_uid' => [
'displayCond' => 'FIELD:doktype:=:4',
'label' => 'LLL:EXT:ew_base/Resources/Private/Language/locallang_db.xlf:pages.content_uid',
'config' => [
'type' => 'group',
'allowed' => 'tt_content',
'size' => 1,
'maxitems' => 1,
'minitems' => 0,
'suggestOptions' => [
'default' => [
'additionalSearchFields' => 'header, bodytext',
'searchWholePhrase' => false
]
],
'default' => 0,
'behaviour' => [
'allowLanguageSynchronization' => true
]
]
],
];
ExtensionManagementUtility::addTCAcolumns('pages', $newColumns);
ExtensionManagementUtility::addFieldsToPalette(
'pages',
'shortcutpage',
'--linebreak--, sectionIndex_uid',
'after:shortcut'
);

View File

@ -0,0 +1,26 @@
mutations:
connect:
mode: extend
directive: connect-src
sources:
- https://consentcdn.cookiebot.com/
frame:
mode: extend
directive: frame-src
sources:
- https://consentcdn.cookiebot.com/
script-src:
mode: extend
directive: script-src-elem
sources:
- https://consent.cookiebot.com/
- https://consentcdn.cookiebot.com/
style-src:
mode: extend
directive: style-src-elem
sources:
- https://consent.cookiebot.com/
- https://consentcdn.cookiebot.com/

View File

@ -0,0 +1,12 @@
mutations:
- mode: extend
directive: script-src
sources:
- https://www.googleadservices.com
- https://www.google.com
- mode: extend
directive: img-src
sources:
- https://googleads.g.doubleclick.net
- https://www.google.com

View File

@ -0,0 +1,17 @@
mutations:
- mode: extend
directive: script-src
sources:
- https://www.googleadservices.com
- https://googleads.g.doubleclick.net
- https://www.google.com
- mode: extend
directive: img-src
sources:
- https://www.google.com
- mode: extend
directive: frame-src
sources:
- https://bid.g.doubleclick.net

View File

@ -0,0 +1,27 @@
mutations:
script:
mode: extend
directive: script-src
sources:
- https://*.googletagmanager.com
script-src:
mode: extend
directive: script-src-elem
sources:
- https://*.googletagmanager.com
img:
mode: extend
directive: img-src
sources:
- https://*.google-analytics.com
- https://*.googletagmanager.com
connect:
mode: extend
directive: connect-src
sources:
- https://*.google-analytics.com
- https://*.analytics.google.com
- https://*.googletagmanager.com

View File

@ -0,0 +1,385 @@
mutations:
- mode: extend
directive: img-src
sources:
- https://*.analytics.google.com
- https://*.g.doubleclick.net
- https://*.google.com
- https://*.google.ad
- https://*.google.ae
- https://*.google.com.af
- https://*.google.com.ag
- https://*.google.al
- https://*.google.am
- https://*.google.co.ao
- https://*.google.com.ar
- https://*.google.as
- https://*.google.at
- https://*.google.com.au
- https://*.google.az
- https://*.google.ba
- https://*.google.com.bd
- https://*.google.be
- https://*.google.bf
- https://*.google.bg
- https://*.google.com.bh
- https://*.google.bi
- https://*.google.bj
- https://*.google.com.bn
- https://*.google.com.bo
- https://*.google.com.br
- https://*.google.bs
- https://*.google.bt
- https://*.google.co.bw
- https://*.google.by
- https://*.google.com.bz
- https://*.google.ca
- https://*.google.cd
- https://*.google.cf
- https://*.google.cg
- https://*.google.ch
- https://*.google.ci
- https://*.google.co.ck
- https://*.google.cl
- https://*.google.cm
- https://*.google.cn
- https://*.google.com.co
- https://*.google.co.cr
- https://*.google.com.cu
- https://*.google.cv
- https://*.google.com.cy
- https://*.google.cz
- https://*.google.de
- https://*.google.dj
- https://*.google.dk
- https://*.google.dm
- https://*.google.com.do
- https://*.google.dz
- https://*.google.com.ec
- https://*.google.ee
- https://*.google.com.eg
- https://*.google.es
- https://*.google.com.et
- https://*.google.fi
- https://*.google.com.fj
- https://*.google.fm
- https://*.google.fr
- https://*.google.ga
- https://*.google.ge
- https://*.google.gg
- https://*.google.com.gh
- https://*.google.com.gi
- https://*.google.gl
- https://*.google.gm
- https://*.google.gr
- https://*.google.com.gt
- https://*.google.gy
- https://*.google.com.hk
- https://*.google.hn
- https://*.google.hr
- https://*.google.ht
- https://*.google.hu
- https://*.google.co.id
- https://*.google.ie
- https://*.google.co.il
- https://*.google.im
- https://*.google.co.in
- https://*.google.iq
- https://*.google.is
- https://*.google.it
- https://*.google.je
- https://*.google.com.jm
- https://*.google.jo
- https://*.google.co.jp
- https://*.google.co.ke
- https://*.google.com.kh
- https://*.google.ki
- https://*.google.kg
- https://*.google.co.kr
- https://*.google.com.kw
- https://*.google.kz
- https://*.google.la
- https://*.google.com.lb
- https://*.google.li
- https://*.google.lk
- https://*.google.co.ls
- https://*.google.lt
- https://*.google.lu
- https://*.google.lv
- https://*.google.com.ly
- https://*.google.co.ma
- https://*.google.md
- https://*.google.me
- https://*.google.mg
- https://*.google.mk
- https://*.google.ml
- https://*.google.com.mm
- https://*.google.mn
- https://*.google.com.mt
- https://*.google.mu
- https://*.google.mv
- https://*.google.mw
- https://*.google.com.mx
- https://*.google.com.my
- https://*.google.co.mz
- https://*.google.com.na
- https://*.google.com.ng
- https://*.google.com.ni
- https://*.google.ne
- https://*.google.nl
- https://*.google.no
- https://*.google.com.np
- https://*.google.nr
- https://*.google.nu
- https://*.google.co.nz
- https://*.google.com.om
- https://*.google.com.pa
- https://*.google.com.pe
- https://*.google.com.pg
- https://*.google.com.ph
- https://*.google.com.pk
- https://*.google.pl
- https://*.google.pn
- https://*.google.com.pr
- https://*.google.ps
- https://*.google.pt
- https://*.google.com.py
- https://*.google.com.qa
- https://*.google.ro
- https://*.google.ru
- https://*.google.rw
- https://*.google.com.sa
- https://*.google.com.sb
- https://*.google.sc
- https://*.google.se
- https://*.google.com.sg
- https://*.google.sh
- https://*.google.si
- https://*.google.sk
- https://*.google.com.sl
- https://*.google.sn
- https://*.google.so
- https://*.google.sm
- https://*.google.sr
- https://*.google.st
- https://*.google.com.sv
- https://*.google.td
- https://*.google.tg
- https://*.google.co.th
- https://*.google.com.tj
- https://*.google.tl
- https://*.google.tm
- https://*.google.tn
- https://*.google.to
- https://*.google.com.tr
- https://*.google.tt
- https://*.google.com.tw
- https://*.google.co.tz
- https://*.google.com.ua
- https://*.google.co.ug
- https://*.google.co.uk
- https://*.google.com.uy
- https://*.google.co.uz
- https://*.google.com.vc
- https://*.google.co.ve
- https://*.google.co.vi
- https://*.google.com.vn
- https://*.google.vu
- https://*.google.ws
- https://*.google.rs
- https://*.google.co.za
- https://*.google.co.zm
- https://*.google.co.zw
- https://*.google.cat
- mode: extend
directive: connect-src
sources:
- https://*.g.doubleclick.net
- https://*.google.com
- https://*.google.ad
- https://*.google.ae
- https://*.google.com.af
- https://*.google.com.ag
- https://*.google.al
- https://*.google.am
- https://*.google.co.ao
- https://*.google.com.ar
- https://*.google.as
- https://*.google.at
- https://*.google.com.au
- https://*.google.az
- https://*.google.ba
- https://*.google.com.bd
- https://*.google.be
- https://*.google.bf
- https://*.google.bg
- https://*.google.com.bh
- https://*.google.bi
- https://*.google.bj
- https://*.google.com.bn
- https://*.google.com.bo
- https://*.google.com.br
- https://*.google.bs
- https://*.google.bt
- https://*.google.co.bw
- https://*.google.by
- https://*.google.com.bz
- https://*.google.ca
- https://*.google.cd
- https://*.google.cf
- https://*.google.cg
- https://*.google.ch
- https://*.google.ci
- https://*.google.co.ck
- https://*.google.cl
- https://*.google.cm
- https://*.google.cn
- https://*.google.com.co
- https://*.google.co.cr
- https://*.google.com.cu
- https://*.google.cv
- https://*.google.com.cy
- https://*.google.cz
- https://*.google.de
- https://*.google.dj
- https://*.google.dk
- https://*.google.dm
- https://*.google.com.do
- https://*.google.dz
- https://*.google.com.ec
- https://*.google.ee
- https://*.google.com.eg
- https://*.google.es
- https://*.google.com.et
- https://*.google.fi
- https://*.google.com.fj
- https://*.google.fm
- https://*.google.fr
- https://*.google.ga
- https://*.google.ge
- https://*.google.gg
- https://*.google.com.gh
- https://*.google.com.gi
- https://*.google.gl
- https://*.google.gm
- https://*.google.gr
- https://*.google.com.gt
- https://*.google.gy
- https://*.google.com.hk
- https://*.google.hn
- https://*.google.hr
- https://*.google.ht
- https://*.google.hu
- https://*.google.co.id
- https://*.google.ie
- https://*.google.co.il
- https://*.google.im
- https://*.google.co.in
- https://*.google.iq
- https://*.google.is
- https://*.google.it
- https://*.google.je
- https://*.google.com.jm
- https://*.google.jo
- https://*.google.co.jp
- https://*.google.co.ke
- https://*.google.com.kh
- https://*.google.ki
- https://*.google.kg
- https://*.google.co.kr
- https://*.google.com.kw
- https://*.google.kz
- https://*.google.la
- https://*.google.com.lb
- https://*.google.li
- https://*.google.lk
- https://*.google.co.ls
- https://*.google.lt
- https://*.google.lu
- https://*.google.lv
- https://*.google.com.ly
- https://*.google.co.ma
- https://*.google.md
- https://*.google.me
- https://*.google.mg
- https://*.google.mk
- https://*.google.ml
- https://*.google.com.mm
- https://*.google.mn
- https://*.google.com.mt
- https://*.google.mu
- https://*.google.mv
- https://*.google.mw
- https://*.google.com.mx
- https://*.google.com.my
- https://*.google.co.mz
- https://*.google.com.na
- https://*.google.com.ng
- https://*.google.com.ni
- https://*.google.ne
- https://*.google.nl
- https://*.google.no
- https://*.google.com.np
- https://*.google.nr
- https://*.google.nu
- https://*.google.co.nz
- https://*.google.com.om
- https://*.google.com.pa
- https://*.google.com.pe
- https://*.google.com.pg
- https://*.google.com.ph
- https://*.google.com.pk
- https://*.google.pl
- https://*.google.pn
- https://*.google.com.pr
- https://*.google.ps
- https://*.google.pt
- https://*.google.com.py
- https://*.google.com.qa
- https://*.google.ro
- https://*.google.ru
- https://*.google.rw
- https://*.google.com.sa
- https://*.google.com.sb
- https://*.google.sc
- https://*.google.se
- https://*.google.com.sg
- https://*.google.sh
- https://*.google.si
- https://*.google.sk
- https://*.google.com.sl
- https://*.google.sn
- https://*.google.so
- https://*.google.sm
- https://*.google.sr
- https://*.google.st
- https://*.google.com.sv
- https://*.google.td
- https://*.google.tg
- https://*.google.co.th
- https://*.google.com.tj
- https://*.google.tl
- https://*.google.tm
- https://*.google.tn
- https://*.google.to
- https://*.google.com.tr
- https://*.google.tt
- https://*.google.com.tw
- https://*.google.co.tz
- https://*.google.com.ua
- https://*.google.co.ug
- https://*.google.co.uk
- https://*.google.com.uy
- https://*.google.co.uz
- https://*.google.com.vc
- https://*.google.co.ve
- https://*.google.co.vi
- https://*.google.com.vn
- https://*.google.vu
- https://*.google.ws
- https://*.google.rs
- https://*.google.co.za
- https://*.google.co.zm
- https://*.google.co.zw
- https://*.google.cat

View File

@ -0,0 +1,17 @@
mutations:
img:
mode: extend
directive: img-src
sources:
- https://*.analytics.google.com
- https://*.g.doubleclick.net
- https://*.google.com
- https://*.google.ie
connect:
mode: extend
directive: connect-src
sources:
- https://*.g.doubleclick.net
- https://*.google.com
- https://*.google.ie

View File

@ -0,0 +1,5 @@
mutations:
- mode: extend
directive: script-src
sources:
- https://www.google-analytics.com

View File

@ -0,0 +1,26 @@
mutations:
script:
mode: extend
directive: script-src
sources:
- https://tagmanager.google.com
style:
mode: extend
directive: style-src
sources:
- https://tagmanager.google.com
- https://fonts.googleapis.com
img:
mode: extend
directive: img-src
sources:
- https://ssl.gstatic.com
- https://www.gstatic.com
font:
mode: extend
directive: font-src
sources:
- https://fonts.gstatic.com

View File

@ -0,0 +1,6 @@
mutations:
img:
mode: extend
directive: img-src
sources:
- https://api.mapbox.com/

View File

@ -0,0 +1,16 @@
mutations:
- mode: extend
directive: script-src
sources:
- https://www.google-analytics.com
- https://ssl.google-analytics.com
- mode: extend
directive: img-src
sources:
- https://www.google-analytics.com
- mode: extend
directive: connect-src
sources:
- https://www.google-analytics.com

View File

@ -0,0 +1,12 @@
mutations:
frame:
mode: extend
directive: frame-src
sources:
- https://www.youtube-nocookie.com
img:
mode: extend
directive: img-src
sources:
- https://i.ytimg.com/

19
README.md Executable file
View File

@ -0,0 +1,19 @@
# ew-base
## Usercentrics Hook Guide
Add this snippet to your config/sites to enable the hook
```yaml
usercentrics:
-
id: LNxzTaK8j
version: loader
useBlocker: false
applicationContext: "Production/Staging\r\nProduction\r\nDevelopment\r\n"
```
```
id - Usercentrics Id
version (loader | main) - loader is the v2 and main is v1 in usercentrics context
useBlocker - if the content blocker script should be rendered
applicationContext - multiline string of application contexts that the snippets should be rendered in
```

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="messages" date="2023-02-07T14:19:01Z">
<header/>
<body>
<trans-unit id="rm.release">
<source>Release information</source>
</trans-unit>
<trans-unit id="rm.release_label-tag">
<source>Release tag</source>
</trans-unit>
<trans-unit id="rm.release_label-branch">
<source>Release branch</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="messages" date="2020-02-07T14:19:01Z">
<header/>
<body>
<trans-unit id="pages.content_uid">
<source>Content element to jump to</source>
</trans-unit>
<trans-unit id="imageField.empty">
<source>Please and an image in field '%1$s' and save the record before picking a color.</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file source-language="en" datatype="plaintext" original="messages" date="2020-02-07T14:19:01Z">
<header/>
<body>
<trans-unit id="site.usercentrics">
<source>UserCentrics</source>
</trans-unit>
<trans-unit id="site.usercentrics.description">
<source>Max one configuration per site</source>
</trans-unit>
<trans-unit id="site_usercentrics.ctrl.title">
<source>Usercentrics configuration</source>
</trans-unit>
<trans-unit id="site_usercentrics.column.id">
<source>ID</source>
</trans-unit>
<trans-unit id="site_usercentrics.column.id.description">
<source>ID defining what user centrics account to use</source>
</trans-unit>
<trans-unit id="site_usercentrics.column.version">
<source>Version</source>
</trans-unit>
<trans-unit id="site_usercentrics.column.applicationContext">
<source>Application Context</source>
</trans-unit>
<trans-unit id="site_usercentrics.column.useBlocker">
<source>use Blocker</source>
</trans-unit>
</body>
</file>
</xliff>

View File

@ -0,0 +1,49 @@
<html
xmlns:f="http://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers"
data-namespace-typo3-fluid="true">
<div class="formengine-field-item t3js-formengine-field-item">
{fieldInformation -> f:format.raw()}
<div class="form-control-wrap">
<div class="form-wizards-wrap">
<div class="form-wizards-element">
<typo3-formengine-element-pick-color recordFieldId="{formEngine.field.id}">
<div class="form-control-wrap" style="max-width: {width}px">
<input type="text" value="" id="{formEngine.field.id}" class="form-control t3js-clearable"
data-formengine-validation-rules="[]"
data-formengine-input-params="{f:format.json(value: {
field: formEngine.field.name,
evalList: 'trim'
})}"
data-formengine-input-name="{formEngine.field.name}"/>
<input type="hidden" name="{formEngine.field.name}" value="{formEngine.field.value -> f:format.htmlspecialchars()}" />
</div>
<div class="picker">
<div class="preview"></div>
<img src="{f:uri.image(image: image, maxWidth: width, cropVariant: config.cropVariants.0.id)}" alt=""
class="picker-thumbnail thumbnail"
style="max-width: {width}px; {f:if(condition: formEngine.field.value, then: 'background: {formEngine.field.value -> f:format.htmlspecialchars()}')}"/>
<canvas id="{formEngine.field.id}-cs" class="pick-color-canvas"></canvas>
</div>
</typo3-formengine-element-pick-color>
</div>
<div class="form-wizards-items-aside form-wizards-items-aside--field-control">
<div class="btn-group">
{fieldControl -> f:format.raw()}
</div>
</div>
<div class="form-wizards-items-bottom">
{fieldWizard -> f:format.raw()}
</div>
</div>
</div>
</div>
</html>

View File

@ -0,0 +1,12 @@
<html xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers" data-namespace-typo3-fluid="true">
<h3 class="dropdown-headline"><f:translate id="LLL:EXT:ew_base/Resources/Private/Language/locallang_core.xlf:rm.release" /></h3>
<table class="dropdown-table">
<tr>
<th data-type="icon"><core:icon identifier="information-git" size="small" /></th>
<th data-type="title"><f:translate id="LLL:EXT:ew_base/Resources/Private/Language/locallang_core.xlf:rm.release_label-{f:if(condition: release.isTag, then: 'tag', else: 'branch')}" /></th>
<td data-type="value" class="text-{f:if(condition: release.isTag, then: 'success', else: 'warning')}">{release.release -> f:format.htmlentities()}</td>
</tr>
</table>
</html>

View File

@ -0,0 +1,8 @@
<html xmlns:core="http://typo3.org/ns/TYPO3/CMS/Core/ViewHelpers" data-namespace-typo3-fluid="true">
<f:render partial="ToolbarItems/ToolbarItem" arguments="{
title: 'LLL:EXT:ew_base/Resources/Private/Language/locallang_core.xlf:rm.release',
icon: '{core:icon(identifier: \'ew-base-extension-small\', size: \'small\', alternativeMarkupIdentifier: \'inline\')}'
}" />
</html>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg id="svg2" xmlns="http://www.w3.org/2000/svg" height="9in" width="9in" version="1.1" viewBox="0 0 640 640">
<path id="Auswahl1" d="m4.73 377l-4.73-45c-1.17-100.04 31.51-196.72 110-262.56 42.12-35.33 96.7-58.01 151-65.57l36-3.87h49s38 4.58 38 4.58c35.73 5.54 72.58 17.87 104 35.85 53.09 30.37 91.35 72.89 118.24 127.57 23.12 47.01 33.76 98.83 33.76 151h-555l1 18c0.07 13.74 5.29 37.39 8.88 51l3.12 11-81 29c-5.4-12.32-10.25-37.27-12.27-51zm489.43-221s-8.16-8.91-8.16-8.91c-34.3-32.33-78.39-52.53-125-58.81-9.87-1.33-20.05-2.26-30-2.28l-11-0.66s-17 0.66-17 0.66l-19 1.84c-35.79 3.82-69.99 15.91-100 35.83-30.13 20-55.03 47.97-71.25 80.33-4.56 9.1-12.24 25.24-13.75 35h443c-10.63-31.88-25.41-57.89-47.84-83z" fill="#87b200"/>
<path id="Auswahl2" d="m143.72 481c12.28 14.12 25.74 25.64 41.28 36 3.08 2.05 12.54 8.89 16 8.28 2.62-0.46 6.12-4.4 8-6.28l15.09-16s56.91-57 56.91-57l29-29c2.36-2.36 7.37-8.57 11-7.66 2.27 0.56 6.26 4.92 8 6.66l18 18 64 64 21 21c2.17 2.17 5.77 7.05 9.17 6.3 1.5-0.33 7.18-3.9 8.83-4.9 8.04-4.85 15.75-10.51 23-16.48 30.57-25.21 51.43-56.38 66-92.92 4.35-10.9 8.34-24.51 10.79-36 0.59-2.78 1.84-12.51 3.8-13.96 2.4-1.78 13.18 0.83 16.41 1.37 0 0 47 8.18 47 8.18 3.47 0.58 16.03 1.63 17.36 4.72 1.15 2.68-2.21 17.15-3.01 20.69-4.17 18.44-9.52 36.44-16.55 54-27.05 67.63-74.22 122.45-139.8 155.25-10.16 5.07-28.35 13.19-39 16.4-7.8 2.36-9.68-1.34-15-6.65l-15-15s-63-64-63-64l-16-16c-1.63-1.6-4.58-4.96-7-4.96-2.73 0-7.1 5.06-9 6.96l-20 20s-16.01 17-16.01 17-37.99 38-37.99 38l-25 24.36c-4.38 2.57-10.64-0.84-15-2.36-10.8-3.75-27.07-10.98-37-16.58-39.58-22.32-67.36-44.68-94.87-81.42-6.36-8.48-21.15-29.57-24.13-39l49-28.42 25-13.58c8.24 15.58 17.18 27.74 28.72 41z" fill="#4f660b"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
<rect width="100%" height="100%" class="icon-color"/>
<path fill="#2F2F2F" d="M 7.523438 1.125 L 9.050781 1.125 L 5.175781 14.875 L 3.675781 14.875 Z M 7.523438 1.125 "/>
<path fill="#2F2F2F" d="M 10.550781 1.125 L 12.074219 1.125 L 8.199219 14.875 L 6.699219 14.875 Z M 10.550781 1.125 "/>
</svg>

After

Width:  |  Height:  |  Size: 413 B

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="layer" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 176 176" style="enable-background:new 0 0 176 176;" xml:space="preserve">
<style type="text/css">
.st0{fill:#0095FF;}
.st1{fill:#B8CEE1;}
.st2{fill:#0D47A1;}
.st3{fill:#F25900;}
</style>
<circle class="st0" cx="90.9" cy="88.7" r="26.4"/>
<circle class="st1" cx="145.1" cy="35.4" r="10.2"/>
<circle class="st2" cx="124.8" cy="132" r="18.3"/>
<circle class="st3" cx="34.8" cy="110.9" r="14.2"/>
</svg>

After

Width:  |  Height:  |  Size: 677 B

View File

@ -0,0 +1,9 @@
.pick-color-canvas {
display: none;
}
.picker-thumbnail {
cursor: crosshair;
width: 100%;
border-top: solid 20px transparent;
}

View File

@ -0,0 +1,83 @@
/*
* This file is developed by evoWeb.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*/
export class PickColorFromImage {
constructor(fieldId, value, readOnly) {
this.value = value;
this.readOnly = readOnly;
this.image = document.querySelector(`[recordFieldId="${fieldId}"] img`);
this.canvas = document.querySelector(`#${fieldId}-cs`);
this.result = document.querySelector(`#${fieldId}`);
this.initializeEvents();
this.updateCanvas();
}
initializeEvents() {
if (!this.readOnly) {
this.image.addEventListener('resize', event => this.imageResized(event));
this.image.addEventListener('click', event => this.imageClicked(event));
this.image.addEventListener('mousemove', event => this.imageMousemove(event));
this.image.addEventListener('mouseout', event => this.imageMouseout(event));
}
}
imageResized() {
this.updateCanvas();
}
imageClicked(event) {
this.value = this.getColorForCoordinates(this.getEventCoordinates(event));
this.result.value = this.value;
this.result.dispatchEvent(new Event('change', {bubbles: !0}));
}
imageMousemove(event) {
this.image.style.borderTopColor = this.getColorForCoordinates(this.getEventCoordinates(event));
}
imageMouseout() {
this.image.style.borderTopColor = this.value;
}
getEventCoordinates(event) {
let x, y;
if (event.offsetX) {
x = event.offsetX;
y = event.offsetY;
} else if (event.layerX) {
x = event.layerX;
y = event.layerY;
}
return { x: x, y: y };
}
getColorForCoordinates(coordinates) {
let p = this.canvas.getContext('2d').getImageData(coordinates.x, coordinates.y, 1, 1).data;
return this.rgbToHex(p[0], p[1], p[2]);
}
updateCanvas() {
this.canvas.width = this.image.width;
this.canvas.height = this.image.height;
this.canvas.getContext('2d').drawImage(this.image, 0, 0, this.image.width, this.image.height);
}
rgbToHex(r, g, b) {
return '#' + this.componentToHex(r) + this.componentToHex(g) + this.componentToHex(b);
}
componentToHex(c) {
let hex = parseInt(c, 10).toString(16);
return hex.length === 1 ? '0' + hex : hex;
}
}

47
composer.json Executable file
View File

@ -0,0 +1,47 @@
{
"name": "evoweb/ew-base",
"type": "typo3-cms-extension",
"version": "1.0.0",
"autoload": {
"psr-4": {
"Evoweb\\EwBase\\": "Classes/"
}
},
"require": {
"typo3/cms-core": "^11.5 || ^12.4 || dev-main",
"typo3/cms-backend": "*",
"typo3/cms-extbase": "*",
"typo3/cms-extensionmanager": "*",
"typo3/cms-filelist": "*",
"typo3/cms-fluid": "*",
"typo3/cms-frontend": "*",
"typo3/cms-install": "*",
"typo3/cms-recordlist": "*",
"typo3/cms-adminpanel": "*",
"typo3/cms-belog": "*",
"typo3/cms-beuser": "*",
"typo3/cms-felogin": "*",
"typo3/cms-fluid-styled-content": "*",
"typo3/cms-form": "*",
"typo3/cms-info": "*",
"typo3/cms-lowlevel": "*",
"typo3/cms-recycler": "*",
"typo3/cms-reports": "*",
"typo3/cms-rte-ckeditor": "*",
"typo3/cms-seo": "*",
"typo3/cms-setup": "*",
"typo3/cms-t3editor": "*",
"typo3/cms-tstemplate": "*",
"typo3/cms-scheduler": "*",
"helhum/typo3-console": "*",
"clickstorm/cs-seo": "*"
},
"extra": {
"typo3/cms": {
"extension-key": "ew_base"
}
}
}

2
ext_conf_template.txt Normal file
View File

@ -0,0 +1,2 @@
# cat=basic//10; type=boolean; label= Weather the content of css files gets inserted to the source code of the page as inline css
inlineCssStyles = 0

67
ext_localconf.php Executable file
View File

@ -0,0 +1,67 @@
<?php
defined('TYPO3') or die('access denied');
use Evoweb\EwBase\Form\Element\PickColorFromImage;
use Evoweb\EwBase\Form\FormDataProvider\UsercentricsDatabaseEditRow;
use Evoweb\EwBase\Form\FormDataProvider\UsercentricsTcaInline;
use Evoweb\EwBase\Hooks\UsercentricsPostRenderHook;
use Evoweb\EwBase\Xclass\SiteDatabaseEditRow;
use Evoweb\EwBase\Xclass\SiteTcaInline;
use TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseParentPageRow;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteDatabaseEditRow as BaseSiteDatabaseEditRow;
use TYPO3\CMS\Backend\Form\FormDataProvider\SiteTcaInline as BaseSiteTcaInline;
use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSiteLanguage;
call_user_func(function () {
$GLOBALS['TYPO3_CONF_VARS']['SYS']['fluid']['namespaces']['ewb'] = [ 'Evoweb\\EwBase\\ViewHelpers' ];
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_pagerenderer.php']['render-postProcess'][] =
UsercentricsPostRenderHook::class . '->executePostRenderHook';
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [
'nodeName' => 'pick-color-from-image',
'priority' => '70',
'class' => PickColorFromImage::class,
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteDatabaseEditRow::class] = [
'className' => SiteDatabaseEditRow::class
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][BaseSiteTcaInline::class] = [
'className' => SiteTcaInline::class
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['siteConfiguration'][
UsercentricsDatabaseEditRow::class
] = [
'depends' => [ BaseSiteDatabaseEditRow::class ],
'before' => [ DatabaseParentPageRow::class ],
];
$GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['formDataGroup']['siteConfiguration'][
UsercentricsTcaInline::class
] = [
'depends' => [ BaseSiteTcaInline::class ],
'before' => [ TcaSiteLanguage::class ],
];
if (empty($GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics'] ?? [])) {
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['loader']['preload'] =
'<link rel="preconnect" href="//app.usercentrics.eu">
<link rel="preconnect" href="//privacy-proxy.usercentrics.eu">
<link rel="preload" href="//app.usercentrics.eu/browser-ui/latest/loader.js" as="script">';
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['loader']['template'] =
'<script src="//app.usercentrics.eu/browser-ui/latest/loader.js" data-settings-id="###ID###" id="usercentrics-cmp" async></script>';
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['main']['preload'] =
'<link rel="preconnect" href="//app.usercentrics.eu">
<link rel="preconnect" href="//api.usercentrics.eu">
<link rel="preconnect" href="//privacy-proxy.usercentrics.eu">
<link rel="preload" href="//app.usercentrics.eu/latest/main.js" as="script">';
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['main']['template'] =
'<script src="//app.usercentrics.eu/latest/main.js" id="###ID###" type="application/javascript"></script>';
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['block']['preload'] =
'<link rel="preload" href="//privacy-proxy.usercentrics.eu/latest/uc-block.bundle.js" as="script">';
$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['ew_base']['userCentrics']['block']['template'] =
'<script type="application/javascript" src="https://privacy-proxy.usercentrics.eu/latest/uc-block.bundle.js"></script>';
}
});

7
ext_tables.sql Executable file
View File

@ -0,0 +1,7 @@
#
# Table structure for table 'pages'
#
CREATE TABLE pages
(
sectionIndex_uid int(11) unsigned DEFAULT '0' NOT NULL
);