Skip to content
Snippets Groups Projects
Commit 87acb2b1 authored by Nikita Hovratov's avatar Nikita Hovratov Committed by Oliver Bartsch
Browse files

[FEATURE] Auto-add system fields showitem for tt_content

This change adds system fields - as defined in the ctrl
section - of tt_content to the showitem field list
automatically. This is either done based on palettes
that already exist or adding the fields individually
to the corresponding tabs.

Tabs that are therefore added automatically are:

* General tab at the beginning
* Language tab including language fields
* Access tab including hidden+access fields
* Description tab including descriptionColumn

In case the showitem of such type defines the
"Extended" tab, it is added at the end.

We do this for tt_content because we want a unified
way of how FormEngine renders the fields.

Resolves: #104814
Releases: main
Change-Id: I69965fbc74ae2192151ad2617c1a88cf1eb2a987
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/85859


Tested-by: default avatarNikita Hovratov <nikita.h@live.de>
Tested-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarBenni Mack <benni@typo3.org>
Reviewed-by: default avatarNikita Hovratov <nikita.h@live.de...>
parent 57552216
Branches
Tags
No related merge requests found
......@@ -17,6 +17,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Core\Configuration\Tca;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
/**
......@@ -48,6 +49,7 @@ readonly class TcaPreparation
$tca = $this->configureLinkSoftReferences($tca);
$tca = $this->configureSelectSingle($tca);
$tca = $this->configureRelationshipToOne($tca);
$tca = $this->addSystemFieldsToShowitemTypes($tca);
return $tca;
}
......@@ -385,4 +387,195 @@ readonly class TcaPreparation
}
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--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general',
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language',
'--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access',
'--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 === 'CType' ? 'CType;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType_formlabel' : $typeField,
'colPos;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:colPos_formlabel',
];
}
// 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--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf: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--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf: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--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf: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') {
$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
{
foreach ($showitemParts as &$showItem) {
// Check if we deal with a field
if (!str_starts_with($showItem, '--div--') && !str_starts_with($showItem, '--palette--')) {
$parts = GeneralUtility::trimExplode(';', $showItem, true, 2);
// Just keep the first part => the fieldname in case field is defined in the $fieldList
if ($fieldList !== [] && in_array($parts[0], $fieldList, true)) {
$showItem = $parts[0];
}
}
}
return $showitemParts;
}
}
.. include:: /Includes.rst.txt
.. _feature-104814-1725444916:
===================================================================
Feature: #104814 - Automatically add system fields to content types
===================================================================
See :issue:`104814`
Description
===========
All content elements (:php:`CTypes`'s) are usually equipped with the same
system fields (`language`, `hidden`, etc.) - see also :ref:`feature-104311-1720176189`.
Adding them to the editor form has previously been done by adding those fields
to each content type's :php:`showitem` definition.
In the effort to simplify content element creation, to unify the available
fields and position for the editor and to finally reduce configuration effort
for integrators, those system fields are now added automatically based
on the :php:`ctrl` definition.
.. note::
The fields are added to the :php:`showitem` through their corresponding
palettes. In case such palette has been changed by extensions, the required
system fields are added individually to corresponding tabs.
The following tabs / palettes are now added automatically:
* The :guilabel:`General` tab with the `general` palette at the very beginning
* The :guilabel:`Language` tab with the `language` palette after custom fields
* The :guilabel:`Access` tab with the `hidden` and `access` palettes
* The :guilabel:`Notes` tab with the `rowDescription` field
As mentioned, in case one of those palettes has been changed to no longer
include the corresponding system fields, those fields are added individually
depending on their definition in the table's :php:`ctrl` section:
* The :php:`ctrl[type]` field (usually :php:`CType`)
* The :php:`colPos` field
* The :php:`ctrl[languageField]` (usually :php:`sys_language_uid`)
* The :php:`ctrl[editlock]` field (usually :php:`editlock`)
* The :php:`ctrl[enablecolumns][disabled]` field (usually :php:`hidden`)
* The :php:`ctrl[enablecolumns][starttime]` field (usually :php:`starttime`)
* The :php:`ctrl[enablecolumns][endtime]` field (usually :php:`endtime`)
* The :php:`ctrl[enablecolumns][fe_group]` field (usually :php:`fe_group`)
* The :php:`ctrl[descriptionColumn]` field (usually :php:`rowDescription`)
By default, all custom fields - the ones still defined in :php:`showitem` - are
added after the `general` palette and are therefore added to the
:guilabel:`General` tab, unless a custom tab (e.g. :guilabel:`Plugin`,
or :guilabel:`Categories`) is defined in between. It's also possible to start
with a custom tab by defining a `--div--` as the first item in the
:php:`showitem`. In this case, the :guilabel:`General` tab will be omitted.
All those system fields, which are added based on the :php:`ctrl` section are
also automatically removed from any custom palette and from the customized
type's :php:`showitem` definition.
If the content element defines the :guilabel:`Extended` tab, it will be
inserted at the end, including all fields added to the type via API methods,
without specifying a position, e.g. via
:php:`ExtensionManagementUtility::addToAllTcaTypes()`.
Impact
======
Creating content elements has been simplified by removing the need to
define the system fields for each element again and again. This shrinks
down a content element's :php:`showitem` to just the element specific fields.
A usual migration will therefore look like the following:
Before:
.. code-block:: php
'slider' => [
'showitem' => '
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:general,
--palette--;;general,
--palette--;;headers,
slider_elements,
bodytext;LLL:EXT:awesome_slider/Resources/Private/Language/locallang_ttc.xlf:bodytext.ALT.slider_description,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:appearance,
--palette--;;frames,
--palette--;;appearanceLinks,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:language,
--palette--;;language,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:access,
--palette--;;hidden,
--palette--;;access,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,
categories,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:notes,
rowDescription,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended,
',
],
After:
.. code-block:: php
'slider' => [
'showitem' => '
--palette--;;headers,
slider_elements,
bodytext;LLL:EXT:awesome_slider/Resources/Private/Language/locallang_ttc.xlf:bodytext.ALT.slider_description,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:categories,
categories,
--div--;LLL:EXT:core/Resources/Private/Language/Form/locallang_tabs.xlf:extended,
',
],
Since all fields, palettes and tabs, which are defined in the :php:`showitem`
are added after the :php:`general` palette, also the :guilabel:`Categories` tab
- if defined - is displayed before the system tabs / fields. The only special
case is the :guilabel:`Extended` tab, which is always added at the end.
.. important::
For consistency reasons, custom labels for system fields are no
longer preserved.
.. index:: PHP-API, TCA, ext:core
......@@ -69,6 +69,7 @@ final class TcaSchemaTest extends FunctionalTestCase
'space_after_class',
'sectionIndex',
'linkToTop',
'categories',
'sys_language_uid',
'l18n_parent',
'hidden',
......@@ -76,7 +77,6 @@ final class TcaSchemaTest extends FunctionalTestCase
'endtime',
'fe_group',
'editlock',
'categories',
'rowDescription',
], $usedColumns);
......
This diff is collapsed.
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment