diff --git a/.gitattributes b/.gitattributes index c1108e7..290f6c5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ -/.github/ export-ignore -/Build/ export-ignore -/Tests/ export-ignore +/.github export-ignore +/Build export-ignore +/Tests export-ignore /.gitattributes export-ignore /.gitignore export-ignore +/.gitlab-ci.yml export-ignore diff --git a/Classes/Command/ContentElementCommand.php b/Classes/Command/ContentElementCommand.php index 687e76d..544fcb2 100644 --- a/Classes/Command/ContentElementCommand.php +++ b/Classes/Command/ContentElementCommand.php @@ -1,6 +1,6 @@ setAliases(['kc-sitepackage']) diff --git a/Classes/Configuration/AdditionalConfiguration.php b/Classes/Configuration/AdditionalConfiguration.php index e6b6165..7998e86 100644 --- a/Classes/Configuration/AdditionalConfiguration.php +++ b/Classes/Configuration/AdditionalConfiguration.php @@ -2,44 +2,22 @@ declare(strict_types=1); -namespace Evoweb\EwBase\Configuration; - /* - * This file is part of TYPO3 CMS-based extension "container" by b13. + * This file is part of TYPO3 CMS-based extension "ew-base" 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. */ -use Mfc\OAuth2\ResourceServer\GitLab; -use Mfc\OAuth2\ResourceServer\Registry; +namespace Evoweb\EwBase\Configuration; + 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, @@ -70,11 +48,11 @@ class AdditionalConfiguration /** * This is needed to override all mail settings instead of merging them. * - * @var array|string[] + * @var array */ protected array $mailConfig = [ 'transport' => 'smtp', - 'transport_smtp_server' => '127.0.0.1:1025' + 'transport_smtp_server' => 'mailpit:1025' ]; public function initialize(array $configuration = []): void diff --git a/Classes/DataProcessing/ContainerProcessor.php b/Classes/DataProcessing/ContainerProcessor.php deleted file mode 100644 index 6d48d2a..0000000 --- a/Classes/DataProcessing/ContainerProcessor.php +++ /dev/null @@ -1,58 +0,0 @@ -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; - } -} diff --git a/Classes/DataProcessing/DatabaseQueryProcessor.php b/Classes/DataProcessing/DatabaseQueryProcessor.php index 057ec66..7f6d5b9 100644 --- a/Classes/DataProcessing/DatabaseQueryProcessor.php +++ b/Classes/DataProcessing/DatabaseQueryProcessor.php @@ -19,15 +19,16 @@ namespace Evoweb\EwBase\DataProcessing; use Doctrine\DBAL\Exception as DBALException; use Doctrine\DBAL\Result; -use TYPO3\CMS\Backend\FrontendBackendUserAuthentication; +use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Context\Context; +use TYPO3\CMS\Core\Context\Exception\AspectNotFoundException; use TYPO3\CMS\Core\Context\LanguageAspect; +use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression; use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder; -use TYPO3\CMS\Core\Database\Query\QueryBuilder; use TYPO3\CMS\Core\Database\Query\QueryHelper; -use TYPO3\CMS\Core\Database\Query\Restriction\DocumentTypeExclusionRestriction; -use TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer; +use TYPO3\CMS\Core\Domain\Repository\PageRepository; use TYPO3\CMS\Core\TimeTracker\TimeTracker; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; @@ -35,7 +36,7 @@ use TYPO3\CMS\Core\Versioning\VersionState; use TYPO3\CMS\Frontend\ContentObject\ContentDataProcessor; use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; use TYPO3\CMS\Frontend\ContentObject\DataProcessorInterface; -use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3\CMS\Frontend\ContentObject\Exception\ContentRenderingException; /** * Fetch records from the database, using the default .select syntax from TypoScript. @@ -63,17 +64,13 @@ use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; */ class DatabaseQueryProcessor implements DataProcessorInterface { - protected ContentDataProcessor $contentDataProcessor; - protected ContentObjectRenderer $cObj; - /** - * Constructor - */ - public function __construct() - { - $this->contentDataProcessor = GeneralUtility::makeInstance(ContentDataProcessor::class); - } + protected ?ServerRequestInterface $request = null; + + protected string $tableName = ''; + + public function __construct(protected readonly ContentDataProcessor $contentDataProcessor) {} /** * Fetches records from the database as an array @@ -84,14 +81,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface * @param array $processedData Key/value store of processed data (e.g. to be passed to a Fluid View) * * @return array the processed data as key/value store + * @throws ContentRenderingException */ public function process( ContentObjectRenderer $cObj, array $contentObjectConfiguration, array $processorConfiguration, array $processedData - ) { - $this->cObj = $cObj; + ): array { if (isset($processorConfiguration['if.']) && !$cObj->checkIf($processorConfiguration['if.'])) { return $processedData; } @@ -108,6 +105,13 @@ class DatabaseQueryProcessor implements DataProcessorInterface unset($processorConfiguration['table']); } + $this->request = $cObj->getRequest(); + $this->tableName = $tableName; + $this->cObj = clone $cObj; + // @extensionScannerIgnoreLine + $this->cObj->start($cObj->data, $tableName); + $this->cObj->setRequest($this->request); + // The variable to be used within the result $targetVariableName = $cObj->stdWrapValue('as', $processorConfiguration, 'records'); @@ -116,8 +120,10 @@ class DatabaseQueryProcessor implements DataProcessorInterface $request = $cObj->getRequest(); $processedRecordVariables = []; foreach ($records as $key => $record) { + /** @var ContentObjectRenderer $recordContentObjectRenderer */ $recordContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class); - $recordContentObjectRenderer->start($record, $tableName, $request); + $recordContentObjectRenderer->setRequest($request); + $recordContentObjectRenderer->start($record, $tableName); $processedRecordVariables[$key] = ['data' => $record]; $processedRecordVariables[$key] = $this->contentDataProcessor->process( $recordContentObjectRenderer, @@ -137,14 +143,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface $statement = $this->exec_getQuery($tableName, $queryConfiguration); - $tsfe = $this->getTypoScriptFrontendController(); + $pageRepository = $this->getPageRepository(); while ($row = $statement->fetchAssociative()) { // Versioning preview: - $tsfe->sys_page->versionOL($tableName, $row, true); + $pageRepository->versionOL($tableName, $row, true); // Language overlay: if (is_array($row)) { - $row = $tsfe->sys_page->getLanguageOverlay($tableName, $row); + $row = $pageRepository->getLanguageOverlay($tableName, $row); } // Might be unset in the language overlay @@ -158,16 +164,15 @@ class DatabaseQueryProcessor implements DataProcessorInterface protected function exec_getQuery(string $table, array $conf): Result { - $statement = $this->getQuery($table, $conf); $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $statement = $this->getQuery($connection, $table, $conf); return $connection->executeQuery($statement); } - public function getQuery($table, $conf, $returnQueryArray = false) + public function getQuery(Connection $connection, $table, $conf): string { // Resolve stdWrap in these properties first - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); $properties = [ 'pidInList', 'uidInList', @@ -200,7 +205,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface } } // Handle PDO-style named parameter markers first - $queryMarkers = $this->getQueryMarkers($table, $conf); + $queryMarkers = $this->cObj->getQueryMarkers($connection, $conf); // Replace the markers in the non-stdWrap properties foreach ($queryMarkers as $marker => $markerValue) { $properties = [ @@ -217,7 +222,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface ]; foreach ($properties as $property) { if ($conf[$property] ?? false) { - $conf[$property] = str_replace('###' . $marker . '###', (string)$markerValue, $conf[$property]); + $conf[$property] = str_replace('###' . $marker . '###', $markerValue, $conf[$property]); } } } @@ -230,10 +235,11 @@ class DatabaseQueryProcessor implements DataProcessorInterface $pidList = GeneralUtility::trimExplode(',', $conf['pidInList'], true); array_walk($pidList, function (&$storagePid) { if ($storagePid === 'this') { - $storagePid = $this->getTypoScriptFrontendController()->id; + $storagePid = $this->getRequest()->getAttribute('frontend.page.information')->getId(); } }); - $expandedPidList = $this->getTypoScriptFrontendController()->sys_page->getPageIdsRecursive($pidList, $conf['recursive']); + $pageRepository = $this->getPageRepository(); + $expandedPidList = $pageRepository->getPageIdsRecursive($pidList, $conf['recursive']); $conf['pidInList'] = implode(',', $expandedPidList); } } @@ -241,7 +247,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface $conf['pidInList'] = 'this'; } - $queryParts = $this->getQueryConstraints($table, $conf); + $queryParts = $this->getQueryConstraints($connection, $table, $conf); $queryBuilder = $connection->createQueryBuilder(); // @todo Check against getQueryConstraints, can probably use FrontendRestrictions @@ -253,7 +259,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface $queryBuilder->where($queryParts['where']); } - if ($queryParts['groupBy'] ?? false) { + if (($queryParts['groupBy'] ?? false) && is_array($queryParts['groupBy'])) { $queryBuilder->groupBy(...$queryParts['groupBy']); } @@ -265,15 +271,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface // Fields: if ($conf['selectFields'] ?? false) { - $queryBuilder->selectLiteral($this->sanitizeSelectPart($conf['selectFields'], $table)); + $queryBuilder->selectLiteral($this->sanitizeSelectPart($connection, $conf['selectFields'], $table)); } // Setting LIMIT: - $error = false; if (($conf['max'] ?? false) || ($conf['begin'] ?? false)) { // Finding the total number of records, if used: if (str_contains(strtolower(($conf['begin'] ?? '') . ($conf['max'] ?? '')), 'total')) { - $countQueryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $countQueryBuilder = $connection->createQueryBuilder(); $countQueryBuilder->getRestrictions()->removeAll(); $countQueryBuilder->count('*') ->from($table) @@ -293,237 +298,77 @@ class DatabaseQueryProcessor implements DataProcessorInterface } } catch (DBALException $e) { $this->getTimeTracker()->setTSlogMessage($e->getPrevious()->getMessage()); - $error = true; + return ''; } } - if (!$error) { - if (isset($conf['begin']) && $conf['begin'] > 0) { - $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['begin'])), 0); - $queryBuilder->setFirstResult($conf['begin']); - } - if (isset($conf['max'])) { - $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->calc($conf['max'])), 0); - $queryBuilder->setMaxResults($conf['max'] ?: 100000); - } + if (isset($conf['begin']) && $conf['begin'] > 0) { + $conf['begin'] = MathUtility::forceIntegerInRange((int)ceil($this->cObj->calc($conf['begin'])), 0); + $queryBuilder->setFirstResult($conf['begin']); + } + if (isset($conf['max'])) { + $conf['max'] = MathUtility::forceIntegerInRange((int)ceil($this->cObj->calc($conf['max'])), 0); + $queryBuilder->setMaxResults($conf['max'] ?: 100000); } } - if (!$error) { - // Setting up tablejoins: - if ($conf['join'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['join']); - $queryBuilder->join( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } elseif ($conf['leftjoin'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['leftjoin']); - $queryBuilder->leftJoin( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } elseif ($conf['rightjoin'] ?? false) { - $joinParts = QueryHelper::parseJoin($conf['rightjoin']); - $queryBuilder->rightJoin( - $table, - $joinParts['tableName'], - $joinParts['tableAlias'], - $joinParts['joinCondition'] - ); - } - - // Convert the QueryBuilder object into a SQL statement. - $query = $queryBuilder->getSQL(); - - // Replace the markers in the queryParts to handle stdWrap enabled properties - foreach ($queryMarkers as $marker => $markerValue) { - // @todo Ugly hack that needs to be cleaned up, with the current architecture - // @todo for exec_Query / getQuery it's the best we can do. - $query = str_replace('###' . $marker . '###', (string)$markerValue, $query); - } - - return $returnQueryArray ? $this->getQueryArray($queryBuilder) : $query; + // Setting up tablejoins: + if ($conf['join'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['join']); + $queryBuilder->join( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['leftjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['leftjoin']); + $queryBuilder->leftJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); + } elseif ($conf['rightjoin'] ?? false) { + $joinParts = QueryHelper::parseJoin($conf['rightjoin']); + $queryBuilder->rightJoin( + $table, + $joinParts['tableName'], + $joinParts['tableAlias'], + $joinParts['joinCondition'] + ); } - return ''; - } + // Convert the QueryBuilder object into a SQL statement. + $query = $queryBuilder->getSQL(); - /** - * Helper to transform a QueryBuilder object into a queryParts array that can be used - * with exec_SELECT_queryArray - * - * @return array - * @throws \RuntimeException - */ - protected function getQueryArray(QueryBuilder $queryBuilder) - { - $fromClauses = []; - $knownAliases = []; - $queryParts = []; - - // Loop through all FROM clauses - foreach ($queryBuilder->getQueryPart('from') as $from) { - if ($from['alias'] === null) { - $tableSql = $from['table']; - $tableReference = $from['table']; - } else { - $tableSql = $from['table'] . ' ' . $from['alias']; - $tableReference = $from['alias']; - } - - $knownAliases[$tableReference] = true; - - $fromClauses[$tableReference] = $tableSql . $this->getQueryArrayJoinHelper( - $tableReference, - $queryBuilder->getQueryPart('join'), - $knownAliases - ); + // Replace the markers in the queryParts to handle stdWrap enabled properties + foreach ($queryMarkers as $marker => $markerValue) { + // @todo Ugly hack that needs to be cleaned up, with the current architecture + // @todo for exec_Query / getQuery it's the best we can do. + $query = str_replace('###' . $marker . '###', (string)$markerValue, $query); } - $queryParts['SELECT'] = implode(', ', $queryBuilder->getQueryPart('select')); - $queryParts['FROM'] = implode(', ', $fromClauses); - $queryParts['WHERE'] = (string)$queryBuilder->getQueryPart('where') ?: ''; - $queryParts['GROUPBY'] = implode(', ', $queryBuilder->getQueryPart('groupBy')); - $queryParts['ORDERBY'] = implode(', ', $queryBuilder->getQueryPart('orderBy')); - if ($queryBuilder->getFirstResult() > 0) { - $queryParts['LIMIT'] = $queryBuilder->getFirstResult() . ',' . $queryBuilder->getMaxResults(); - } elseif ($queryBuilder->getMaxResults() > 0) { - $queryParts['LIMIT'] = $queryBuilder->getMaxResults(); - } - - return $queryParts; - } - - /** - * Helper to transform the QueryBuilder join part into a SQL fragment. - * - * @throws \RuntimeException - */ - protected function getQueryArrayJoinHelper(string $fromAlias, array $joinParts, array &$knownAliases): string - { - $sql = ''; - - if (isset($joinParts['join'][$fromAlias])) { - foreach ($joinParts['join'][$fromAlias] as $join) { - if (array_key_exists($join['joinAlias'], $knownAliases)) { - throw new \RuntimeException( - 'Non unique join alias: "' . $join['joinAlias'] . '" found.', - 1472748872 - ); - } - $sql .= ' ' . strtoupper($join['joinType']) - . ' JOIN ' . $join['joinTable'] . ' ' . $join['joinAlias'] - . ' ON ' . ((string)$join['joinCondition']); - $knownAliases[$join['joinAlias']] = true; - } - - foreach ($joinParts['join'][$fromAlias] as $join) { - $sql .= $this->getQueryArrayJoinHelper($join['joinAlias'], $joinParts, $knownAliases); - } - } - - return $sql; - } - - /** - * Builds list of marker values for handling PDO-like parameter markers in select parts. - * Marker values support stdWrap functionality thus allowing a way to use stdWrap functionality in various - * properties of 'select' AND prevents SQL-injection problems by quoting and escaping of numeric values, strings, - * NULL values and comma separated lists. - * - * @param string $table Table to select records from - * @param array $conf Select part of CONTENT definition - * @return array List of values to replace markers with - * @internal - * @see getQuery() - */ - public function getQueryMarkers(string $table, array $conf): array - { - if (!isset($conf['markers.']) || !is_array($conf['markers.'])) { - return []; - } - // Parse markers and prepare their values - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); - $markerValues = []; - foreach ($conf['markers.'] as $dottedMarker => $dummy) { - $marker = rtrim($dottedMarker, '.'); - if ($dottedMarker != $marker . '.') { - continue; - } - // Parse definition - // todo else value is always null - $tempValue = isset($conf['markers.'][$dottedMarker]) - ? $this->cObj->stdWrap($conf['markers.'][$dottedMarker]['value'] ?? '', $conf['markers.'][$dottedMarker]) - : $conf['markers.'][$dottedMarker]['value']; - // Quote/escape if needed - if (is_numeric($tempValue)) { - if ((int)$tempValue == $tempValue) { - // Handle integer - $markerValues[$marker] = (int)$tempValue; - } else { - // Handle float - $markerValues[$marker] = (float)$tempValue; - } - } elseif ($tempValue === null) { - // It represents NULL - $markerValues[$marker] = 'NULL'; - } elseif (!empty($conf['markers.'][$dottedMarker]['commaSeparatedList'])) { - // See if it is really a comma separated list of values - $explodeValues = GeneralUtility::trimExplode(',', $tempValue); - if (count($explodeValues) > 1) { - // Handle each element of list separately - $tempArray = []; - foreach ($explodeValues as $listValue) { - if (is_numeric($listValue)) { - if ((int)$listValue == $listValue) { - $tempArray[] = (int)$listValue; - } else { - $tempArray[] = (float)$listValue; - } - } else { - // If quoted, remove quotes before - // escaping. - if (preg_match('/^\'([^\']*)\'$/', $listValue, $matches)) { - $listValue = $matches[1]; - } elseif (preg_match('/^\\"([^\\"]*)\\"$/', $listValue, $matches)) { - $listValue = $matches[1]; - } - $tempArray[] = $connection->quote($listValue); - } - } - $markerValues[$marker] = implode(',', $tempArray); - } else { - // Handle remaining values as string - $markerValues[$marker] = $connection->quote($tempValue); - } - } else { - // Handle remaining values as string - $markerValues[$marker] = $connection->quote($tempValue); - } - } - return $markerValues; + return $query; } /** * Helper function for getQuery(), creating the WHERE clause of the SELECT query * + * @param Connection $connection * @param string $table The table name * @param array $conf The TypoScript configuration properties * @return array Associative array containing the prepared data for WHERE, ORDER BY and GROUP BY fragments - * @throws \InvalidArgumentException + * @throws AspectNotFoundException + * @throws ContentRenderingException * @see getQuery() */ - protected function getQueryConstraints(string $table, array $conf): array + protected function getQueryConstraints(Connection $connection, string $table, array $conf): array { - // Init: - $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($table); + $queryBuilder = $connection->createQueryBuilder(); $expressionBuilder = $queryBuilder->expr(); - $tsfe = $this->getTypoScriptFrontendController(); + $request = $this->getRequest(); + $contentPid = $request->getAttribute('frontend.page.information')->getContentFromPid(); $constraints = []; $pid_uid_flag = 0; $enableFieldsIgnore = []; @@ -540,16 +385,16 @@ class DatabaseQueryProcessor implements DataProcessorInterface ); if (trim($conf['uidInList'] ?? '')) { - $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['uidInList'])); + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['uidInList'])); // If moved records shall be considered, select via t3ver_oid if ($considerMovePointers) { - $constraints[] = (string)$expressionBuilder->orX( + $constraints[] = (string)$expressionBuilder->or( $expressionBuilder->in($table . '.uid', $listArr), - $expressionBuilder->andX( + $expressionBuilder->and( $expressionBuilder->eq( $table . '.t3ver_state', - (int)(string)VersionState::cast(VersionState::MOVE_POINTER) + VersionState::MOVE_POINTER->value ), $expressionBuilder->in($table . '.t3ver_oid', $listArr) ) @@ -561,14 +406,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface } // Static_* tables are allowed to be fetched from root page - if (strpos($table, 'static_') === 0) { + if (str_starts_with($table, 'static_')) { $pid_uid_flag++; } if (trim($conf['pidInList'])) { - $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$tsfe->contentPid, $conf['pidInList'])); + $listArr = GeneralUtility::intExplode(',', str_replace('this', (string)$contentPid, $conf['pidInList'])); // Removes all pages which are not visible for the user! - $listArr = $this->checkPidArray($listArr); + $listArr = $this->cObj->checkPidArray($listArr); if (GeneralUtility::inList($conf['pidInList'], 'root')) { $listArr[] = 0; } @@ -586,11 +431,11 @@ class DatabaseQueryProcessor implements DataProcessorInterface } // If not uid and not pid then uid is set to 0 - which results in nothing!! - if (!$pid_uid_flag && trim($conf['pidInList'] ?? '') != 'ignore') { + if (!$pid_uid_flag && trim($conf['pidInList'] ?? '') !== 'ignore') { $constraints[] = $expressionBuilder->eq($table . '.uid', 0); } - $where = trim((string)$this->cObj->stdWrapValue('where', $conf ?? [])); + $where = trim((string)$this->cObj->stdWrapValue('where', $conf)); if ($where) { $constraints[] = QueryHelper::stripLogicalOperatorPrefix($where); } @@ -602,26 +447,25 @@ class DatabaseQueryProcessor implements DataProcessorInterface $constraints[] = $languageConstraint; } - // Enablefields - if ($table === 'pages') { - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_hid_del); - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->where_groupAccess); - } else { - $constraints[] = QueryHelper::stripLogicalOperatorPrefix($tsfe->sys_page->enableFields($table, -1, $enableFieldsIgnore)); - } + // default constraints from TCA + $pageRepository = $this->getPageRepository(); + $constraints = array_merge( + $constraints, + array_values($pageRepository->getDefaultConstraints($table, $enableFieldsIgnore)) + ); // MAKE WHERE: - if (count($constraints) !== 0) { - $queryParts['where'] = $expressionBuilder->andX(...$constraints); + if ($constraints !== []) { + $queryParts['where'] = $expressionBuilder->and(...$constraints); } // GROUP BY - $groupBy = trim((string)$this->cObj->stdWrapValue('groupBy', $conf ?? [])); + $groupBy = trim((string)$this->cObj->stdWrapValue('groupBy', $conf)); if ($groupBy) { $queryParts['groupBy'] = QueryHelper::parseGroupBy($groupBy); } // ORDER BY - $orderByString = trim((string)$this->cObj->stdWrapValue('orderBy', $conf ?? [])); + $orderByString = trim((string)$this->cObj->stdWrapValue('orderBy', $conf)); if ($orderByString) { $queryParts['orderBy'] = QueryHelper::parseOrderBy($orderByString); } @@ -630,26 +474,6 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $queryParts; } - /** - * Removes Page UID numbers from the input array which are not available due to enableFields() or the list of bad doktype numbers ($this->checkPid_badDoktypeList) - * - * @param int[] $pageIds Array of Page UID numbers for select and for which pages with enablefields and bad doktypes should be removed. - * @return array Returns the array of remaining page UID numbers - * @internal - */ - public function checkPidArray(array $pageIds): array - { - if (empty($pageIds)) { - return []; - } - $restrictionContainer = GeneralUtility::makeInstance(FrontendRestrictionContainer::class); - $restrictionContainer->add(GeneralUtility::makeInstance( - DocumentTypeExclusionRestriction::class, - GeneralUtility::intExplode(',', (string)$this->cObj->checkPid_badDoktypeList, true) - )); - return $this->getTypoScriptFrontendController()->sys_page->filterAccessiblePageIds($pageIds, $restrictionContainer); - } - /** * Adds parts to the WHERE clause that are related to language. * This only works on TCA tables which have the [ctrl][languageField] field set or if they @@ -669,15 +493,14 @@ class DatabaseQueryProcessor implements DataProcessorInterface * If the language aspect has NO overlays enabled, it behaves as in "free mode" (= only fetch the records * for the current language. * - * @param ExpressionBuilder $expressionBuilder - * @param string $table - * @param array $conf - * @param Context $context - * @return string|\TYPO3\CMS\Core\Database\Query\Expression\CompositeExpression|null - * @throws \TYPO3\CMS\Core\Context\Exception\AspectNotFoundException + * @throws AspectNotFoundException */ - protected function getLanguageRestriction(ExpressionBuilder $expressionBuilder, string $table, array $conf, Context $context) - { + protected function getLanguageRestriction( + ExpressionBuilder $expressionBuilder, + string $table, + array $conf, + Context $context + ): string|CompositeExpression|null { $languageField = ''; $localizationParentField = $GLOBALS['TCA'][$table]['ctrl']['transOrigPointerField'] ?? null; // Check if the table is translatable, and set the language field by default from the TCA information @@ -706,15 +529,16 @@ class DatabaseQueryProcessor implements DataProcessorInterface $includeRecordsWithoutDefaultTranslation = isset($conf['includeRecordsWithoutDefaultTranslation.']) ? $this->cObj->stdWrap($conf['includeRecordsWithoutDefaultTranslation'], $conf['includeRecordsWithoutDefaultTranslation.']) : $conf['includeRecordsWithoutDefaultTranslation']; - $includeRecordsWithoutDefaultTranslation = trim($includeRecordsWithoutDefaultTranslation) !== ''; + $includeRecordsWithoutDefaultTranslation = trim((string)$includeRecordsWithoutDefaultTranslation); + $includeRecordsWithoutDefaultTranslation = $includeRecordsWithoutDefaultTranslation !== '' && $includeRecordsWithoutDefaultTranslation !== '0'; } else { // Option was not explicitly set, check what's in for the language overlay type. $includeRecordsWithoutDefaultTranslation = $languageAspect->getOverlayType() === $languageAspect::OVERLAYS_ON_WITH_FLOATING; } if ($includeRecordsWithoutDefaultTranslation) { - $languageQuery = $expressionBuilder->orX( + $languageQuery = $expressionBuilder->or( $languageQuery, - $expressionBuilder->andX( + $expressionBuilder->and( $expressionBuilder->eq($table . '.' . $localizationParentField, 0), $expressionBuilder->eq($languageField, $languageAspect->getContentId()) ) @@ -723,7 +547,7 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $languageQuery; } // No overlays = only fetch records given for the requested language and "all languages" - return $expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]); + return (string)$expressionBuilder->in($languageField, [$languageAspect->getContentId(), -1]); } /** @@ -732,21 +556,18 @@ class DatabaseQueryProcessor implements DataProcessorInterface * This functions checks if the necessary fields are part of the select * and adds them if necessary. * - * @param string $selectPart Select part - * @param string $table Table to select from * @return string Sanitized select part * @internal * @see getQuery */ - protected function sanitizeSelectPart(string $selectPart, string $table): string + protected function sanitizeSelectPart(Connection $connection, string $selectPart, string $table): string { - $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); - // Pattern matching parts $matchStart = '/(^\\s*|,\\s*|' . $table . '\\.)'; $matchEnd = '(\\s*,|\\s*$)/'; $necessaryFields = ['uid', 'pid']; $wsFields = ['t3ver_state']; + $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField'] ?? false; if (isset($GLOBALS['TCA'][$table]) && !preg_match($matchStart . '\\*' . $matchEnd, $selectPart) && !preg_match('/(count|max|min|avg|sum)\\([^\\)]+\\)|distinct/i', $selectPart)) { foreach ($necessaryFields as $field) { $match = $matchStart . $field . $matchEnd; @@ -754,6 +575,12 @@ class DatabaseQueryProcessor implements DataProcessorInterface $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $field) . ' AS ' . $connection->quoteIdentifier($field); } } + if (is_string($languageField)) { + $match = $matchStart . $languageField . $matchEnd; + if (!preg_match($match, $selectPart)) { + $selectPart .= ', ' . $connection->quoteIdentifier($table . '.' . $languageField) . ' AS ' . $connection->quoteIdentifier($languageField); + } + } if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ?? false) { foreach ($wsFields as $field) { $match = $matchStart . $field . $matchEnd; @@ -766,66 +593,24 @@ class DatabaseQueryProcessor implements DataProcessorInterface return $selectPart; } - /** - * Performs basic mathematical evaluation of the input string. Does NOT take parenthesis and operator precedence into account! (for that, see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction()) - * - * @param string $val The string to evaluate. Example: "3+4*10/5" will generate "35". Only integer numbers can be used. - * @return int The result (might be a float if you did a division of the numbers). - * @see \TYPO3\CMS\Core\Utility\MathUtility::calculateWithPriorityToAdditionAndSubtraction() - */ - public function calc($val) + protected function getPageRepository(): PageRepository { - $parts = GeneralUtility::splitCalc($val, '+-*/'); - $value = 0; - foreach ($parts as $part) { - $theVal = $part[1]; - $sign = $part[0]; - if ((string)(int)$theVal === (string)$theVal) { - $theVal = (int)$theVal; - } else { - $theVal = 0; - } - if ($sign === '-') { - $value -= $theVal; - } - if ($sign === '+') { - $value += $theVal; - } - if ($sign === '/') { - if ((int)$theVal) { - $value /= (int)$theVal; - } - } - if ($sign === '*') { - $value *= $theVal; - } - } - return $value; + return GeneralUtility::makeInstance(PageRepository::class); } - /** - * @return TimeTracker - */ - protected function getTimeTracker() + protected function getTimeTracker(): TimeTracker { return GeneralUtility::makeInstance(TimeTracker::class); } - /** - * Returns the current BE user. - * - * @return FrontendBackendUserAuthentication - */ - protected function getFrontendBackendUser() + public function getRequest(): ServerRequestInterface { - return $GLOBALS['BE_USER']; - } - - /** - * @return TypoScriptFrontendController|null - */ - protected function getTypoScriptFrontendController() - { - return $GLOBALS['TSFE'] ?? null; + if ($this->request instanceof ServerRequestInterface) { + return $this->request; + } + throw new ContentRenderingException( + 'PSR-7 request is missing in ContentObjectRenderer. Inject with start(), setRequest() or provide via $GLOBALS[\'TYPO3_REQUEST\'].', + 1607172972 + ); } } diff --git a/Classes/EventListener/CssMerger.php b/Classes/EventListener/CssMerger.php index dfd8de0..ab6003b 100644 --- a/Classes/EventListener/CssMerger.php +++ b/Classes/EventListener/CssMerger.php @@ -1,17 +1,33 @@ extensionConfiguration->get('ew_base', 'inlineCssStyles'); - if ($inlineCssStyles) { + /** @var Site $site */ + $site = $this->getRequest()->getAttribute('site'); + if ( + $site instanceof Site + && ($siteSettings = $site->getSettings()) + && !$siteSettings->isEmpty() + && $siteSettings->get('ew-base.inlineCssStyles') + ) { $assetCollector->addInlineStyleSheet('ew_base', $styles, [], ['priority' => true]); } else { $temporaryFile = GeneralUtility::writeStyleSheetContentToTemporaryFile($styles); - $assetCollector->addStyleSheet('combined_styles', $temporaryFile); + // @extensionScannerIgnoreLine + $assetCollector->addStyleSheet( + 'ew_base_combined_styles', + $temporaryFile, + ['nounce' => $this->requestId->nonce] + ); } } } @@ -96,4 +123,9 @@ class CssMerger $content ); } + + protected function getRequest(): ServerRequestInterface + { + return $GLOBALS['TYPO3_REQUEST']; + } } diff --git a/Classes/EventListener/IsContentUsedOnPageLayout.php b/Classes/EventListener/IsContentUsedOnPageLayout.php deleted file mode 100644 index daafc1e..0000000 --- a/Classes/EventListener/IsContentUsedOnPageLayout.php +++ /dev/null @@ -1,25 +0,0 @@ -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; - } -} diff --git a/Classes/EventListener/JsMerger.php b/Classes/EventListener/JsMerger.php index 52d7381..8fd6092 100644 --- a/Classes/EventListener/JsMerger.php +++ b/Classes/EventListener/JsMerger.php @@ -1,14 +1,32 @@ isInline() && $event->isPriority()) { @@ -22,7 +40,11 @@ class JsMerger $scripts = trim(implode(LF, $scripts)); if (!empty($scripts)) { $temporaryFile = GeneralUtility::writeJavaScriptContentToTemporaryFile($scripts); - $assetCollector->addJavaScript('combined_scripts', $temporaryFile); + $assetCollector->addJavaScript( + 'ew_base_combined_scripts', + $temporaryFile, + ['nounce' => $this->requestId->nonce] + ); } } } @@ -90,7 +112,7 @@ class JsMerger 'url("' . $relativeFilePath . '/..', 'url(\'' . $relativeFilePath . '/..', ], - $content + $content, ); } } diff --git a/Classes/EventListener/ReleaseToolbarItem.php b/Classes/EventListener/ReleaseToolbarItem.php new file mode 100644 index 0000000..e316acc --- /dev/null +++ b/Classes/EventListener/ReleaseToolbarItem.php @@ -0,0 +1,58 @@ +getToolbarItem(); + + $release = $this->getRelease(); + if ($release !== []) { + $label = $this->getLanguageService()->sL( + 'LLL:EXT:ew_base/Resources/Private/Language/locallang_core.xlf:rm.release_label-' + . ($release['isTag'] ? 'tag' : 'branch') + ); + $systemInformationToolbarItem->addSystemInformation( + $label, + $release['release'], + 'information-git', + InformationStatus::OK + ); + } + } + + protected function getRelease(): array + { + $release = GeneralUtility::getUrl(Environment::getProjectPath() . '/release'); + return $release ? [ + 'release' => trim($release), + 'isTag' => str_contains($release, '.'), + ] : []; + } + + private function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } +} diff --git a/Classes/Form/Element/PickColorFromImage.php b/Classes/Form/Element/PickColorFromImage.php index 705918b..38eb3f8 100644 --- a/Classes/Form/Element/PickColorFromImage.php +++ b/Classes/Form/Element/PickColorFromImage.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Evoweb\EwBase\Form\Element; - /* * This file is developed by evoWeb. * @@ -15,11 +13,14 @@ namespace Evoweb\EwBase\Form\Element; * LICENSE.txt file that was distributed with this source code. */ +namespace Evoweb\EwBase\Form\Element; + use TYPO3\CMS\Backend\Form\Element\AbstractFormElement; use TYPO3\CMS\Backend\View\BackendViewFactory; 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\Imaging\ImageManipulation\Ratio; use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction; use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; use TYPO3\CMS\Core\Resource\File; @@ -127,15 +128,12 @@ class PickColorFromImage extends AbstractFormElement $config = $this->processConfiguration($config, $parameterArray['itemFormElValue'], $file); $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( @@ -147,9 +145,9 @@ class PickColorFromImage extends AbstractFormElement ); $arguments = [ - 'fieldInformation' => $fieldInformationHtml, - 'fieldControl' => $fieldControlHtml, - 'fieldWizard' => $fieldWizardHtml, + 'fieldInformation' => $fieldInformationResult['html'], + 'fieldControl' => $fieldControlResult['html'], + 'fieldWizard' => $fieldWizardResult['html'], 'isAllowedFileExtension' => in_array( strtolower($file->getExtension()), GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), @@ -185,10 +183,8 @@ class PickColorFromImage extends AbstractFormElement $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); } } - $view = $this->backendViewFactory->create($this->data['request']); - $templatePaths = $view->getRenderingContext()->getTemplatePaths(); - $templatePaths->setTemplateRootPaths(['EXT:ew_base/Resources/Private/Templates']); - $templatePaths->setPartialRootPaths(['EXT:ew_base/Resources/Private/Partials']); + + $view = $this->backendViewFactory->create($this->data['request'], ['evoweb/ew-base']); $view->assignMultiple($arguments); $resultArray['html'] = $view->render('Form/ImageManipulationElement'); @@ -211,12 +207,19 @@ class PickColorFromImage extends AbstractFormElement return $file; } + /** + * @throws InvalidConfigurationException + */ protected function populateConfiguration(array $baseConfiguration): array { $defaultConfig = self::$defaultConfig; + + // If ratios are set do not add default options + if (isset($baseConfiguration['cropVariants'])) { + unset($defaultConfig['cropVariants']); + } + $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); @@ -225,12 +228,17 @@ class PickColorFromImage extends AbstractFormElement $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); - } - ); + $cropVariant['allowedAspectRatios'] = array_filter($cropVariant['allowedAspectRatios'] ?? [], static function (array $aspectRatio): bool { + return !(bool)($aspectRatio['disabled'] ?? false); + }); + + // Aspect ratios may not contain a "." character, see Ratio::__construct() + // To match them again properly, same replacement is required here. + $preparedAllowedAspectRatios = []; + foreach ($cropVariant['allowedAspectRatios'] as $aspectRatio => $aspectRatioDefinition) { + $preparedAllowedAspectRatios[Ratio::prepareAspectRatioId($aspectRatio)] = $aspectRatioDefinition; + } + $cropVariant['allowedAspectRatios'] = $preparedAllowedAspectRatios; // Ignore disabled crop variants if (!empty($cropVariant['disabled'])) { @@ -238,10 +246,7 @@ class PickColorFromImage extends AbstractFormElement } if (empty($cropVariant['allowedAspectRatios'])) { - throw new InvalidConfigurationException( - 'Crop variants configuration ' . $id . ' contains no allowed aspect ratios', - 1620147893 - ); + throw new InvalidConfigurationException('Crop variants configuration ' . $id . ' contains no allowed aspect ratios', 1620147893); } // Enforce a crop area (default is full image) @@ -254,8 +259,8 @@ class PickColorFromImage extends AbstractFormElement $config['cropVariants'] = $cropVariants; + // By default we allow all image extensions that can be handled by the GFX functionality $config['allowedExtensions'] ??= $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']; - return $config; } @@ -267,10 +272,7 @@ class PickColorFromImage extends AbstractFormElement $elementValue = (string)$cropVariantCollection; } $config['cropVariants'] = $cropVariantCollection->asArray(); - $config['allowedExtensions'] = implode( - ', ', - GeneralUtility::trimExplode(',', $config['allowedExtensions'], true) - ); + $config['allowedExtensions'] = implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions'], true)); return $config; } } diff --git a/Classes/Hooks/PageLayoutView.php b/Classes/Hooks/PageLayoutView.php deleted file mode 100644 index 608ccf1..0000000 --- a/Classes/Hooks/PageLayoutView.php +++ /dev/null @@ -1,34 +0,0 @@ -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; - } -} diff --git a/Classes/ToolbarItems/ReleaseToolbarItem.php b/Classes/ToolbarItems/ReleaseToolbarItem.php deleted file mode 100644 index 4f01aa4..0000000 --- a/Classes/ToolbarItems/ReleaseToolbarItem.php +++ /dev/null @@ -1,98 +0,0 @@ -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']; - } -} diff --git a/Classes/Updates/GridelementsToContainerMigration.php b/Classes/Updates/GridelementsToContainerMigration.php deleted file mode 100644 index 9a205cb..0000000 --- a/Classes/Updates/GridelementsToContainerMigration.php +++ /dev/null @@ -1,96 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - 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); - } -} diff --git a/Classes/Updates/GridelementsToContainerService.php b/Classes/Updates/GridelementsToContainerService.php deleted file mode 100644 index 4b0cdac..0000000 --- a/Classes/Updates/GridelementsToContainerService.php +++ /dev/null @@ -1,268 +0,0 @@ - [ - '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; - } -} diff --git a/Classes/Updates/ParentChildToContainerMigration.php b/Classes/Updates/ParentChildToContainerMigration.php deleted file mode 100644 index 685daab..0000000 --- a/Classes/Updates/ParentChildToContainerMigration.php +++ /dev/null @@ -1,106 +0,0 @@ -logger = $logManager->getLogger(self::class); - } - - 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); - } -} diff --git a/Classes/Updates/ParentChildToContainerService.php b/Classes/Updates/ParentChildToContainerService.php deleted file mode 100644 index 737ed75..0000000 --- a/Classes/Updates/ParentChildToContainerService.php +++ /dev/null @@ -1,168 +0,0 @@ - [ '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; - } -} diff --git a/Classes/User/AssetPath.php b/Classes/User/AssetPath.php index 9659830..8232df3 100644 --- a/Classes/User/AssetPath.php +++ b/Classes/User/AssetPath.php @@ -2,6 +2,17 @@ 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\User; use TYPO3\CMS\Core\Utility\GeneralUtility; diff --git a/Classes/ViewHelpers/Array/AddViewHelper.php b/Classes/ViewHelpers/Array/AddViewHelper.php index 1d0b5c9..06357e8 100644 --- a/Classes/ViewHelpers/Array/AddViewHelper.php +++ b/Classes/ViewHelpers/Array/AddViewHelper.php @@ -1,6 +1,6 @@ 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(); + public function render(): array + { + $array = $this->arguments['array'] ?: []; + $key = $this->arguments['key']; + $value = !is_null($this->arguments['value']) ? $this->arguments['value'] : $this->renderChildren(); return array_merge($array, [$key => $value]); } diff --git a/Classes/ViewHelpers/Array/ExplodeViewHelper.php b/Classes/ViewHelpers/Array/ExplodeViewHelper.php index 089b68e..0d04347 100755 --- a/Classes/ViewHelpers/Array/ExplodeViewHelper.php +++ b/Classes/ViewHelpers/Array/ExplodeViewHelper.php @@ -1,5 +1,7 @@ arguments['content'] ?? $this->renderChildren(); + $glue = $this->resolveGlue($this->arguments); + $content = call_user_func_array($this->method, [$glue, $content]); - $as = $arguments['as']; + $as = $this->arguments['as']; if ($as !== null) { - $templateVariableContainer = $renderingContext->getVariableProvider(); + $templateVariableContainer = $this->renderingContext->getVariableProvider(); $templateVariableContainer->add($as, $content); - $output = $renderChildrenClosure(); + $output = $this->renderChildren(); $templateVariableContainer->remove($as); } else { $output = $content; @@ -73,7 +68,7 @@ class ExplodeViewHelper extends AbstractViewHelper return $output; } - protected static function resolveGlue(array $arguments): string + protected function resolveGlue(array $arguments): string { $glue = $arguments['glue']; if (str_contains($glue, ':') && strlen($glue) > 1) { diff --git a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php index 8449574..0fa73a2 100644 --- a/Classes/ViewHelpers/Be/ThumbnailViewHelper.php +++ b/Classes/ViewHelpers/Be/ThumbnailViewHelper.php @@ -15,24 +15,21 @@ declare(strict_types=1); 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\Imaging\IconFactory; +use TYPO3\CMS\Core\Imaging\IconSize; use TYPO3\CMS\Core\Localization\LanguageService; +use TYPO3\CMS\Core\Resource\ProcessedFile; 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() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('row', 'array', 'content data', true); @@ -43,34 +40,47 @@ class ThumbnailViewHelper extends AbstractViewHelper /** * 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']; + public function render(): string + { + $table = $this->arguments['tableName']; + $field = $this->arguments['fieldName']; + $row = $this->arguments['row']; - return self::linkEditContent( - BackendUtility::thumbCode(row: $row, table: $tableName, field: $fieldName, linkInfoPopup: false), - $row + $fileReferences = BackendUtility::resolveFileReferences($table, $field, $row); + $fileObject = is_array($fileReferences) ? $fileReferences[0]->getOriginalFile() : null; + + if ($fileObject && $fileObject->isMissing()) { + /** @var IconFactory $iconFactory */ + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + $label = $this->getLanguageService()->sL( + 'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:warning.file_missing' + ); + return $iconFactory + ->getIcon('mimetypes-other-other', IconSize::MEDIUM, 'overlay-missing') + ->setTitle($label . ' ' . $fileObject->getName()) + ->render(); + } + + $previewFile = $fileObject->process( + ProcessedFile::CONTEXT_IMAGEPREVIEW, + [ + 'width' => 64, + 'height' => 64, + ] ); + + return $this->linkEditContent('', $row ); } - protected static function linkEditContent(string $linkText, $row): string + protected function linkEditContent(string $linkText, $row): string { if (empty($linkText)) { return $linkText; } - $backendUser = self::getBackendUser(); + $backendUser = $this->getBackendUser(); if ( $backendUser->check('tables_modify', 'tt_content') && $backendUser->recordEditAccessInternals('tt_content', $row) @@ -86,14 +96,15 @@ class ThumbnailViewHelper extends AbstractViewHelper $row['uid'] => 'edit', ], ], - 'returnUrl' => $GLOBALS['TYPO3_REQUEST']->getAttribute('normalizedParams')->getRequestUri() + // @extensionScannerIgnoreLine + 'returnUrl' => $this->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 '' . $linkText . ''; @@ -101,13 +112,18 @@ class ThumbnailViewHelper extends AbstractViewHelper return $linkText; } - protected static function getBackendUser(): BackendUserAuthentication + protected function getBackendUser(): BackendUserAuthentication { return $GLOBALS['BE_USER']; } - protected static function getLanguageService(): LanguageService + protected function getLanguageService(): LanguageService { return $GLOBALS['LANG']; } + + protected function getRequest(): ServerRequestInterface + { + return $GLOBALS['TYPO3_REQUEST']; + } } diff --git a/Classes/ViewHelpers/Condition/InArrayViewHelper.php b/Classes/ViewHelpers/Condition/InArrayViewHelper.php index bc713f5..36b47a2 100644 --- a/Classes/ViewHelpers/Condition/InArrayViewHelper.php +++ b/Classes/ViewHelpers/Condition/InArrayViewHelper.php @@ -1,14 +1,21 @@ registerArgument('haystack', 'array', 'haystack', true); $this->registerArgument('needle', 'mixed', 'needle', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + public static function verdict(array $arguments, RenderingContextInterface $renderingContext): bool { $array = $arguments['haystack']->toArray(); return in_array($arguments['needle'], $array); diff --git a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php index efb6b6f..7b28cc6 100644 --- a/Classes/ViewHelpers/Condition/StringContainsViewHelper.php +++ b/Classes/ViewHelpers/Condition/StringContainsViewHelper.php @@ -1,14 +1,21 @@ registerArgument('haystack', 'string', 'haystack', true); $this->registerArgument('needle', 'string', 'need', true); } - /** - * @param array $arguments - * @return bool - */ - protected static function evaluateCondition($arguments = null) + public static function verdict(array $arguments, RenderingContextInterface $renderingContext): bool { - return false !== strpos($arguments['haystack'], $arguments['needle']); + return str_contains($arguments['haystack'], $arguments['needle']); } } diff --git a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php index 0875df1..fe4a5b2 100644 --- a/Classes/ViewHelpers/Context/DevelopmentViewHelper.php +++ b/Classes/ViewHelpers/Context/DevelopmentViewHelper.php @@ -1,24 +1,27 @@ isDevelopment(); } diff --git a/Classes/ViewHelpers/Context/ProductionViewHelper.php b/Classes/ViewHelpers/Context/ProductionViewHelper.php index f5356bb..e57fc31 100644 --- a/Classes/ViewHelpers/Context/ProductionViewHelper.php +++ b/Classes/ViewHelpers/Context/ProductionViewHelper.php @@ -1,24 +1,27 @@ isProduction(); } diff --git a/Classes/ViewHelpers/Context/StagingViewHelper.php b/Classes/ViewHelpers/Context/StagingViewHelper.php index a76003e..bbbae08 100644 --- a/Classes/ViewHelpers/Context/StagingViewHelper.php +++ b/Classes/ViewHelpers/Context/StagingViewHelper.php @@ -1,24 +1,27 @@ 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(); + public function render(): string + { + $templateVariableContainer = $this->renderingContext->getVariableProvider(); /** @var FlexFormTools $flexFormTools */ $flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class); - $data = $arguments['data']; + $data = $this->arguments['data']; if (is_array($data['pi_flexform'])) { $data['pi_flexform'] = $flexFormTools->flexArray2Xml($data['pi_flexform']); } $templateVariableContainer->add('data', $data); - $output = $renderChildrenClosure(); + $output = $this->renderChildren(); $templateVariableContainer->remove('data'); return $output; diff --git a/Classes/ViewHelpers/FlexFormViewHelper.php b/Classes/ViewHelpers/FlexFormViewHelper.php index cd7fc27..ea793b0 100644 --- a/Classes/ViewHelpers/FlexFormViewHelper.php +++ b/Classes/ViewHelpers/FlexFormViewHelper.php @@ -1,6 +1,6 @@ registerArgument('data', 'array', 'Array to get flex form data from', true); @@ -47,33 +45,23 @@ class FlexFormViewHelper extends AbstractViewHelper $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 - ) { + public function render(): string + { $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']]); + if (is_array($this->arguments['data'])) { + if (isset($this->arguments['data'][$this->arguments['fieldName']])) { + $data = is_array($this->arguments['data'][$this->arguments['fieldName']]) ? + $this->arguments['data'][$this->arguments['fieldName']] : + GeneralUtility::xml2array($this->arguments['data'][$this->arguments['fieldName']]); $data = $data['data'] ?? $data; } } - $templateVariableContainer = $renderingContext->getVariableProvider(); - $templateVariableContainer->add($arguments['as'], $data); - $content = $renderChildrenClosure(); - $templateVariableContainer->remove($arguments['as']); + $templateVariableContainer = $this->renderingContext->getVariableProvider(); + $templateVariableContainer->add($this->arguments['as'], $data); + $content = $this->renderChildren(); + $templateVariableContainer->remove($this->arguments['as']); return $content; } diff --git a/Classes/ViewHelpers/HashViewHelper.php b/Classes/ViewHelpers/HashViewHelper.php deleted file mode 100644 index 0b61138..0000000 --- a/Classes/ViewHelpers/HashViewHelper.php +++ /dev/null @@ -1,60 +0,0 @@ -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; - } -} diff --git a/Classes/ViewHelpers/Iterator/AddViewHelper.php b/Classes/ViewHelpers/Iterator/AddViewHelper.php deleted file mode 100644 index bb24422..0000000 --- a/Classes/ViewHelpers/Iterator/AddViewHelper.php +++ /dev/null @@ -1,56 +0,0 @@ -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]); - } -} diff --git a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php b/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php deleted file mode 100644 index e50139f..0000000 --- a/Classes/ViewHelpers/Iterator/ExplodeViewHelper.php +++ /dev/null @@ -1,93 +0,0 @@ - ewb:iterator.explode(glue: 'constant:LF')} - * - * - * {as} - * - */ -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; - } -} diff --git a/Classes/ViewHelpers/PublicPathViewHelper.php b/Classes/ViewHelpers/PublicPathViewHelper.php index 769f001..f9c1329 100644 --- a/Classes/ViewHelpers/PublicPathViewHelper.php +++ b/Classes/ViewHelpers/PublicPathViewHelper.php @@ -2,8 +2,6 @@ declare(strict_types=1); -namespace Evoweb\EwBase\ViewHelpers; - /* * This file is developed by evoWeb. * @@ -15,10 +13,11 @@ namespace Evoweb\EwBase\ViewHelpers; * LICENSE.txt file that was distributed with this source code. */ +namespace Evoweb\EwBase\ViewHelpers; + +use TYPO3\CMS\Core\Resource\Exception\InvalidFileException; 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 = @@ -34,34 +33,23 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; */ class PublicPathViewHelper extends AbstractViewHelper { - use CompileWithRenderStatic; - /** * @var boolean */ protected $escapeOutput = false; - protected static ?array $frontendGroupIds = null; - - public function initializeArguments() + public function initializeArguments(): void { parent::initializeArguments(); $this->registerArgument('path', 'string', 'Extension resource path', true); } /** - * @param array $arguments - * @param \Closure $renderChildrenClosure - * @param RenderingContextInterface $renderingContext - * - * @return bool + * @throws InvalidFileException */ - public static function renderStatic( - array $arguments, - \Closure $renderChildrenClosure, - RenderingContextInterface $renderingContext - ) { - $path = (string)$arguments['path']; + public function renderStatic(): string + { + $path = (string)$this->arguments['path']; return PathUtility::getPublicResourceWebPath($path); } } diff --git a/Classes/ViewHelpers/ReplaceViewHelper.php b/Classes/ViewHelpers/ReplaceViewHelper.php index 77a9837..a9fe781 100644 --- a/Classes/ViewHelpers/ReplaceViewHelper.php +++ b/Classes/ViewHelpers/ReplaceViewHelper.php @@ -1,6 +1,6 @@ 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']; + public function render(): string + { + $content = $this->arguments['value']; if ($content === null) { - $content = $renderChildrenClosure(); + $content = $this->renderChildren(); } - return str_replace($arguments['search'], $arguments['replace'], $content); + return str_replace($this->arguments['search'], $this->arguments['replace'], $content); } } diff --git a/Classes/ViewHelpers/SvgViewHelper.php b/Classes/ViewHelpers/SvgViewHelper.php index 2290936..6dc418f 100644 --- a/Classes/ViewHelpers/SvgViewHelper.php +++ b/Classes/ViewHelpers/SvgViewHelper.php @@ -1,6 +1,6 @@ * */ -class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper +class SvgViewHelper extends AbstractViewHelper { - use CompileWithRenderStatic; - /** * ViewHelper returns HTML, thus we need to disable output escaping * @@ -75,30 +74,25 @@ class SvgViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper 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('size', 'string', 'Desired size of the icon. All values of the Icons.sizes enum are allowed, these are: "small", "default", "large" and "overlay".', false, IconSize::SMALL); + $this->registerArgument('overlay', 'string', 'Identifier of an overlay icon as registered in the Icon Registry.'); $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); + $this->registerArgument('alternativeMarkupIdentifier', 'string', 'Alternative icon identifier. Takes precedence over the identifier if supported by the IconProvider.'); } /** * 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']; + public function render(): string + { + $identifier = $this->arguments['identifier']; + $size = $this->arguments['size']; + $overlay = $this->arguments['overlay']; + $state = IconState::tryFrom($this->arguments['state']); + $alternativeMarkupIdentifier = $this->arguments['alternativeMarkupIdentifier']; + /** @var IconFactory $iconFactory */ $iconFactory = GeneralUtility::makeInstance(IconFactory::class); return $iconFactory->getIcon($identifier, $size, $overlay, $state)->getMarkup($alternativeMarkupIdentifier); } diff --git a/Classes/ViewHelpers/TrimViewHelper.php b/Classes/ViewHelpers/TrimViewHelper.php index 68683ff..5d1db02 100644 --- a/Classes/ViewHelpers/TrimViewHelper.php +++ b/Classes/ViewHelpers/TrimViewHelper.php @@ -1,6 +1,6 @@ registerArgument('content', 'string', 'Content to be trimmed'); - $this->registerArgument('characters', 'string', 'Characters to be removed', false); + $this->registerArgument('characters', 'string', 'Characters to be removed'); } - /** - * @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; + public function render(): string + { + $content = $this->arguments['content'] ?: $this->renderChildren(); + $characters = $this->arguments['characters'] ?: null; if ($characters !== null) { $content = trim($content, $characters); diff --git a/Configuration/Icons.php b/Configuration/Icons.php index 4220dea..8962136 100644 --- a/Configuration/Icons.php +++ b/Configuration/Icons.php @@ -1,6 +1,5 @@ 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', - ], ]; diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 8050c5b..3f7994e 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -16,29 +16,14 @@ services: Evoweb\EwBase\EventListener\CssMerger: tags: ['event.listener'] + public: true Evoweb\EwBase\EventListener\JsMerger: tags: ['event.listener'] + public: true - Evoweb\EwBase\EventListener\IsContentUsedOnPageLayout: + Evoweb\EwBase\EventListener\ReleaseToolbarItem: 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: diff --git a/Configuration/Sets/EwBase/settings.definitions.yaml b/Configuration/Sets/EwBase/settings.definitions.yaml index 31732ee..38aef6d 100644 --- a/Configuration/Sets/EwBase/settings.definitions.yaml +++ b/Configuration/Sets/EwBase/settings.definitions.yaml @@ -1,33 +1,11 @@ +categories: + ew-base: + label: 'Evoweb Base' + settings: - ew-base.userCentrics.loader.preload: - default: '' - label: 'UserCentrics loader preloader' - type: string - description: 'Preloader for loader' - ew-base.userCentrics.loader.template: - default: '' - label: 'UserCentrics loader template' - type: string - description: 'Template for loader' - - ew-base.userCentrics.main.preload: - default: '' - label: 'UserCentrics main preloader' - type: string - description: 'Preloader for main' - ew-base.userCentrics.main.template: - default: '' - label: 'UserCentrics main template' - type: string - description: 'Template for main' - - ew-base.userCentrics.block.preload: - default: '' - label: 'UserCentrics block preloader' - type: string - description: 'Preloader for block' - ew-base.userCentrics.block.template: - default: '' - label: 'UserCentrics block template' - type: string - description: 'Template for block' + ew-base.inlineCssStyles: + default: false + label: 'Inline css' + type: bool + description: 'Weather the concatenated css should get added inline style tag or a css file' + category: ew-base diff --git a/Configuration/Sets/EwBase/settings.yaml b/Configuration/Sets/EwBase/settings.yaml index a40b5a9..b61c029 100644 --- a/Configuration/Sets/EwBase/settings.yaml +++ b/Configuration/Sets/EwBase/settings.yaml @@ -1,22 +1,2 @@ ew-base: - userCentrics: - loader: - preload: | - - - - template: | - - main: - preload: | - - - - - template: | - - block: - preload: | - - template: | - + inlineCssStyles: false diff --git a/Configuration/TCA/Overrides/pages.php b/Configuration/TCA/Overrides/pages.php index 6a697aa..02ebb34 100644 --- a/Configuration/TCA/Overrides/pages.php +++ b/Configuration/TCA/Overrides/pages.php @@ -10,15 +10,15 @@ $newColumns = [ 'type' => 'group', 'allowed' => 'tt_content', 'size' => 1, + 'relationship' => 'manyToOne', + 'default' => 0, 'maxitems' => 1, - 'minitems' => 0, 'suggestOptions' => [ 'default' => [ 'additionalSearchFields' => 'header, bodytext', 'searchWholePhrase' => false ] ], - 'default' => 0, 'behaviour' => [ 'allowLanguageSynchronization' => true ] diff --git a/README.md b/README.md index 99d4154..adc44fa 100644 --- a/README.md +++ b/README.md @@ -1,19 +1 @@ # 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 -``` diff --git a/Resources/Private/Language/locallang_core.xlf b/Resources/Private/Language/locallang_core.xlf index ef33b70..f226a6f 100644 --- a/Resources/Private/Language/locallang_core.xlf +++ b/Resources/Private/Language/locallang_core.xlf @@ -3,9 +3,6 @@
- - Release information - Release tag diff --git a/Resources/Private/Language/locallang_siteconfiguration.xlf b/Resources/Private/Language/locallang_siteconfiguration.xlf deleted file mode 100644 index 682ac47..0000000 --- a/Resources/Private/Language/locallang_siteconfiguration.xlf +++ /dev/null @@ -1,33 +0,0 @@ - - - -
- - - UserCentrics - - - Max one configuration per site - - - - Usercentrics configuration - - - ID - - - ID defining what user centrics account to use - - - Version - - - Application Context - - - use Blocker - - - - diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html deleted file mode 100644 index aa01a16..0000000 --- a/Resources/Private/Templates/ToolbarItems/ShowReleaseDropDown.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - diff --git a/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html b/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html deleted file mode 100644 index 93c156f..0000000 --- a/Resources/Private/Templates/ToolbarItems/ShowReleaseToolbarItem.html +++ /dev/null @@ -1,8 +0,0 @@ - - - - - diff --git a/Resources/Public/Icons/usercentrics.svg b/Resources/Public/Icons/usercentrics.svg deleted file mode 100644 index 6d44c30..0000000 --- a/Resources/Public/Icons/usercentrics.svg +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - diff --git a/composer.json b/composer.json index d9f7594..e1292ee 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,6 @@ { "name": "evoweb/ew-base", "type": "typo3-cms-extension", - "version": "1.0.0", "autoload": { "psr-4": { "Evoweb\\EwBase\\": "Classes/" diff --git a/ext_conf_template.txt b/ext_conf_template.txt deleted file mode 100644 index b51c2fa..0000000 --- a/ext_conf_template.txt +++ /dev/null @@ -1,2 +0,0 @@ -# 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 diff --git a/ext_localconf.php b/ext_localconf.php index 9f47c15..9741611 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -9,7 +9,7 @@ use Evoweb\EwBase\Form\Element\PickColorFromImage; $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1681197508] = [ 'nodeName' => 'pick-color-from-image', - 'priority' => '70', + 'priority' => 70, 'class' => PickColorFromImage::class, ]; })(); diff --git a/ext_tables.sql b/ext_tables.sql deleted file mode 100644 index 232ee2c..0000000 --- a/ext_tables.sql +++ /dev/null @@ -1,7 +0,0 @@ -# -# Table structure for table 'pages' -# -CREATE TABLE pages -( - sectionIndex_uid int(11) unsigned DEFAULT '0' NOT NULL -);