TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,293 @@
|
||||
<?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 TYPO3\CMS\Core\Configuration\Tca;
|
||||
|
||||
/**
|
||||
* Automatically "enrich" TCA. This mainly adds "columns" definitions
|
||||
* based on "ctrl" settings. This is *not* for migration or preparation.
|
||||
*
|
||||
* This class is executed when building final TCA *after* "base"
|
||||
* files in "Configuration/TCA" have been loaded, and *before*
|
||||
* files in "Configuration/TCA/Overrides" are loaded.
|
||||
*
|
||||
* @internal Class and API may change any time.
|
||||
*/
|
||||
final readonly class TcaEnrichment
|
||||
{
|
||||
public function enrich(array $tca): array
|
||||
{
|
||||
$tca = $this->enrichDisabledField($tca);
|
||||
$tca = $this->enrichStarttimeField($tca);
|
||||
$tca = $this->enrichEndtimeField($tca);
|
||||
$tca = $this->enrichFeGroupField($tca);
|
||||
$tca = $this->enrichEditLockField($tca);
|
||||
$tca = $this->enrichDescriptionField($tca);
|
||||
$tca = $this->enrichLanguageField($tca);
|
||||
$tca = $this->setTransOrigPointerFieldInCtrl($tca);
|
||||
$tca = $this->enrichTransOrigPointerField($tca);
|
||||
$tca = $this->enrichTransOrigDiffSourceField($tca);
|
||||
$tca = $this->enrichTranslationSourceField($tca);
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichDisabledField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$disabledFieldName = $tableDefinition['ctrl']['enablecolumns']['disabled'] ?? null;
|
||||
if ($disabledFieldName && !is_array($tableDefinition['columns'][$disabledFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$disabledFieldName] = [
|
||||
'label' => 'core.db.general:enabled',
|
||||
'exclude' => true,
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
'default' => 0,
|
||||
'items' => [
|
||||
[
|
||||
'label' => '',
|
||||
'invertStateDisplay' => true,
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichStarttimeField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$starttimeFieldName = $tableDefinition['ctrl']['enablecolumns']['starttime'] ?? null;
|
||||
if ($starttimeFieldName && !is_array($tableDefinition['columns'][$starttimeFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$starttimeFieldName] = [
|
||||
'exclude' => true,
|
||||
'label' => 'core.db.general:starttime',
|
||||
'config' => [
|
||||
'type' => 'datetime',
|
||||
'default' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichEndtimeField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$endtimeFieldName = $tableDefinition['ctrl']['enablecolumns']['endtime'] ?? null;
|
||||
if ($endtimeFieldName && !is_array($tableDefinition['columns'][$endtimeFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$endtimeFieldName] = [
|
||||
'exclude' => true,
|
||||
'label' => 'core.db.general:endtime',
|
||||
'config' => [
|
||||
'type' => 'datetime',
|
||||
'default' => 0,
|
||||
'range' => [
|
||||
'upper' => mktime(0, 0, 0, 1, 1, 2106),
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichFeGroupField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$feGroupFieldName = $tableDefinition['ctrl']['enablecolumns']['fe_group'] ?? null;
|
||||
if ($feGroupFieldName && !is_array($tableDefinition['columns'][$feGroupFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$feGroupFieldName] = [
|
||||
'exclude' => true,
|
||||
'label' => 'core.db.general:fe_group',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectMultipleSideBySide',
|
||||
'size' => 5,
|
||||
'maxitems' => 20,
|
||||
'items' => [
|
||||
[
|
||||
'label' => 'core.db.general:fe_group.hide_at_login',
|
||||
'value' => -1,
|
||||
],
|
||||
[
|
||||
'label' => 'core.db.general:fe_group.any_login',
|
||||
'value' => -2,
|
||||
],
|
||||
[
|
||||
'label' => 'core.db.general:fe_group.usergroups',
|
||||
'value' => '--div--',
|
||||
],
|
||||
],
|
||||
'exclusiveKeys' => '-1,-2',
|
||||
'foreign_table' => 'fe_groups',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichEditLockField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$editLockFieldName = $tableDefinition['ctrl']['editlock'] ?? null;
|
||||
if ($editLockFieldName && !is_array($tableDefinition['columns'][$editLockFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$editLockFieldName] = [
|
||||
'displayCond' => 'HIDE_FOR_NON_ADMINS',
|
||||
'label' => 'core.db.general:editlock',
|
||||
'config' => [
|
||||
'type' => 'check',
|
||||
'renderType' => 'checkboxToggle',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichDescriptionField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$descriptionFieldName = $tableDefinition['ctrl']['descriptionColumn'] ?? null;
|
||||
if ($descriptionFieldName && !is_array($tableDefinition['columns'][$descriptionFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$descriptionFieldName] = [
|
||||
'exclude' => true,
|
||||
'label' => 'core.db.general:description',
|
||||
'config' => [
|
||||
'type' => 'text',
|
||||
'rows' => 5,
|
||||
'cols' => 30,
|
||||
'max' => 2000,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichLanguageField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$languageFieldName = $tableDefinition['ctrl']['languageField'] ?? null;
|
||||
if ($languageFieldName && !is_array($tableDefinition['columns'][$languageFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$languageFieldName] = [
|
||||
'exclude' => true,
|
||||
'label' => 'core.db.general:language',
|
||||
'config' => [
|
||||
'type' => 'language',
|
||||
],
|
||||
];
|
||||
}
|
||||
if ($languageFieldName && !is_array($tableDefinition['columns']['language_tag'] ?? null)) {
|
||||
$tca[$table]['columns']['language_tag'] = [
|
||||
'exclude' => true,
|
||||
'label' => 'Language Tag',
|
||||
'config' => [
|
||||
'type' => 'input',
|
||||
'size' => 10,
|
||||
'max' => 35,
|
||||
'eval' => 'trim',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* When 'languageField' is set, 'transOrigPointerField' must be set as well.
|
||||
* We silently add 'transOrigPointerField' if that iss not the case.
|
||||
*
|
||||
* @todo: This obviously needs a consolidation in ctrl. We should have a single, probably
|
||||
* boolean ctrl toggle to make a table 'localization' aware, with core then handling
|
||||
* all internals. This will require streamlining the field names along the way, and
|
||||
* we can think about this as soon as sys_language_uid=-1 is gone.
|
||||
*/
|
||||
private function setTransOrigPointerFieldInCtrl(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
if (isset($tableDefinition['ctrl']['languageField']) && !isset($tableDefinition['ctrl']['transOrigPointerField'])) {
|
||||
$tca[$table]['ctrl']['transOrigPointerField'] = 'l10n_parent';
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichTransOrigPointerField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$transOrigPointerFieldName = $tableDefinition['ctrl']['transOrigPointerField'] ?? null;
|
||||
if ($transOrigPointerFieldName && !is_array($tableDefinition['columns'][$transOrigPointerFieldName] ?? null)) {
|
||||
$languageFieldName = $tableDefinition['ctrl']['languageField'];
|
||||
$tca[$table]['columns'][$transOrigPointerFieldName] = [
|
||||
'displayCond' => 'FIELD:' . $languageFieldName . ':>:0',
|
||||
'label' => 'core.db.general:l18n_parent',
|
||||
'config' => [
|
||||
'type' => 'select',
|
||||
'renderType' => 'selectSingle',
|
||||
'items' => [
|
||||
[
|
||||
'label' => '',
|
||||
'value' => 0,
|
||||
],
|
||||
],
|
||||
'foreign_table' => $table,
|
||||
'foreign_table_where' => 'AND {#' . $table . '}.{#pid}=###CURRENT_PID### AND {#' . $table . '}.{#' . $languageFieldName . '} IN (-1,0)',
|
||||
'default' => 0,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichTransOrigDiffSourceField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$transOrigDiffSourceFieldName = $tableDefinition['ctrl']['transOrigDiffSourceField'] ?? null;
|
||||
if ($transOrigDiffSourceFieldName && !is_array($tableDefinition['columns'][$transOrigDiffSourceFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$transOrigDiffSourceFieldName] = [
|
||||
'config' => [
|
||||
'type' => 'passthrough',
|
||||
'default' => '',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichTranslationSourceField(array $tca): array
|
||||
{
|
||||
foreach ($tca as $table => $tableDefinition) {
|
||||
$translationSourceFieldName = $tableDefinition['ctrl']['translationSource'] ?? null;
|
||||
if ($translationSourceFieldName && !is_array($tableDefinition['columns'][$translationSourceFieldName] ?? null)) {
|
||||
$tca[$table]['columns'][$translationSourceFieldName] = [
|
||||
'config' => [
|
||||
'type' => 'passthrough',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
<?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 TYPO3\CMS\Core\Configuration\Tca;
|
||||
|
||||
use Psr\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autoconfigure;
|
||||
use Symfony\Component\DependencyInjection\Attribute\Autowire;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
|
||||
use TYPO3\CMS\Core\Configuration\Event\AfterTcaCompilationEvent;
|
||||
use TYPO3\CMS\Core\Configuration\Event\BeforeTcaOverridesEvent;
|
||||
use TYPO3\CMS\Core\Package\Cache\PackageDependentCacheIdentifier;
|
||||
use TYPO3\CMS\Core\Package\PackageManager;
|
||||
|
||||
/**
|
||||
* @internal Bootstrap related base TCA loading. Extensions must not use this.
|
||||
*/
|
||||
#[Autoconfigure(public: true)]
|
||||
final readonly class TcaFactory
|
||||
{
|
||||
public function __construct(
|
||||
private PackageManager $packageManager,
|
||||
private EventDispatcherInterface $eventDispatcher,
|
||||
#[Autowire(service: 'cache.core')]
|
||||
private PhpFrontend $codeCache,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* The main production worker method.
|
||||
*/
|
||||
public function get(): array
|
||||
{
|
||||
$cacheData = $this->codeCache->require($this->getTcaCacheIdentifier());
|
||||
if ($cacheData) {
|
||||
$tca = $cacheData['tca'];
|
||||
} else {
|
||||
$tca = $this->create();
|
||||
$this->createBaseTcaCacheFile($tca);
|
||||
}
|
||||
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is (indirectly) used by extension manager when loading
|
||||
* extensions, by install tool bootstrap and cache warmup.
|
||||
*/
|
||||
public function create(): array
|
||||
{
|
||||
$tca = $this->loadConfigurationTcaFiles();
|
||||
$tca = $this->dispatchBeforeTcaOverridesEvent($tca);
|
||||
$tca = $this->enrichTca($tca);
|
||||
$tca = $this->loadConfigurationTcaOverridesFiles($tca);
|
||||
$tca = $this->migrateTca($tca);
|
||||
$tca = $this->prepareTca($tca);
|
||||
return $this->dispatchAfterTcaCompilationEvent($tca);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is used by install tool LoadTcaService to check certain aspects of TCA
|
||||
*/
|
||||
public function createNotMigrated(): array
|
||||
{
|
||||
$tca = $this->loadConfigurationTcaFiles();
|
||||
$tca = $this->dispatchBeforeTcaOverridesEvent($tca);
|
||||
$tca = $this->enrichTca($tca);
|
||||
return $this->loadConfigurationTcaOverridesFiles($tca);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public since it's also used by CacheWarmupCommand
|
||||
*/
|
||||
public function createBaseTcaCacheFile(array $tca): void
|
||||
{
|
||||
$this->codeCache->set(
|
||||
$this->getTcaCacheIdentifier(),
|
||||
'return '
|
||||
. var_export(['tca' => $tca], true)
|
||||
. ';'
|
||||
);
|
||||
}
|
||||
|
||||
private function getTcaCacheIdentifier(): string
|
||||
{
|
||||
return (new PackageDependentCacheIdentifier($this->packageManager))->withPrefix('tca_base')->toString();
|
||||
}
|
||||
|
||||
private function loadConfigurationTcaFiles(): array
|
||||
{
|
||||
// To require TCA in a safe scoped environment avoiding local variable clashes.
|
||||
// Note: Return type 'mixed' is intended, otherwise broken TCA files with missing "return [];" statement would
|
||||
// emit a "return value must be of type array, int returned" PHP TypeError. This is mitigated by an array
|
||||
// check below.
|
||||
$scopedReturnRequire = static function (string $filename): mixed {
|
||||
return require $filename;
|
||||
};
|
||||
// First load "full table" files from Configuration/TCA
|
||||
$tca = [];
|
||||
$activePackages = $this->packageManager->getActivePackages();
|
||||
foreach ($activePackages as $package) {
|
||||
try {
|
||||
$finder = Finder::create()->files()->sortByName()->depth(0)->name('*.php')->in($package->getPackagePath() . 'Configuration/TCA');
|
||||
} catch (\InvalidArgumentException) {
|
||||
// No such directory in this package
|
||||
continue;
|
||||
}
|
||||
foreach ($finder as $fileInfo) {
|
||||
$tcaOfTable = $scopedReturnRequire($fileInfo->getPathname());
|
||||
if (is_array($tcaOfTable)) {
|
||||
$tcaTableName = substr($fileInfo->getBasename(), 0, -4);
|
||||
$tca[$tcaTableName] = $tcaOfTable;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function enrichTca(array $tca): array
|
||||
{
|
||||
return (new TcaEnrichment())->enrich($tca);
|
||||
}
|
||||
|
||||
private function loadConfigurationTcaOverridesFiles(array $tca): array
|
||||
{
|
||||
// To require TCA Overrides in a safe scoped environment avoiding local variable clashes.
|
||||
$scopedRequire = static function (string $filename): void {
|
||||
require $filename;
|
||||
};
|
||||
// Execute override files from Configuration/TCA/Overrides
|
||||
$GLOBALS['TCA'] = $tca;
|
||||
$activePackages = $this->packageManager->getActivePackages();
|
||||
foreach ($activePackages as $package) {
|
||||
try {
|
||||
$finder = Finder::create()->files()->sortByName()->depth(0)->name('*.php')->in($package->getPackagePath() . 'Configuration/TCA/Overrides');
|
||||
} catch (\InvalidArgumentException) {
|
||||
// No such directory in this package
|
||||
continue;
|
||||
}
|
||||
foreach ($finder as $fileInfo) {
|
||||
$scopedRequire($fileInfo->getPathname());
|
||||
}
|
||||
}
|
||||
$tca = $GLOBALS['TCA'];
|
||||
unset($GLOBALS['TCA']);
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function migrateTca(array $tca): array
|
||||
{
|
||||
// Call the TcaMigration and log any deprecations.
|
||||
$tcaMigration = new TcaMigration();
|
||||
$tcaProcessingResult = $tcaMigration->migrate($tca);
|
||||
$messages = $tcaProcessingResult->getMessages();
|
||||
if (!empty($messages)) {
|
||||
$context = 'Automatic TCA migration done during bootstrap. Please adapt TCA accordingly, these migrations'
|
||||
. ' will be removed. The backend module "Configuration -> TCA" shows the modified values.'
|
||||
. ' Please adapt these areas:';
|
||||
array_unshift($messages, $context);
|
||||
trigger_error(implode(LF, $messages), E_USER_DEPRECATED);
|
||||
}
|
||||
return $tcaProcessingResult->getTca();
|
||||
}
|
||||
|
||||
private function prepareTca(array $tca): array
|
||||
{
|
||||
return (new TcaPreparation())->prepare($tca);
|
||||
}
|
||||
|
||||
private function dispatchBeforeTcaOverridesEvent($tca): array
|
||||
{
|
||||
return $this->eventDispatcher->dispatch(new BeforeTcaOverridesEvent($tca))->getTca();
|
||||
}
|
||||
|
||||
private function dispatchAfterTcaCompilationEvent($tca): array
|
||||
{
|
||||
$GLOBALS['TCA'] = $tca;
|
||||
$tca = $this->eventDispatcher->dispatch(new AfterTcaCompilationEvent($tca))->getTca();
|
||||
unset($GLOBALS['TCA']);
|
||||
return $tca;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,624 @@
|
||||
<?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 TYPO3\CMS\Core\Configuration\Tca;
|
||||
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\StringUtility;
|
||||
|
||||
/**
|
||||
* Prepare TCA. Used in bootstrap and Flex Form Data Structures,
|
||||
* executed *after* TcaMigration.
|
||||
* This is used to add *internal TCA details* needed by core.
|
||||
* For instance, type=category fields receive all the relation
|
||||
* details in order to work properly.
|
||||
*
|
||||
* @internal Class and API may change any time.
|
||||
*/
|
||||
readonly class TcaPreparation
|
||||
{
|
||||
/**
|
||||
* Prepare TCA
|
||||
*
|
||||
* This class is typically called within bootstrap with empty caches after all TCA
|
||||
* files from extensions have been loaded. The preparation is then applied and
|
||||
* the prepared result is cached.
|
||||
* For flex form TCA, this class is called dynamically if opening a record in the backend.
|
||||
*
|
||||
* See unit tests for details.
|
||||
*/
|
||||
public function prepare(array $tca, bool $isFlexForm = false): array
|
||||
{
|
||||
$tca = $this->configureCategoryRelations($tca, $isFlexForm);
|
||||
$tca = $this->configureFileReferences($tca, $isFlexForm);
|
||||
$tca = $this->configureEmailSoftReferences($tca);
|
||||
$tca = $this->configureLinkSoftReferences($tca);
|
||||
$tca = $this->configureSelectSingle($tca);
|
||||
$tca = $this->configureRelationshipToOne($tca);
|
||||
$tca = $this->addSystemFieldsToShowitemTypes($tca);
|
||||
$tca = $this->addIgnoredPageTypeRestrictionRecords($tca);
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares TCA configuration of type='category' fields.
|
||||
*
|
||||
* It adds some TCA config settings so category fields end up with similar
|
||||
* config as type='select' field, but in a more restricted way.
|
||||
* Some settings could also be set in TCA directly, but some fields
|
||||
* can not be overridden, e.g. foreign_table.
|
||||
*
|
||||
* This also sets necessary MM properties, in case relationship is
|
||||
* set to "manyToMany", which is the default. Note it is "oneToMany"
|
||||
* with flex forms, since flex forms do NOT support "manyToMany".
|
||||
*
|
||||
* Finally, all category fields with a "manyToMany" relationship are
|
||||
* added to the MM_oppositeUsage of sys_category "items".
|
||||
*
|
||||
* Important: Since this method defines a "foreign_table_where", this
|
||||
* must always be executed before prepareQuotingOfTableNamesAndColumnNames().
|
||||
*/
|
||||
protected function configureCategoryRelations(array $tca, bool $isFlexForm): array
|
||||
{
|
||||
foreach ($tca as $table => &$tableDefinition) {
|
||||
if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
|
||||
if (($fieldConfig['config']['type'] ?? '') !== 'category') {
|
||||
continue;
|
||||
}
|
||||
if (!isset($fieldConfig['label'])) {
|
||||
$fieldConfig['label'] = 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:sys_category.categories';
|
||||
}
|
||||
// Force foreign_table for type category
|
||||
$fieldConfig['config']['foreign_table'] = 'sys_category';
|
||||
// Initialize default column configuration and merge it with already defined
|
||||
$fieldConfig['config']['size'] ??= 20;
|
||||
$defaultTag = \Local\Multilanguage\Service\DefaultLanguageTagService::getTag();
|
||||
$fieldConfig['config']['foreign_table_where'] ??= " AND {#sys_category}.{#language_tag} IN ('" . $defaultTag . "', '')";
|
||||
if (empty($fieldConfig['config']['relationship'])) {
|
||||
// In case no relationship is given, set "manyToMany" for non flex form, but "oneToMany" with flex form.
|
||||
$fieldConfig['config']['relationship'] = $isFlexForm ? 'oneToMany' : 'manyToMany';
|
||||
}
|
||||
|
||||
// Sanitize 'relationship'
|
||||
if ($isFlexForm && !in_array($fieldConfig['config']['relationship'], ['oneToOne', 'oneToMany'], true)) {
|
||||
throw new \UnexpectedValueException(
|
||||
'"relationship" must be one of "oneToOne" or "oneToMany", "manyToMany" is not supported as "relationship"'
|
||||
. ' for field ' . $fieldName . ' of type "category" in flexform.',
|
||||
1627640208
|
||||
);
|
||||
}
|
||||
if (!in_array($fieldConfig['config']['relationship'], ['oneToOne', 'oneToMany', 'manyToMany'], true)) {
|
||||
throw new \RuntimeException(
|
||||
$fieldName . ' of table ' . $table . ' is defined as type category with relationship "'
|
||||
. $fieldConfig['config']['relationship'] . '", but only "oneToOne", "oneToMany" and "manyToMany"'
|
||||
. ' are allowed.',
|
||||
1627898896
|
||||
);
|
||||
}
|
||||
|
||||
// Set the maxitems value (necessary for DataHandling and FormEngine)
|
||||
if ($fieldConfig['config']['relationship'] === 'oneToOne') {
|
||||
// In case relationship is set to "oneToOne", the database column for this
|
||||
// field will be an integer column. This means, only one uid can be stored.
|
||||
// Therefore, maxitems must be 1. Sanitize for flex form fields as well.
|
||||
if ((int)($fieldConfig['config']['maxitems'] ?? 0) > 1) {
|
||||
throw new \RuntimeException(
|
||||
$fieldName . ' of table ' . $table . ' is defined as type category with an oneToOne relationship. '
|
||||
. 'Therefore maxitems must be 1. Otherwise, use oneToMany or manyToMany as relationship instead.',
|
||||
1627335016
|
||||
);
|
||||
}
|
||||
$fieldConfig['config']['maxitems'] = 1;
|
||||
} elseif (!($fieldConfig['config']['maxitems'] ?? false)) {
|
||||
// In case maxitems is not set or set to 0, set the default value "99999"
|
||||
$fieldConfig['config']['maxitems'] = 99999;
|
||||
} elseif ($fieldConfig['config']['relationship'] === 'oneToMany'
|
||||
&& (int)($fieldConfig['config']['maxitems'] ?? 0) === 1
|
||||
) {
|
||||
throw new \RuntimeException(
|
||||
$fieldName . ' of table ' . $table . ' is defined as type category with a ' . $fieldConfig['config']['relationship']
|
||||
. ' relationship. Therefore, maxitems can not be set to 1. Use oneToOne as relationship instead.',
|
||||
1627335017
|
||||
);
|
||||
}
|
||||
|
||||
// Add the default value if not set
|
||||
if (!isset($fieldConfig['config']['default'])
|
||||
&& $fieldConfig['config']['relationship'] !== 'oneToMany'
|
||||
) {
|
||||
// @todo: This is db wise not accurate: A oneToOne relation without a relation being assigned,
|
||||
// @todo: should have NULL as value, not 0, since 0 looks like a uid, but it isn't.
|
||||
// @todo: The field should be nullable and DH should handle that.
|
||||
$fieldConfig['config']['default'] = 0;
|
||||
}
|
||||
|
||||
// Add MM related properties in case relationship is set to "manyToMany".
|
||||
// This will not be done for the sys_category table itself. Not relevant with flex forms.
|
||||
if ($fieldConfig['config']['relationship'] === 'manyToMany' && $table !== 'sys_category') {
|
||||
// Note these settings are hard coded here and can't be overridden.
|
||||
$fieldConfig['config'] = array_replace_recursive($fieldConfig['config'], [
|
||||
'MM' => 'sys_category_record_mm',
|
||||
'MM_opposite_field' => 'items',
|
||||
'MM_match_fields' => [
|
||||
'tablenames' => $table,
|
||||
'fieldname' => $fieldName,
|
||||
],
|
||||
]);
|
||||
// Register opposite references for the foreign side of a category relation
|
||||
if (empty($tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table])) {
|
||||
$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table] = [];
|
||||
}
|
||||
if (!in_array($fieldName, $tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table], true)) {
|
||||
$tca['sys_category']['columns']['items']['config']['MM_oppositeUsage'][$table][] = $fieldName;
|
||||
}
|
||||
// Take specific value of exclude flag into account
|
||||
if (!isset($fieldConfig['exclude'])) {
|
||||
$fieldConfig['exclude'] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
protected function configureFileReferences(array $tca, bool $isFlexForm): array
|
||||
{
|
||||
foreach ($tca as $table => &$tableDefinition) {
|
||||
if (!isset($tableDefinition['columns']) || !is_array($tableDefinition['columns'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as $fieldName => &$fieldConfig) {
|
||||
if (($fieldConfig['config']['type'] ?? '') === 'file') {
|
||||
// Set static values for this type. Most of them are not needed due to the
|
||||
// dedicated TCA type. However a lot of underlying code in DataHandler and
|
||||
// friends relies on those keys, especially "foreign_table" and "foreign_selector".
|
||||
// @todo Check which of those values can be removed since only used by FormEngine
|
||||
$fieldConfig['config'] = array_replace_recursive($fieldConfig['config'], [
|
||||
'foreign_table' => 'sys_file_reference',
|
||||
'foreign_field' => 'uid_foreign',
|
||||
'foreign_sortby' => 'sorting_foreign',
|
||||
'foreign_table_field' => 'tablenames',
|
||||
'foreign_label' => 'uid_local',
|
||||
'foreign_selector' => 'uid_local',
|
||||
]);
|
||||
if (!isset($fieldConfig['config']['foreign_match_fields']['fieldname'])) {
|
||||
$fieldConfig['config']['foreign_match_fields']['fieldname'] = $fieldName;
|
||||
}
|
||||
if (!$isFlexForm) {
|
||||
$fieldConfig['config']['foreign_match_fields']['tablenames'] = $table;
|
||||
}
|
||||
$fieldConfig['config'] = $this->configureAllowedDisallowedFileExtensions($fieldConfig['config']);
|
||||
}
|
||||
if (is_array($fieldConfig['config']['overrideChildTca'] ?? null)) {
|
||||
$fieldConfig['config']['overrideChildTca'] = $this->configureAllowedDisallowedInOverrideChildTca($fieldConfig['config']['overrideChildTca']);
|
||||
}
|
||||
}
|
||||
unset($fieldConfig);
|
||||
if (is_array($tableDefinition['types'] ?? null)) {
|
||||
foreach ($tableDefinition['types'] as &$typeConfig) {
|
||||
if (!isset($typeConfig['columnsOverrides']) || !is_array($typeConfig['columnsOverrides'])) {
|
||||
continue;
|
||||
}
|
||||
foreach ($typeConfig['columnsOverrides'] as &$columnsOverridesConfig) {
|
||||
if (!isset($columnsOverridesConfig['config']) || !is_array($columnsOverridesConfig['config'])) {
|
||||
continue;
|
||||
}
|
||||
$columnsOverridesConfig['config'] = $this->configureAllowedDisallowedFileExtensions($columnsOverridesConfig['config']);
|
||||
if (is_array($columnsOverridesConfig['config']['overrideChildTca'] ?? null)) {
|
||||
$columnsOverridesConfig['config']['overrideChildTca'] = $this->configureAllowedDisallowedInOverrideChildTca($columnsOverridesConfig['config']['overrideChildTca']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* configureFileReferences() helper
|
||||
*/
|
||||
protected function configureAllowedDisallowedInOverrideChildTca(array $overrideChildTcaConfig): array
|
||||
{
|
||||
if (is_array($overrideChildTcaConfig['columns'] ?? null)) {
|
||||
foreach ($overrideChildTcaConfig['columns'] as &$overrideChildTcaColumnConfig) {
|
||||
if (!isset($overrideChildTcaColumnConfig['config'])) {
|
||||
continue;
|
||||
}
|
||||
$overrideChildTcaColumnConfig['config'] = $this->configureAllowedDisallowedFileExtensions($overrideChildTcaColumnConfig['config']);
|
||||
}
|
||||
unset($overrideChildTcaColumnConfig);
|
||||
}
|
||||
if (is_array($overrideChildTcaConfig['types'] ?? null)) {
|
||||
foreach ($overrideChildTcaConfig['types'] as &$overrideChildTcaTypeConfig) {
|
||||
if (!isset($overrideChildTcaTypeConfig['config'])) {
|
||||
continue;
|
||||
}
|
||||
$overrideChildTcaTypeConfig['config'] = $this->configureAllowedDisallowedFileExtensions($overrideChildTcaTypeConfig['config']);
|
||||
}
|
||||
}
|
||||
return $overrideChildTcaConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* configureFileReferences() helper
|
||||
*/
|
||||
protected function configureAllowedDisallowedFileExtensions(array $config): array
|
||||
{
|
||||
if (!empty($allowed = ($config['allowed'] ?? null))) {
|
||||
$config['allowed'] = $this->prepareFileExtensions($allowed);
|
||||
}
|
||||
if (!empty($disallowed = ($config['disallowed'] ?? null))) {
|
||||
$config['disallowed'] = $this->prepareFileExtensions($disallowed);
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* configureFileReferences() helper: Ensures format, replaces placeholders and remove duplicates
|
||||
*/
|
||||
protected function prepareFileExtensions(mixed $fileExtensions): string
|
||||
{
|
||||
if (is_array($fileExtensions)) {
|
||||
$fileExtensions = implode(',', $fileExtensions);
|
||||
} else {
|
||||
$fileExtensions = (string)$fileExtensions;
|
||||
}
|
||||
// Replace placeholders with the corresponding $GLOBALS value for now
|
||||
if (preg_match_all('/common-(image|text|media)-types/', $fileExtensions, $matches)) {
|
||||
foreach ($matches[1] as $key => $type) {
|
||||
$fileExtensions = str_replace(
|
||||
$matches[0][$key],
|
||||
$GLOBALS['TYPO3_CONF_VARS'][$type === 'image' ? 'GFX' : 'SYS'][$type . 'file_ext'] ?? '',
|
||||
$fileExtensions
|
||||
);
|
||||
}
|
||||
}
|
||||
return StringUtility::uniqueList($fileExtensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "'softref' = 'email[subst]'" to all 'type' = 'email' column fields.
|
||||
*/
|
||||
protected function configureEmailSoftReferences(array $tca): array
|
||||
{
|
||||
foreach ($tca as &$tableDefinition) {
|
||||
if (!is_array($tableDefinition['columns'] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as &$fieldConfig) {
|
||||
if (($fieldConfig['config']['type'] ?? null) === 'email') {
|
||||
// Hard set/override: 'softref' is not listed as property for type=email at all,
|
||||
// there is little need to have this configurable.
|
||||
$fieldConfig['config']['softref'] = 'email[subst]';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "'softref' = 'typolink'" to all 'type' = 'link' column fields.
|
||||
*/
|
||||
protected function configureLinkSoftReferences(array $tca): array
|
||||
{
|
||||
foreach ($tca as &$tableDefinition) {
|
||||
if (!is_array($tableDefinition['columns'] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as &$fieldConfig) {
|
||||
if (($fieldConfig['config']['type'] ?? null) === 'link') {
|
||||
// Hard set/override: 'softref' is not listed as property for type=link at all,
|
||||
// there is little need to have this configurable.
|
||||
$fieldConfig['config']['softref'] = 'typolink';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "'relationship' for TCA type "select" fields, having "selectSingle" set as renderType and are
|
||||
* pointing to a "foreign_table". Depending on further configuration, this will set the "relationship"
|
||||
* to either "manyToMany" (in case "MM" is set) or to "manyToOne".
|
||||
* Already defined "relationship" is not overwritten!
|
||||
*
|
||||
* This is mainly done to prevent checks on the renderType, which should be avoided.
|
||||
*/
|
||||
protected function configureSelectSingle(array $tca): array
|
||||
{
|
||||
foreach ($tca as &$tableDefinition) {
|
||||
if (!is_array($tableDefinition['columns'] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as &$fieldConfig) {
|
||||
if (($fieldConfig['config']['type'] ?? null) !== 'select'
|
||||
|| ($fieldConfig['config']['renderType'] ?? null) !== 'selectSingle'
|
||||
|| !isset($fieldConfig['config']['foreign_table'])
|
||||
|| isset($fieldConfig['config']['relationship'])
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($fieldConfig['config']['MM'])) {
|
||||
$fieldConfig['config']['relationship'] = 'manyToMany';
|
||||
} else {
|
||||
$fieldConfig['config']['relationship'] = 'manyToOne';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add "'maxitems' => 1" to all relation type column fields with 'relationship' set to 'oneToOne' or 'manyToOne'.
|
||||
*/
|
||||
protected function configureRelationshipToOne(array $tca): array
|
||||
{
|
||||
foreach ($tca as &$tableDefinition) {
|
||||
if (!is_array($tableDefinition['columns'] ?? null)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($tableDefinition['columns'] as &$fieldConfig) {
|
||||
$type = $fieldConfig['config']['type'] ?? null;
|
||||
if (in_array($type, ['select', 'inline', 'group', 'folder', 'file'], true)
|
||||
&& in_array($fieldConfig['config']['relationship'] ?? null, ['oneToOne', 'manyToOne'], true)
|
||||
) {
|
||||
// Hard set/override: 'maxitems' to 1, since relationship [x]ToOne - as the name suggests -
|
||||
// only allows a single item to be selected.
|
||||
$fieldConfig['config']['maxitems'] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that all system fields (CType, colPos, hidden etc.) are automatically added
|
||||
* to the showitem list of all CTypes in tt_content. As custom CTypes might have added
|
||||
* the fields, the respective fields also need to be removed first.
|
||||
*/
|
||||
protected function addSystemFieldsToShowitemTypes(array $tca): array
|
||||
{
|
||||
// @todo Only deal with this for tt_content in v13, as other parts might be too intrusive
|
||||
// might change in v14
|
||||
if (!isset($tca['tt_content'])) {
|
||||
return $tca;
|
||||
}
|
||||
// Only proceed in case the record type field is defined
|
||||
$typeField = (string)($tca['tt_content']['ctrl']['type'] ?? '');
|
||||
if ($typeField === '') {
|
||||
return $tca;
|
||||
}
|
||||
// Build list of values (fields and palettes) which should be removed
|
||||
// from custom palettes, because they will be added automatically.
|
||||
$listOfValuesToRemove = [
|
||||
'--div--;core.form.tabs:general',
|
||||
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general',
|
||||
'--div--;core.form.tabs:language',
|
||||
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language',
|
||||
'--div--;core.form.tabs:access',
|
||||
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access',
|
||||
'--div--;core.form.tabs:notes',
|
||||
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes',
|
||||
'--palette--;;general',
|
||||
'--palette--;;language',
|
||||
'--palette--;;access',
|
||||
'--palette--;;hidden',
|
||||
'colPos',
|
||||
];
|
||||
$listOfValuesToRemove[] = $typeField;
|
||||
if (($languageField = (string)($tca['tt_content']['ctrl']['languageField'] ?? '')) !== '') {
|
||||
$listOfValuesToRemove[] = $languageField;
|
||||
}
|
||||
if (($transOrigPointerField = (string)($tca['tt_content']['ctrl']['transOrigPointerField'] ?? '')) !== '') {
|
||||
$listOfValuesToRemove[] = $transOrigPointerField;
|
||||
}
|
||||
$enablecolumns = $tca['tt_content']['ctrl']['enablecolumns'] ?? [];
|
||||
foreach ($enablecolumns as $fieldName) {
|
||||
$listOfValuesToRemove[] = $fieldName;
|
||||
}
|
||||
if (($editlock = (string)($tca['tt_content']['ctrl']['editlock'] ?? '')) !== '') {
|
||||
$listOfValuesToRemove[] = $editlock;
|
||||
}
|
||||
if (($descriptionColumn = (string)($tca['tt_content']['ctrl']['descriptionColumn'] ?? '')) !== '') {
|
||||
$listOfValuesToRemove[] = $descriptionColumn;
|
||||
}
|
||||
|
||||
// Remove any system field from custom palettes
|
||||
foreach ($tca['tt_content']['palettes'] as $paletteName => &$paletteConfig) {
|
||||
if (in_array($paletteName, ['general', 'language', 'access', 'hidden'], true)) {
|
||||
continue;
|
||||
}
|
||||
$showItemSplitted = GeneralUtility::trimExplode(',', $paletteConfig['showitem'], true);
|
||||
$paletteConfig['showitem'] = implode(',', array_diff($this->removeCustomFieldLabels($showItemSplitted, $listOfValuesToRemove), $listOfValuesToRemove));
|
||||
}
|
||||
unset($paletteConfig);
|
||||
|
||||
// Process the content types
|
||||
foreach ($tca['tt_content']['types'] as $type => $typeInformation) {
|
||||
// Remove any of the special fields from the content type's current showitem
|
||||
$showItemSplitted = GeneralUtility::trimExplode(',', $typeInformation['showitem'] ?? '', true);
|
||||
$showItemFiltered = array_diff($this->removeCustomFieldLabels($showItemSplitted, $listOfValuesToRemove), $listOfValuesToRemove);
|
||||
|
||||
// Extract all fields of the extended tab to add it at the end
|
||||
[$showItemList, $extendedParts] = $this->extractExtendedParts($showItemFiltered);
|
||||
|
||||
// Add record type field (usually "CType") and colPos either using the "general" palette
|
||||
// or manually, in case the palette does not exist or does not contain the fields.
|
||||
$generalPaletteItems = $this->removeCustomFieldLabels(GeneralUtility::trimExplode(',', $tca['tt_content']['palettes']['general']['showitem'] ?? '', true), $listOfValuesToRemove);
|
||||
if (in_array($typeField, $generalPaletteItems, true) && in_array('colPos', $generalPaletteItems, true)) {
|
||||
$showItemParts = ['--palette--;;general'];
|
||||
} else {
|
||||
$showItemParts = [
|
||||
$typeField,
|
||||
'colPos',
|
||||
];
|
||||
}
|
||||
|
||||
// Because FormEngine will add the general tab automatically, we will not do this here
|
||||
// However, if the first item in the $showItemList is actually a tab (--div--), we need to
|
||||
// add if before the "first fields"
|
||||
if (str_starts_with($showItemList[0] ?? '', '--div--')) {
|
||||
array_unshift($showItemParts, $showItemList[0]);
|
||||
unset($showItemList[0]);
|
||||
}
|
||||
$showItemParts = array_merge($showItemParts, $showItemList);
|
||||
|
||||
// Add language field either using the "language" palette or manually,
|
||||
// in case the palette does not exist or does not contain the field.
|
||||
if ($languageField !== '') {
|
||||
$showItemParts[] = '--div--;core.form.tabs:language';
|
||||
$languagePaletteItems = $this->removeCustomFieldLabels(GeneralUtility::trimExplode(',', $tca['tt_content']['palettes']['language']['showitem'] ?? '', true), $listOfValuesToRemove);
|
||||
if (in_array($languageField, $languagePaletteItems, true)
|
||||
&& ($transOrigPointerField === '' || in_array($transOrigPointerField, $languagePaletteItems, true))
|
||||
) {
|
||||
$showItemParts[] = '--palette--;;language';
|
||||
} else {
|
||||
$showItemParts[] = $languageField;
|
||||
if ($transOrigPointerField) {
|
||||
$showItemParts[] = $transOrigPointerField;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add enable fields either using the "hidden" amd "access" palettes or
|
||||
// manually, in case the palettes do not exist or do not contain the fields.
|
||||
if ($enablecolumns !== [] || $editlock !== '') {
|
||||
$showItemParts[] = '--div--;core.form.tabs:access';
|
||||
if (isset($enablecolumns['disabled'])) {
|
||||
$hiddenPaletteParts = $this->removeCustomFieldLabels(GeneralUtility::trimExplode(',', $tca['tt_content']['palettes']['hidden']['showitem'] ?? '', true), $listOfValuesToRemove);
|
||||
if (in_array($enablecolumns['disabled'], $hiddenPaletteParts, true)) {
|
||||
$showItemParts[] = '--palette--;;hidden';
|
||||
} else {
|
||||
$showItemParts[] = $enablecolumns['disabled'];
|
||||
}
|
||||
}
|
||||
if ((isset($enablecolumns['starttime']) || isset($enablecolumns['endtime']) || isset($enablecolumns['fe_group']) || $editlock)) {
|
||||
$accessPaletteParts = $this->removeCustomFieldLabels(GeneralUtility::trimExplode(',', $tca['tt_content']['palettes']['access']['showitem'] ?? '', true), $listOfValuesToRemove);
|
||||
if ((!isset($enablecolumns['starttime']) || in_array($enablecolumns['starttime'], $accessPaletteParts, true))
|
||||
&& (!isset($enablecolumns['endtime']) || in_array($enablecolumns['endtime'], $accessPaletteParts, true))
|
||||
&& (!isset($enablecolumns['fe_group']) || in_array($enablecolumns['fe_group'], $accessPaletteParts, true))
|
||||
&& (!$editlock || in_array($editlock, $accessPaletteParts, true))
|
||||
) {
|
||||
$showItemParts[] = '--palette--;;access';
|
||||
} else {
|
||||
if (isset($enablecolumns['starttime'])) {
|
||||
$showItemParts[] = $enablecolumns['starttime'];
|
||||
}
|
||||
if (isset($enablecolumns['endtime'])) {
|
||||
$showItemParts[] = $enablecolumns['endtime'];
|
||||
}
|
||||
if (isset($enablecolumns['fe_group'])) {
|
||||
$showItemParts[] = $enablecolumns['fe_group'];
|
||||
}
|
||||
if ($editlock) {
|
||||
$showItemParts[] = $editlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add description column if defined
|
||||
if ($descriptionColumn !== '') {
|
||||
$showItemParts[] = '--div--;core.form.tabs:notes,' . $descriptionColumn;
|
||||
}
|
||||
|
||||
// Add extended tab at the end - if it exists
|
||||
$showItemParts = array_merge($showItemParts, $extendedParts);
|
||||
|
||||
// Merge parts together
|
||||
$tca['tt_content']['types'][$type]['showitem'] = trim(implode(',', $showItemParts), ',');
|
||||
}
|
||||
return $tca;
|
||||
}
|
||||
|
||||
private function extractExtendedParts(array $showItemFiltered): array
|
||||
{
|
||||
$extendedParts = [];
|
||||
$addFields = false;
|
||||
foreach ($showItemFiltered as $key => $part) {
|
||||
if ($part === '--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended' || $part === '--div--;core.form.tabs:extended') {
|
||||
$extendedParts[] = $part;
|
||||
$addFields = true;
|
||||
unset($showItemFiltered[$key]);
|
||||
} elseif ($addFields) {
|
||||
if (str_starts_with($part, '--div--')) {
|
||||
break;
|
||||
}
|
||||
$extendedParts[] = $part;
|
||||
unset($showItemFiltered[$key]);
|
||||
}
|
||||
}
|
||||
return [$showItemFiltered, $extendedParts];
|
||||
}
|
||||
|
||||
private function removeCustomFieldLabels(array $showitemParts, array $fieldList): array
|
||||
{
|
||||
if ($fieldList === []) {
|
||||
return $showitemParts;
|
||||
}
|
||||
foreach ($showitemParts as &$showItem) {
|
||||
// Tabs keep their labels
|
||||
if (str_starts_with($showItem, '--div--')) {
|
||||
continue;
|
||||
}
|
||||
// Remove label of palette
|
||||
if (str_starts_with($showItem, '--palette--')) {
|
||||
$parts = GeneralUtility::trimExplode(';', $showItem, true, 3);
|
||||
// Palette without label, continue.
|
||||
if (count($parts) !== 3) {
|
||||
continue;
|
||||
}
|
||||
$paletteName = '--palette--;;' . $parts[2];
|
||||
if (in_array($paletteName, $fieldList, true)) {
|
||||
$showItem = $paletteName;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// This must be a field.
|
||||
$parts = GeneralUtility::trimExplode(';', $showItem, true, 2);
|
||||
$fieldName = $parts[0];
|
||||
// Just keep the first part => the fieldname in case field is defined in the $fieldList
|
||||
if (in_array($fieldName, $fieldList, true)) {
|
||||
$showItem = $fieldName;
|
||||
}
|
||||
}
|
||||
return $showitemParts;
|
||||
}
|
||||
|
||||
protected function addIgnoredPageTypeRestrictionRecords(array $tca): array
|
||||
{
|
||||
$allowedRecordTypes = [];
|
||||
foreach ($tca as $table => $configuration) {
|
||||
if ($configuration['ctrl']['security']['ignorePageTypeRestriction'] ?? false) {
|
||||
$allowedRecordTypes[] = $table;
|
||||
}
|
||||
}
|
||||
if ($allowedRecordTypes === []) {
|
||||
return $tca;
|
||||
}
|
||||
$mergedAllowedRecords = array_merge(
|
||||
$tca['pages']['ctrl']['defaultAllowedRecordTypes'] ?? [],
|
||||
$allowedRecordTypes
|
||||
);
|
||||
$tca['pages']['ctrl']['defaultAllowedRecordTypes'] = array_unique($mergedAllowedRecords);
|
||||
return $tca;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
<?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 TYPO3\CMS\Core\Configuration\Tca;
|
||||
|
||||
/**
|
||||
* @internal internal data object - only to be used within TYPO3 Core
|
||||
*/
|
||||
final readonly class TcaProcessingResult
|
||||
{
|
||||
public function __construct(
|
||||
private array $tca,
|
||||
/** Accumulate messages, occurred on TCA processing, e.g. by TcaMigration. */
|
||||
private array $messages = []
|
||||
) {}
|
||||
|
||||
public function getTca(): array
|
||||
{
|
||||
return $this->tca;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getMessages(): array
|
||||
{
|
||||
return $this->messages;
|
||||
}
|
||||
|
||||
public function withTca(array $tca): TcaProcessingResult
|
||||
{
|
||||
return new self($tca, $this->messages);
|
||||
}
|
||||
|
||||
public function withAdditionalMessages(string ...$messages): TcaProcessingResult
|
||||
{
|
||||
return new self($this->tca, array_merge($this->messages, $messages));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user