TYPO3 v15 dev-main snapshot ()

This commit is contained in:
2026-08-10 22:31:00 +02:00
commit f9941541b7
1178 changed files with 135377 additions and 0 deletions
@@ -0,0 +1,96 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\DiffUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Renders the diff-view of default language record content compared with what the record was originally
* translated from. Will render content if any is found in the internal array.
*
* This is typically used of renderTypes that are based on text input
*/
class DefaultLanguageDifferences extends AbstractNode
{
public function __construct(
private readonly DiffUtility $diffUtility,
) {}
/**
* Render the diff view if enabled
*
* @return array Result array
*/
public function render(): array
{
$result = $this->initializeResultArray();
$row = $this->data['databaseRow'];
$table = $this->data['tableName'];
$fieldName = $this->data['fieldName'];
$fieldConfig = $this->data['processedTca']['columns'][$fieldName];
$l10nDisplay = $this->data['parameterArray']['fieldConf']['l10n_display'] ?? '';
$defaultLanguageRow = $this->data['defaultLanguageRow'] ?? null;
$defaultLanguageDiffRow = $this->data['defaultLanguageDiffRow'][$table . ':' . $row['uid']] ?? null;
if (!is_array($defaultLanguageDiffRow)
|| GeneralUtility::inList($l10nDisplay, 'hideDiff')
|| GeneralUtility::inList($l10nDisplay, 'defaultAsReadonly')
|| !isset($defaultLanguageDiffRow[$fieldName])
|| $fieldConfig['config']['type'] === 'inline'
|| $fieldConfig['config']['type'] === 'file'
|| $fieldConfig['config']['type'] === 'flex'
) {
// Early return if there is no diff row or if display is disabled
return $result;
}
$languageService = $this->getLanguageService();
$html = [];
if ((string)$defaultLanguageDiffRow[$fieldName] !== (string)$defaultLanguageRow[$fieldName]) {
// Create diff-result:
$diffResult = $this->diffUtility->diff(
(string)BackendUtility::getProcessedValue($table, $fieldName, $defaultLanguageDiffRow[$fieldName], 0, true, false, 0, true, 0, $defaultLanguageRow),
(string)BackendUtility::getProcessedValue($table, $fieldName, $defaultLanguageRow[$fieldName], 0, true, false, 0, true, 0, $defaultLanguageDiffRow)
);
$html[] = '<div class="t3-form-original-language-diff">';
$html[] = '<div class="t3-form-original-language-diffheader">';
$html[] = htmlspecialchars($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.changeInOrig'));
$html[] = '</div>';
$html[] = '<div class="t3-form-original-language-diffcontent">';
$html[] = '<div class="diff">';
$html[] = '<div class="diff-item">';
$html[] = '<div class="diff-item-result diff-item-result-inline">' . $diffResult . '</div>';
$html[] = '</div>';
$html[] = '</div>';
$html[] = '</div>';
$html[] = '</div>';
}
$result['html'] = implode(LF, $html);
return $result;
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,154 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\DataHandling\Localization\State;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
use TYPO3\CMS\Core\Utility\StringUtility;
/**
* Allows to define the localization state per field.
*/
class LocalizationStateSelector extends AbstractNode
{
/**
* Render the radio buttons if enabled
*/
public function render(): array
{
$languageService = $this->getLanguageService();
$result = $this->initializeResultArray();
$fieldName = $this->data['fieldName'];
$fieldId = StringUtility::getUniqueId('formengine-localization-state-selector-');
$l10nStateFieldName = 'l10n_state';
$localizationState = State::fromJSON(
$this->data['tableName'],
$this->data['databaseRow'][$l10nStateFieldName] ?? null
);
if (
$localizationState === null
|| !isset($this->data['defaultLanguageRow'])
|| !isset($this->data['processedTca']['columns'][$fieldName]['config']['behaviour']['allowLanguageSynchronization'])
|| !$this->data['processedTca']['columns'][$fieldName]['config']['behaviour']['allowLanguageSynchronization']
) {
return $result;
}
$l10nParentFieldName = $this->data['processedTca']['ctrl']['transOrigPointerField'] ?? null;
$l10nSourceFieldName = $this->data['processedTca']['ctrl']['translationSource'] ?? null;
$sourceLanguageTitle = '';
$fieldValueInParentRow = '';
$fieldValueInSourceRow = null;
if ($l10nParentFieldName && $this->data['databaseRow'][$l10nParentFieldName] > 0) {
if ($l10nSourceFieldName && $this->data['databaseRow'][$l10nSourceFieldName] > 0) {
$languageField = $this->data['processedTca']['ctrl']['languageField'] ?? null;
if ($languageField
&& isset($this->data['sourceLanguageRow'][$languageField])
&& $this->data['sourceLanguageRow'][$languageField] > 0
) {
$languageUidOfSourceRow = $this->data['sourceLanguageRow'][$languageField];
$sourceLanguageTitle = $this->data['systemLanguageRows'][$languageUidOfSourceRow]['title'] ?? '';
$fieldValueInSourceRow = $this->data['sourceLanguageRow'][$fieldName] ?? null;
}
}
$fieldValueInParentRow = (string)$this->data['defaultLanguageRow'][$fieldName];
}
$fieldElementName = 'data[' . htmlspecialchars($this->data['tableName']) . ']'
. '[' . htmlspecialchars((string)$this->data['databaseRow']['uid']) . ']'
. '[' . htmlspecialchars($l10nStateFieldName) . ']'
. '[' . htmlspecialchars($this->data['fieldName']) . ']';
$html = [];
$html[] = '<div class="t3js-l10n-state-container">';
$html[] = '<div class="form-label">';
$html[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:localizationStateSelector.header');
$html[] = '</div>';
$html[] = '<div class="form-check">';
$html[] = '<input';
$html[] = ' id="' . $fieldId . '-custom"';
$html[] = ' type="radio"';
$html[] = ' name="' . htmlspecialchars($fieldElementName) . '"';
$html[] = ' class="form-check-input t3js-l10n-state-custom"';
$html[] = ' value="custom"';
$html[] = $localizationState->isCustomState($fieldName) ? ' checked="checked"' : '';
$html[] = ' data-original-language-value=""';
$html[] = '>';
$html[] = '<label';
$html[] = ' for="' . $fieldId . '-custom"';
$html[] = ' class="form-check-label"';
$html[] = '>';
$html[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:localizationStateSelector.customValue');
$html[] = '</label>';
$html[] = '</div>';
$html[] = '<div class="form-check">';
$html[] = '<input';
$html[] = ' id="' . $fieldId . '-parent"';
$html[] = ' type="radio"';
$html[] = ' name="' . htmlspecialchars($fieldElementName) . '"';
$html[] = ' class="form-check-input"';
$html[] = ' value="parent"';
$html[] = $localizationState->isParentState($fieldName) ? ' checked="checked"' : '';
$html[] = ' data-original-language-value="' . htmlspecialchars((string)$fieldValueInParentRow) . '"';
$html[] = '>';
$html[] = '<label';
$html[] = ' for="' . $fieldId . '-parent"';
$html[] = ' class="form-check-label"';
$html[] = '>';
$html[] = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:localizationStateSelector.defaultLanguageValue');
$html[] = '</label>';
$html[] = '</div>';
if ($fieldValueInSourceRow !== null) {
$html[] = '<div class="form-check">';
$html[] = '<input';
$html[] = ' id="' . $fieldId . '-source"';
$html[] = ' type="radio"';
$html[] = ' name="' . htmlspecialchars($fieldElementName) . '"';
$html[] = ' class="form-check-input"';
$html[] = ' value="source"';
$html[] = $localizationState->isSourceState($fieldName) ? ' checked="checked"' : '';
$html[] = ' data-original-language-value="' . htmlspecialchars((string)$fieldValueInSourceRow) . '"';
$html[] = '>';
$html[] = '<label';
$html[] = ' for="' . $fieldId . '-source"';
$html[] = ' class="form-check-label"';
$html[] = '>';
$html[] = sprintf($languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:localizationStateSelector.sourceLanguageValue'), htmlspecialchars($sourceLanguageTitle));
$html[] = '</label>';
$html[] = '</div>';
}
$html[] = '</div>';
$result['javaScriptModules'][] = JavaScriptModuleInstruction::create(
'@typo3/backend/form-engine/field-wizard/localization-state-selector.js'
)->instance($fieldElementName);
$result['html'] = implode(LF, $html);
return $result;
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,117 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Render values of "other" languages. If editing a localized row, this is typically
* the content value of the according default record, but it may render field values
* of other languages too, depending on configuration.
*/
class OtherLanguageContent extends AbstractNode
{
public function __construct(
private readonly IconFactory $iconFactory,
) {}
/**
* Render other language content if enabled.
*/
public function render(): array
{
$result = $this->initializeResultArray();
$fieldName = $this->data['fieldName'];
$fieldConfig = $this->data['processedTca']['columns'][$fieldName];
$fieldType = $fieldConfig['config']['type'];
$l10nDisplay = $this->data['parameterArray']['fieldConf']['l10n_display'] ?? '';
$defaultLanguageRow = $this->data['defaultLanguageRow'] ?? null;
if (!is_array($defaultLanguageRow)
|| GeneralUtility::inList($l10nDisplay, 'hideDiff')
|| GeneralUtility::inList($l10nDisplay, 'defaultAsReadonly')
|| $fieldType === 'inline'
|| $fieldType === 'file'
|| $fieldType === 'flex'
|| (in_array($fieldType, ['select', 'category', 'group'], true) && isset($fieldConfig['config']['MM']))
) {
// Early return if there is no default language row or the display is disabled
return $result;
}
$table = $this->data['tableName'];
$html = [];
$defaultLanguageValue = BackendUtility::getProcessedValue(
$table,
$fieldName,
$defaultLanguageRow[$fieldName],
0,
true,
false,
$defaultLanguageRow['uid'],
true,
$defaultLanguageRow['pid'],
$defaultLanguageRow
) ?? '';
if ($defaultLanguageValue !== '') {
$iconIdentifier = ($this->data['systemLanguageRows'][0]['flagIconIdentifier'] ?? false) ?: 'flags-multiple';
$html[] = '<div class="t3-form-original-language">';
$html[] = $this->iconFactory->getIcon($iconIdentifier, IconSize::SMALL)->render();
$html[] = $this->previewFieldValue($defaultLanguageValue);
$html[] = '</div>';
}
$additionalPreviewLanguages = $this->data['additionalLanguageRows'];
foreach ($additionalPreviewLanguages as $previewLanguage) {
$defaultLanguageValue = BackendUtility::getProcessedValue(
$table,
$fieldName,
$previewLanguage[$fieldName],
0,
true,
false,
0,
true,
0,
$previewLanguage
) ?? '';
if ($defaultLanguageValue !== '') {
$html[] = '<div class="t3-form-original-language">';
$html[] = $this->iconFactory->getIcon($this->data['systemLanguageRows'][$previewLanguage['language_tag']]['flagIconIdentifier'], IconSize::SMALL)->render();
$html[] = $this->previewFieldValue($defaultLanguageValue);
$html[] = '</div>';
}
}
$result['html'] = implode(LF, $html);
return $result;
}
/**
* Rendering preview output of a field value which is not shown as a form field but just outputted.
*
* @param string $value The value to output
* @return string HTML formatted output
*/
protected function previewFieldValue($value)
{
return nl2br(htmlspecialchars((string)$value));
}
}
@@ -0,0 +1,117 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
use TYPO3\CMS\Core\Resource\ProcessedFile;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Render cropped thumbnails for default and additional preview
* languages by respecting the corresponding cropping configuration.
*
* @internal
*/
class OtherLanguageThumbnails extends AbstractNode
{
public function __construct(
private readonly IconFactory $iconFactory,
private readonly ResourceFactory $resourceFactory,
) {}
/**
* Render cropped thumbnails from other language rows
*/
public function render(): array
{
$result = $this->initializeResultArray();
$fieldConfig = $this->data['parameterArray']['fieldConf'];
$l10nDisplay = $fieldConfig['l10n_display'] ?? '';
$cropVariants = $fieldConfig['config']['cropVariants'] ?? ['default' => []];
$defaultLanguageRow = $this->data['defaultLanguageRow'] ?? null;
if (!is_array($defaultLanguageRow)
|| !is_array($cropVariants)
|| $cropVariants === []
|| $fieldConfig['config']['type'] !== 'imageManipulation'
|| GeneralUtility::inList($l10nDisplay, 'hideDiff')
|| GeneralUtility::inList($l10nDisplay, 'defaultAsReadonly')
) {
return $result;
}
$html = [];
$languages = [$defaultLanguageRow['language_tag'] => $defaultLanguageRow] + ($this->data['additionalLanguageRows'] ?? []);
foreach ($languages as $sysLanguageUid => $languageRow) {
$file = null;
$fileUid = (int)($languageRow['uid_local'] ?? 0);
if (!$fileUid) {
continue;
}
try {
$file = $this->resourceFactory->getFileObject($fileUid);
} catch (FileDoesNotExistException|\InvalidArgumentException $e) {
continue;
}
$processedImages = [];
$cropVariantCollection = CropVariantCollection::create((string)($languageRow['crop'] ?? ''), $cropVariants);
foreach (array_keys($cropVariants) as $variant) {
$processedFileUrl = $file
->process(
ProcessedFile::CONTEXT_IMAGECROPSCALEMASK,
[
'maxWidth' => '145',
'maxHeight' => '45',
'crop' => $cropVariantCollection->getCropArea($variant)->makeAbsoluteBasedOnFile($file),
]
)
->getPublicUrl() ?? '';
if ($processedFileUrl !== '') {
$processedImages[] = '<img ' . GeneralUtility::implodeAttributes([
'loading' => 'lazy',
'src' => $processedFileUrl,
'alt' => $languageRow['alternative'] ?? $file->getProperty('alternative') ?? '',
'title' => $languageRow['title'] ?? $file->getProperty('title') ?? '',
], true) . ' />';
}
}
if ($processedImages !== []) {
$iconIdentifier = $this->data['systemLanguageRows'][(int)$sysLanguageUid]['flagIconIdentifier'] ?? 'flags-multiple';
$html[] = '<div class="t3-form-original-language">';
$html[] = $this->iconFactory->getIcon($iconIdentifier, IconSize::SMALL)->render();
$html[] = implode(LF, $processedImages);
$html[] = '</div>';
}
}
$result['html'] = implode(LF, $html);
return $result;
}
}
@@ -0,0 +1,106 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
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\Type\Bitmask\Permission;
/**
* Render details of selected records,
* typically used with type='group'.
*/
class RecordsOverview extends AbstractNode
{
public function __construct(
private readonly IconFactory $iconFactory,
) {}
/**
* Render table with record details
*/
public function render(): array
{
$languageService = $this->getLanguageService();
$result = $this->initializeResultArray();
$parameterArray = $this->data['parameterArray'];
$selectedItems = $parameterArray['itemFormElValue'];
$recordsOverviewHtml = [];
foreach ($selectedItems as $selectedItem) {
$title = (string)$selectedItem['title'];
if (empty($title)) {
$title = '[' . $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title') . ']';
}
$shortenedTitle = BackendUtility::cropToTitleLength($title);
$linkedIcon = BackendUtility::wrapClickMenuOnIcon(
$this->iconFactory->getIconForRecord($selectedItem['table'], $selectedItem['row'], IconSize::SMALL)->render(),
$selectedItem['table'],
$selectedItem['uid']
);
$linkedTitle = BackendUtility::wrapClickMenuOnIcon(
htmlspecialchars($shortenedTitle),
$selectedItem['table'],
$selectedItem['uid']
);
$pathToContainingPage = BackendUtility::getRecordPath($selectedItem['row']['pid'], $this->getBackendUserAuthentication()->getPagePermsClause(Permission::PAGE_SHOW), 0);
$recordsOverviewHtml[] = '<tr>';
$recordsOverviewHtml[] = '<td class="col-icon">';
$recordsOverviewHtml[] = $linkedIcon;
$recordsOverviewHtml[] = '</td>';
$recordsOverviewHtml[] = '<td class="col-title">';
$recordsOverviewHtml[] = $linkedTitle;
$recordsOverviewHtml[] = '<span class="text-variant">';
$recordsOverviewHtml[] = ' [' . $selectedItem['uid'] . ']';
$recordsOverviewHtml[] = ' ' . htmlspecialchars($pathToContainingPage);
$recordsOverviewHtml[] = '</span>';
$recordsOverviewHtml[] = '</td>';
$recordsOverviewHtml[] = '</tr>';
}
$html = [];
if (!empty($recordsOverviewHtml)) {
$html[] = '<div class="table-fit mt-1">';
$html[] = '<table class="table">';
$html[] = '<tbody>';
$html[] = implode(LF, $recordsOverviewHtml);
$html[] = '</tbody>';
$html[] = '</table>';
$html[] = '</div>';
}
$result['html'] = implode(LF, $html);
return $result;
}
protected function getBackendUserAuthentication(): BackendUserAuthentication
{
return $GLOBALS['BE_USER'];
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
+75
View File
@@ -0,0 +1,75 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
/**
* Render thumbnails of icons,
* typically used with type=select.
*/
class SelectIcons extends AbstractNode
{
/**
* Render thumbnails of selected files
*/
public function render(): array
{
$selectIcons = [];
$result = $this->initializeResultArray();
$parameterArray = $this->data['parameterArray'];
$selectItems = $parameterArray['fieldConf']['config']['items'];
$selectItemCounter = 0;
foreach ($selectItems as $item) {
if ($item['value'] === '--div--') {
continue;
}
$icon = !empty($item['icon']) ? FormEngineUtility::getIconHtml($item['icon'], $item['label'], $item['label']) : '';
if ($icon) {
$fieldValue = $this->data['databaseRow'][$this->data['fieldName']];
$selectIcons[] = [
'title' => $item['label'],
'active' => ($fieldValue[0] ?? false) === (string)($item['value'] ?? ''),
'icon' => $icon,
'index' => $selectItemCounter,
];
}
$selectItemCounter++;
}
$html = [];
if (!empty($selectIcons)) {
$html[] = '<div class="t3js-forms-select-single-icons form-wizard-icon-list">';
foreach ($selectIcons as $selectIcon) {
$active = $selectIcon['active'] ? ' active' : '';
$html[] = '<div class="form-wizard-icon-list-item">';
$html[] = '<button type="button" class="' . $active . '" title="' . htmlspecialchars($selectIcon['title'], ENT_COMPAT, 'UTF-8', false) . '" data-select-index="' . htmlspecialchars((string)$selectIcon['index']) . '">';
$html[] = $selectIcon['icon'];
$html[] = '</button>';
$html[] = '</div>';
}
$html[] = '</div>';
}
$result['html'] = implode(LF, $html);
return $result;
}
}
@@ -0,0 +1,38 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Core\Page\JavaScriptModuleInstruction;
/**
* Field wizard to toggle field required state for the "shortcut" field.
*
* This element injects a JS module so the client can re-evaluate
* the required state when "shortcut_mode" changes.
*/
class ShortcutValidation extends AbstractNode
{
public function render(): array
{
$result['javaScriptModules']['requiredByCondition'] = JavaScriptModuleInstruction::create(
'@typo3/backend/form-engine/field-wizard/shortcut-validation.js'
);
return $result;
}
}
+152
View File
@@ -0,0 +1,152 @@
<?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\Backend\Form\FieldWizard;
use TYPO3\CMS\Backend\Form\AbstractNode;
use TYPO3\CMS\Backend\Form\Utility\FormEngineUtility;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Imaging\IconSize;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Render list of tables and link to element browser,
* typically used with type=group and internal_type=db.
*/
class TableList extends AbstractNode
{
public function __construct(
private readonly IconFactory $iconFactory,
) {}
/**
* Render table buttons
*/
public function render(): array
{
$languageService = $this->getLanguageService();
$result = $this->initializeResultArray();
$parameterArray = $this->data['parameterArray'];
$config = $parameterArray['fieldConf']['config'];
$itemName = $parameterArray['itemFormElName'];
if (empty($config['allowed']) || !is_string($config['allowed'])) {
// No handling if the field has no, or funny "allowed" settings.
return $result;
}
$allowed = GeneralUtility::trimExplode(',', $config['allowed'], true);
$allowedTablesHtml = [];
foreach ($allowed as $tableName) {
if ($tableName === '*') {
$label = $languageService->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.allTables');
$allowedTablesHtml[] = '<span>';
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</span>';
} else {
$tableSchema = $this->data['tcaSchemata']->has($tableName) ? $this->data['tcaSchemata']->get($tableName) : null;
$label = $languageService->sL($tableSchema?->getTitle() ?? '');
$icon = $this->iconFactory->getIconForRecord($tableName, [], IconSize::SMALL)->render();
if ((bool)($config['fieldControl']['elementBrowser']['disabled'] ?? false)) {
$allowedTablesHtml[] = '<span class="tablelist-item-nolink">';
$allowedTablesHtml[] = $icon;
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</span>';
} else {
// Initialize attributes
$attributes = [
'class' => 'btn btn-default t3js-element-browser',
'data-mode' => $tableName === 'sys_file' ? 'file' : 'db',
'data-field-reference' => $itemName,
'data-allowed-types' => $tableName,
];
// Add the entry point - if found
$attributes = $this->addEntryPoint($tableName, $config, $attributes);
$allowedTablesHtml[] = '<button ' . GeneralUtility::implodeAttributes($attributes, true) . '>';
$allowedTablesHtml[] = $icon;
$allowedTablesHtml[] = htmlspecialchars($label);
$allowedTablesHtml[] = '</button>';
}
}
}
$html = [];
$html[] = '<div class="form-text">';
$html[] = implode(LF, $allowedTablesHtml);
$html[] = '</div>';
$result['html'] = implode(LF, $html);
return $result;
}
/**
* Try to resolve a configured default entry point - page / folder
* to be expanded - and add it to the attributes if found.
*/
protected function addEntryPoint(string $tableName, array $fieldConfig, array $attributes): array
{
if (!isset($fieldConfig['elementBrowserEntryPoints']) || !is_array($fieldConfig['elementBrowserEntryPoints'])) {
// Early return in case no entry points are defined
return $attributes;
}
// Fetch the configured value (which might be a marker) - falls back to _default
$entryPoint = (string)($fieldConfig['elementBrowserEntryPoints'][$tableName] ?? $fieldConfig['elementBrowserEntryPoints']['_default'] ?? '');
if ($entryPoint === '') {
// In case no entry point exists for the given table and also no default is defined, return
return $attributes;
}
// Check and resolve possible marker
if (str_starts_with($entryPoint, '###') && str_ends_with($entryPoint, '###')) {
if ($entryPoint === '###CURRENT_PID###') {
// Use the current pid
$entryPoint = (string)$this->data['effectivePid'];
} elseif ($entryPoint === '###SITEROOT###' && ($this->data['site'] ?? null) instanceof Site) {
// Use the root page id from the current site
$entryPoint = (string)$this->data['site']->getRootPageId();
} else {
// Check for special TSconfig marker
$TSconfig = FormEngineUtility::getTCEFORM_TSconfig($this->data['tableName'], ['pid' => $this->data['effectivePid']]);
$keyword = substr($entryPoint, 3, -3);
if (str_starts_with($keyword, 'PAGE_TSCONFIG_')) {
$entryPoint = (string)($TSconfig[$this->data['fieldName']][$keyword] ?? '');
} else {
$entryPoint = (string)($TSconfig['_' . $keyword] ?? '');
}
}
}
// Add the entry point to the attribute - if resolved
if ($entryPoint !== '') {
$attributes['data-entry-point'] = $entryPoint;
}
return $attributes;
}
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}