first commit

This commit is contained in:
salam committed 2026-10-19 08:18:39 +02:00
commit 1218dd1517
12716 files changed
+989233

No files matched your search

@@ -0,0 +1,379 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Utility\ArrayUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessing;
use TYPO3\CMS\Form\Domain\Configuration\ArrayProcessing\ArrayProcessor;
use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService;
use TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\FinisherOptionGenerator;
use TYPO3\CMS\Form\Domain\Configuration\FlexformConfiguration\Processors\ProcessorDto;
use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
use TYPO3\CMS\Form\Service\TranslationService;
/**
* Hooks into flex form handling of backend for tt_content form elements:
*
* * Adds existing forms to flex form drop down list
* * Adds finisher settings if "override finishers" is active
*
* Scope: backend
* @internal
*/
class DataStructureIdentifierHook
{
/**
* Localisation prefix
*/
const L10N_PREFIX = 'LLL:EXT:form/Resources/Private/Language/Database.xlf:';
/**
* The data structure depends on a current form selection (persistenceIdentifier)
* and if the field "overrideFinishers" is active. Add both to the identifier to
* hand these information over to parseDataStructureByIdentifierPostProcess() hook.
*
* @param array $fieldTca Incoming field TCA
* @param string $tableName Handled table
* @param string $fieldName Handled field
* @param array $row Current data row
* @param array $identifier Already calculated identifier
* @return array Modified identifier
*/
public function getDataStructureIdentifierPostProcess(
array $fieldTca,
string $tableName,
string $fieldName,
array $row,
array $identifier
): array {
if ($tableName === 'tt_content' && $fieldName === 'pi_flexform' && $row['CType'] === 'form_formframework') {
$currentFlexData = [];
if (!empty($row['pi_flexform']) && !\is_array($row['pi_flexform'])) {
$currentFlexData = GeneralUtility::xml2array($row['pi_flexform']);
}
// Add selected form value
$identifier['ext-form-persistenceIdentifier'] = '';
if (!empty($currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'])) {
$identifier['ext-form-persistenceIdentifier'] = $currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'];
}
// Add bool - finisher override active or not
$identifier['ext-form-overrideFinishers'] = false;
if (
isset($currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF'])
&& (int)$currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF'] === 1
) {
$identifier['ext-form-overrideFinishers'] = true;
}
}
return $identifier;
}
/**
* Returns a modified flexform data array.
*
* This adds the list of existing form definitions to the form selection drop down
* and adds sheets to override finisher settings if requested.
*
* @param array $dataStructure
* @param array $identifier
* @return array
*/
public function parseDataStructureByIdentifierPostProcess(array $dataStructure, array $identifier): array
{
if (isset($identifier['ext-form-persistenceIdentifier'])) {
try {
// Add list of existing forms to drop down if we find our key in the identifier
$formPersistenceManager = GeneralUtility::makeInstance(ObjectManager::class)->get(FormPersistenceManagerInterface::class);
$formIsAccessible = false;
foreach ($formPersistenceManager->listForms() as $form) {
$invalidFormDefinition = $form['invalid'] ?? false;
$hasDeprecatedFileExtension = $form['deprecatedFileExtension'] ?? false;
if ($form['location'] === 'storage' && $hasDeprecatedFileExtension) {
continue;
}
if ($form['persistenceIdentifier'] === $identifier['ext-form-persistenceIdentifier']) {
$formIsAccessible = true;
}
if ($invalidFormDefinition || $hasDeprecatedFileExtension) {
$dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [
$form['name'] . ' (' . $form['persistenceIdentifier'] . ')',
$form['persistenceIdentifier'],
'overlay-missing'
];
} else {
$dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [
$form['name'] . ' (' . $form['persistenceIdentifier'] . ')',
$form['persistenceIdentifier'],
'content-form'
];
}
}
if (!empty($identifier['ext-form-persistenceIdentifier']) && !$formIsAccessible) {
$dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [
sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.inaccessiblePersistenceIdentifier'),
$identifier['ext-form-persistenceIdentifier']
),
$identifier['ext-form-persistenceIdentifier'],
];
}
// If a specific form is selected and if finisher override is active, add finisher sheets
if (!empty($identifier['ext-form-persistenceIdentifier'])
&& $formIsAccessible
&& isset($identifier['ext-form-overrideFinishers'])
&& $identifier['ext-form-overrideFinishers'] === true
) {
$persistenceIdentifier = $identifier['ext-form-persistenceIdentifier'];
$formDefinition = $formPersistenceManager->load($persistenceIdentifier);
$newSheets = $this->getAdditionalFinisherSheets($persistenceIdentifier, $formDefinition);
ArrayUtility::mergeRecursiveWithOverrule(
$dataStructure,
$newSheets
);
}
} catch (NoSuchFileException $e) {
$dataStructure = $this->addSelectedPersistenceIdentifier($identifier['ext-form-persistenceIdentifier'], $dataStructure);
$this->addInvalidFrameworkConfigurationFlashMessage($e);
} catch (ParseErrorException $e) {
$dataStructure = $this->addSelectedPersistenceIdentifier($identifier['ext-form-persistenceIdentifier'], $dataStructure);
$this->addInvalidFrameworkConfigurationFlashMessage($e);
}
}
return $dataStructure;
}
/**
* Returns additional flexform sheets with finisher fields
*
* @param string $persistenceIdentifier Current persistence identifier
* @param array $formDefinition The form definition
* @return array
*/
protected function getAdditionalFinisherSheets(string $persistenceIdentifier, array $formDefinition): array
{
if (!isset($formDefinition['finishers']) || empty($formDefinition['finishers'])) {
return [];
}
$prototypeName = $formDefinition['prototypeName'] ?? 'standard';
$prototypeConfiguration = GeneralUtility::makeInstance(ObjectManager::class)
->get(ConfigurationService::class)
->getPrototypeConfiguration($prototypeName);
if (!isset($prototypeConfiguration['finishersDefinition']) || empty($prototypeConfiguration['finishersDefinition'])) {
return [];
}
$formIdentifier = $formDefinition['identifier'];
$finishersDefinition = $prototypeConfiguration['finishersDefinition'];
$sheets = ['sheets' => []];
foreach ($formDefinition['finishers'] as $formFinisherDefinition) {
$finisherIdentifier = $formFinisherDefinition['identifier'];
if (!isset($finishersDefinition[$finisherIdentifier]['FormEngine']['elements'])) {
continue;
}
$sheetIdentifier = $this->buildFlexformSheetIdentifier(
$persistenceIdentifier,
$prototypeName,
$formIdentifier,
$finisherIdentifier
);
$finishersDefinition = $this->translateFinisherDefinitionByIdentifier(
$finisherIdentifier,
$finishersDefinition,
$prototypeConfiguration
);
$prototypeFinisherDefinition = $finishersDefinition[$finisherIdentifier];
$finisherLabel = $prototypeFinisherDefinition['FormEngine']['label'] ?? '';
$sheet = $this->initializeNewSheetArray($sheetIdentifier, $finisherLabel);
$converterDto = GeneralUtility::makeInstance(
ProcessorDto::class,
$finisherIdentifier,
$prototypeFinisherDefinition,
$formFinisherDefinition
);
// Iterate over all `TYPO3.CMS.Form.prototypes.<prototypeName>.finishersDefinition.<finisherIdentifier>.FormEngine.elements` values
// and convert them to FlexForm elements
GeneralUtility::makeInstance(ArrayProcessor::class, $prototypeFinisherDefinition['FormEngine']['elements'])->forEach(
GeneralUtility::makeInstance(
ArrayProcessing::class,
'convertToFlexFormSheets',
'^(.*)\.config\.type$',
GeneralUtility::makeInstance(FinisherOptionGenerator::class, $converterDto)
)
);
$sheet[$sheetIdentifier]['ROOT']['el'] = $converterDto->getResult();
ArrayUtility::mergeRecursiveWithOverrule($sheets['sheets'], $sheet);
}
if (empty($sheets['sheets'])) {
return [];
}
return $sheets;
}
/**
* Boilerplate XML array of a new sheet
*
* @param string $sheetIdentifier
* @param string $finisherName
* @throws \InvalidArgumentException
* @return array
*/
protected function initializeNewSheetArray(string $sheetIdentifier, string $finisherName): array
{
if (empty($sheetIdentifier)) {
throw new \InvalidArgumentException('$sheetIdentifier must not be empty.', 1472060918);
}
if (empty($finisherName)) {
throw new \InvalidArgumentException('$finisherName must not be empty.', 1472060919);
}
return [
$sheetIdentifier => [
'ROOT' => [
'TCEforms' => [
'sheetTitle' => $finisherName,
],
'type' => 'array',
'el' => [],
],
],
];
}
/**
* @param string $persistenceIdentifier
* @param array $dataStructure
* @return array
*/
protected function addSelectedPersistenceIdentifier(string $persistenceIdentifier, array $dataStructure): array
{
if (!empty($persistenceIdentifier)) {
$dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [
sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.inaccessiblePersistenceIdentifier'),
$persistenceIdentifier
),
$persistenceIdentifier,
];
}
return $dataStructure;
}
/**
* @param \Exception $e
*/
protected function addInvalidFrameworkConfigurationFlashMessage(\Exception $e)
{
$messageText = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidFrameworkConfiguration.text'),
$e->getMessage()
);
GeneralUtility::makeInstance(ObjectManager::class)
->get(FlashMessageService::class)
->getMessageQueueByIdentifier('core.template.flashMessages')
->enqueue(
GeneralUtility::makeInstance(
FlashMessage::class,
$messageText,
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidFrameworkConfiguration.title'),
AbstractMessage::ERROR,
true
)
);
}
/**
* @param string $persistenceIdentifier
* @param string $prototypeName
* @param string $formIdentifier
* @param string $finisherIdentifier
* @return string
*/
protected function buildFlexformSheetIdentifier(
string $persistenceIdentifier,
string $prototypeName,
string $formIdentifier,
string $finisherIdentifier
): string {
return md5(
implode('', [
$persistenceIdentifier,
$prototypeName,
$formIdentifier,
$finisherIdentifier
])
);
}
/**
* @param string $finisherIdentifier
* @param array $finishersDefinition
* @param array $prototypeConfiguration
* @return array
*/
protected function translateFinisherDefinitionByIdentifier(
string $finisherIdentifier,
array $finishersDefinition,
array $prototypeConfiguration
): array {
if (isset($finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'])) {
$translationFile = $finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile'];
} else {
$translationFile = $prototypeConfiguration['formEngine']['translationFile'];
}
$finishersDefinition[$finisherIdentifier]['FormEngine'] = TranslationService::getInstance()->translateValuesRecursive(
$finishersDefinition[$finisherIdentifier]['FormEngine'],
$translationFile
);
return $finishersDefinition;
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,52 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Filelist\FileList;
use TYPO3\CMS\Filelist\FileListEditIconHookInterface;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager;
/**
* @internal
*/
class FileListEditIconsHook implements FileListEditIconHookInterface
{
/**
* Modifies edit icon array
*
* @param array $cells
* @param FileList $parentObject
*/
public function manipulateEditIcons(&$cells, &$parentObject)
{
$fileOrFolderObject = $cells['__fileOrFolderObject'];
$fullIdentifier = $fileOrFolderObject->getCombinedIdentifier();
$isFormDefinition = StringUtility::endsWith($fullIdentifier, FormPersistenceManager::FORM_DEFINITION_FILE_EXTENSION);
if (!$isFormDefinition) {
return;
}
$disableIconNames = ['edit', 'view', 'replace', 'rename'];
foreach ($disableIconNames as $disableIconName) {
if (!empty($cells[$disableIconName])) {
$cells[$disableIconName] = $parentObject->spaceIcon;
}
}
}
}
@@ -0,0 +1,99 @@
<?php
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Validation\Error;
use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface;
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
use TYPO3\CMS\Form\Service\TranslationService;
/**
* Scope: frontend
* @internal
*/
class FormElementHooks
{
/**
* This hook is invoked by the FormRuntime for each form element
* **after** a form page was submitted but **before** values are
* property-mapped, validated and pushed within the FormRuntime's `FormState`.
*
* @param FormRuntime $formRuntime
* @param RenderableInterface $renderable
* @param mixed $elementValue submitted value of the element *before post processing*
* @param array $requestArguments submitted raw request values
* @return mixed
* @see FormRuntime::mapAndValidate()
* @internal
*/
public function afterSubmit(FormRuntime $formRuntime, RenderableInterface $renderable, $elementValue, array $requestArguments = [])
{
if ($renderable->getType() === 'AdvancedPassword') {
if ($elementValue['password'] !== $elementValue['confirmation']) {
$processingRule = $renderable->getRootForm()->getProcessingRule($renderable->getIdentifier());
$processingRule->getProcessingMessages()->addError(
GeneralUtility::makeInstance(ObjectManager::class)
->get(
Error::class,
TranslationService::getInstance()->translate('validation.error.1556283177', null, 'EXT:form/Resources/Private/Language/locallang.xlf'),
1556283177
)
);
}
$elementValue = $elementValue['password'];
}
return $elementValue;
}
/**
* This is a hook that is invoked by the rendering system **before**
* the corresponding element is rendered.
*
* @param FormRuntime $formRuntime
* @param RootRenderableInterface $renderable
*/
public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterface $renderable)
{
if ($renderable->getType() === 'Date') {
$date = $formRuntime[$renderable->getIdentifier()];
if ($date instanceof \DateTime) {
// @see https://www.w3.org/TR/2011/WD-html-markup-20110405/input.date.html#input.date.attrs.value
// 'Y-m-d' = https://tools.ietf.org/html/rfc3339#section-5.6 -> full-date
$formRuntime[$renderable->getIdentifier()] = $date->format('Y-m-d');
}
}
}
/**
* This hook is invoked whenever a form element is created.
* Note that this hook will be called **after** all properties from the
* prototype configuration are set in the form element but **before**
* the properties from the form definition are set in the form element.
*
* @param RenderableInterface $renderable
*/
public function initializeFormElement(RenderableInterface $renderable)
{
if ($renderable->getType() === 'Date' || $renderable->getType() === 'DatePicker') {
// Set the property mapping type for the `Date` and `DatePicker` element.
$renderable->setDataType('DateTime');
}
}
}
@@ -0,0 +1,825 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use Symfony\Component\Console\Output\OutputInterface;
use TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Database\ReferenceIndex;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Resource\DuplicationBehavior;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\PathUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager;
use TYPO3\CMS\Form\Slot\FilePersistenceSlot;
use TYPO3\CMS\Install\Updates\ChattyInterface;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\ReferenceIndexUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
/**
* Update wizard to migrate all forms currently in use to new ending
* @internal
*/
class FormFileExtensionUpdate implements ChattyInterface, UpgradeWizardInterface
{
/**
* @var OutputInterface
*/
protected $output;
/**
* @var FormPersistenceManager
*/
protected $persistenceManager;
/**
* @var ResourceFactory
*/
protected $resourceFactory;
/**
* @var ReferenceIndex
*/
protected $referenceIndex;
/**
* @var FlexFormTools
*/
protected $flexFormTools;
/**
* @var Connection
*/
protected $connection;
/**
* Return the identifier for this wizard
* This should be the same string as used in the ext_localconf class registration
*
* @return string
*/
public function getIdentifier(): string
{
return 'formFileExtension';
}
/**
* Return the speaking name of this wizard
*
* @return string
*/
public function getTitle(): string
{
return 'Rename form definition file extension from .yaml to .form.yaml';
}
/**
* Return the description for this wizard
*
* @return string
*/
public function getDescription(): string
{
return 'Form definition files need to be named *.form.yaml to have a way of distinguishing form yaml ' .
'configuration files from other yaml configuration files. This wizard will analyze and rename found files.';
}
/**
* Returns an array of class names of Prerequisite classes
* This way a wizard can define dependencies like "database up-to-date" or
* "reference index updated"
*
* @return string[]
*/
public function getPrerequisites(): array
{
return [
ReferenceIndexUpdatedPrerequisite::class,
DatabaseUpdatedPrerequisite::class
];
}
public function setOutput(OutputInterface $output): void
{
$this->output = $output;
}
/**
* Checks whether updates are required.
*
* @return bool Whether an update is required (TRUE) or not (FALSE)
*/
public function updateNecessary(): bool
{
$updateNeeded = false;
$this->persistenceManager = $this->getObjectManager()->get(FormPersistenceManager::class);
$this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
foreach ($this->getFormDefinitionsInformation() as $formDefinitionInformation) {
if (
(
$formDefinitionInformation['hasNewFileExtension'] === true
&& $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
&& $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
)
|| (
$formDefinitionInformation['hasNewFileExtension'] === false
&& $formDefinitionInformation['location'] === 'extension'
&& $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
&& $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
)
) {
continue;
}
if (
$formDefinitionInformation['hasNewFileExtension'] === false
&& $formDefinitionInformation['location'] === 'storage'
) {
$updateNeeded = true;
$this->output->writeln('Form definition files were found that should be migrated to be named .form.yaml.');
}
if (
$formDefinitionInformation['hasNewFileExtension']
&& $formDefinitionInformation['hasReferencesForOldFileExtension']
) {
$updateNeeded = true;
$this->output->writeln('Referenced form definition files found that should be updated.');
}
if (
$formDefinitionInformation['referencesForOldFileExtensionNeedsFlexformUpdates'] === true
|| $formDefinitionInformation['referencesForNewFileExtensionNeedsFlexformUpdates'] === true
) {
$updateNeeded = true;
if ($formDefinitionInformation['hasNewFileExtension'] === true) {
$this->output->writeln('Referenced form definition files found that should be updated.');
} elseif ($formDefinitionInformation['location'] === 'storage') {
$this->output->writeln('Referenced form definition files found that should be updated.');
} else {
$this->output->writeln(
'<warning>There are references to form definitions which are located in extensions and thus cannot be renamed automatically by this wizard.'
. 'This form definitions from extensions that do not end with .form.yaml have to be renamed by hand!'
. 'After that you can run this wizard again to migrate the references.</warning>'
);
}
}
}
return $updateNeeded;
}
/**
* Performs the accordant updates.
*
* @return bool Whether everything went smoothly or not
*/
public function executeUpdate(): bool
{
$success = true;
$GLOBALS['LANG'] = GeneralUtility::makeInstance(LanguageService::class);
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$filePersistenceSlot = GeneralUtility::makeInstance(FilePersistenceSlot::class);
$this->connection = $connectionPool->getConnectionForTable('tt_content');
$this->persistenceManager = $this->getObjectManager()->get(FormPersistenceManager::class);
$this->resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
$this->referenceIndex = GeneralUtility::makeInstance(ReferenceIndex::class);
$this->flexFormTools = GeneralUtility::makeInstance(FlexFormTools::class);
$filePersistenceSlot->defineInvocation(
FilePersistenceSlot::COMMAND_FILE_RENAME,
true
);
$formDefinitionsInformation = $this->getFormDefinitionsInformation();
foreach ($formDefinitionsInformation as $currentPersistenceIdentifier => $formDefinitionInformation) {
if (
(
$formDefinitionInformation['hasNewFileExtension'] === true
&& $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
&& $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
)
|| (
$formDefinitionInformation['hasNewFileExtension'] === false
&& $formDefinitionInformation['location'] === 'extension'
&& $formDefinitionInformation['hasReferencesForOldFileExtension'] === false
&& $formDefinitionInformation['hasReferencesForNewFileExtension'] === false
)
) {
continue;
}
if (
$formDefinitionInformation['hasNewFileExtension'] === true
&& (
$formDefinitionInformation['hasReferencesForOldFileExtension'] === true
|| $formDefinitionInformation['hasReferencesForNewFileExtension'] === true
)
) {
foreach ($formDefinitionInformation['referencesForOldFileExtension'] as $referenceForOldFileExtension) {
$newFlexformXml = $this->generateNewFlexformForReference(
$referenceForOldFileExtension,
$referenceForOldFileExtension['sheetIdentifiersWhichNeedsUpdate'],
$formDefinitionInformation['persistenceIdentifier']
);
$this->updateContentReference(
$referenceForOldFileExtension['ttContentUid'],
$newFlexformXml,
true
);
}
foreach ($formDefinitionInformation['referencesForNewFileExtension'] as $referenceForNewFileExtension) {
$newFlexformXml = $this->generateNewFlexformForReference(
$referenceForNewFileExtension,
$referenceForNewFileExtension['sheetIdentifiersWhichNeedsUpdate']
);
$this->updateContentReference(
$referenceForNewFileExtension['ttContentUid'],
$newFlexformXml
);
}
continue;
}
if ($formDefinitionInformation['location'] === 'storage') {
$file = $formDefinitionInformation['file'];
$newPossiblePersistenceIdentifier = $this->persistenceManager->getUniquePersistenceIdentifier(
$file->getNameWithoutExtension(),
$file->getParentFolder()->getCombinedIdentifier()
);
$newFileName = PathUtility::pathinfo(
$newPossiblePersistenceIdentifier,
PATHINFO_BASENAME
);
try {
$file->rename($newFileName, DuplicationBehavior::RENAME);
$newPersistenceIdentifier = $file->getCombinedIdentifier();
} catch (\Exception $e) {
$this->output->writeln(sprintf(
'<error>Failed to rename form definition "%s" to "%s".</error>',
$formDefinitionInformation['persistenceIdentifier'],
$newFileName
));
$success = false;
continue;
}
if (
$formDefinitionInformation['hasReferencesForOldFileExtension'] === true
|| $formDefinitionInformation['hasReferencesForNewFileExtension'] === true
) {
foreach ($formDefinitionInformation['referencesForOldFileExtension'] as $referenceForOldFileExtension) {
$sheetIdentifiersWhichNeedsUpdate = $this->getSheetIdentifiersWhichNeedsUpdate(
$referenceForOldFileExtension['flexform'],
$formDefinitionsInformation,
$currentPersistenceIdentifier,
$formDefinitionInformation['persistenceIdentifier'],
$newPersistenceIdentifier
);
$newFlexformXml = $this->generateNewFlexformForReference(
$referenceForOldFileExtension,
$sheetIdentifiersWhichNeedsUpdate,
$newPersistenceIdentifier
);
$this->updateContentReference(
$referenceForOldFileExtension['ttContentUid'],
$newFlexformXml
);
}
foreach ($formDefinitionInformation['referencesForNewFileExtension'] as $referenceForNewFileExtension) {
$sheetIdentifiersWhichNeedsUpdate = $this->getSheetIdentifiersWhichNeedsUpdate(
$referenceForNewFileExtension['flexform'],
$formDefinitionsInformation,
$currentPersistenceIdentifier,
$formDefinitionInformation['persistenceIdentifier'],
$newPersistenceIdentifier
);
$newFlexformXml = $this->generateNewFlexformForReference(
$referenceForNewFileExtension,
$sheetIdentifiersWhichNeedsUpdate,
$newPersistenceIdentifier
);
$this->updateContentReference(
$referenceForNewFileExtension['ttContentUid'],
$newFlexformXml
);
}
}
} else {
$success = false;
$this->output->writeln(sprintf(
'<error>Failed to rename form definition "%s" to "%s". You have to be rename it by hand!. '
. 'After that you can run this wizard again to migrate the references.</error>',
$formDefinitionInformation['persistenceIdentifier'],
$this->getNewPersistenceIdentifier($formDefinitionInformation['persistenceIdentifier'])
));
}
}
$filePersistenceSlot->defineInvocation(
FilePersistenceSlot::COMMAND_FILE_RENAME,
null
);
return $success;
}
/**
* @return array
*/
protected function getFormDefinitionsInformation(): array
{
$formDefinitionsInformation = array_merge(
$this->getFormDefinitionsInformationFromStorages(),
$this->getFormDefinitionsInformationFromExtensions()
);
$formDefinitionsInformation = $this->enrichFormDefinitionsInformationWithDataFromReferences($formDefinitionsInformation);
return $formDefinitionsInformation;
}
/**
* @return array
*/
protected function getFormDefinitionsInformationFromStorages(): array
{
$formDefinitionsInformation = [];
foreach ($this->persistenceManager->retrieveYamlFilesFromStorageFolders() as $file) {
$persistenceIdentifier = $file->getCombinedIdentifier();
$formDefinition = $this->getFormDefinition($file);
if (empty($formDefinition)) {
continue;
}
$formDefinitionsInformation[$persistenceIdentifier] = $this->setFormDefinitionInformationData(
$persistenceIdentifier,
$formDefinition,
$file,
'storage'
);
}
return $formDefinitionsInformation;
}
/**
* @return array
*/
protected function getFormDefinitionsInformationFromExtensions(): array
{
$formDefinitionsInformation = [];
foreach ($this->persistenceManager->retrieveYamlFilesFromExtensionFolders() as $persistenceIdentifier => $_) {
try {
$file = $this->resourceFactory->retrieveFileOrFolderObject($persistenceIdentifier);
} catch (\Exception $exception) {
continue;
}
$formDefinition = $this->getFormDefinition($file);
if (empty($formDefinition)) {
continue;
}
$formDefinitionsInformation[$persistenceIdentifier] = $this->setFormDefinitionInformationData(
$persistenceIdentifier,
$formDefinition,
$file,
'extension'
);
}
return $formDefinitionsInformation;
}
/**
* @param string $persistenceIdentifier
* @param array $formDefinition
* @param File $file
* @param string $localtion
* @return array
*/
protected function setFormDefinitionInformationData(
string $persistenceIdentifier,
array $formDefinition,
File $file,
string $localtion
): array {
return [
'location' => $localtion,
'persistenceIdentifier' => $persistenceIdentifier,
'prototypeName' => $formDefinition['prototypeName'],
'formIdentifier' => $formDefinition['identifier'],
'file' => $file,
'referencesForOldFileExtension' => [],
'referencesForNewFileExtension' => [],
'hasNewFileExtension' => $this->hasNewFileExtension($persistenceIdentifier),
'hasReferencesForOldFileExtension' => false,
'hasReferencesForNewFileExtension' => false,
'referencesForOldFileExtensionNeedsFlexformUpdates' => false,
'referencesForNewFileExtensionNeedsFlexformUpdates' => false,
];
}
/**
* @param array $formDefinitionsInformation
* @return array
*/
protected function enrichFormDefinitionsInformationWithDataFromReferences(array $formDefinitionsInformation): array
{
foreach ($this->getAllFlexformFieldsFromFormPlugins() as $pluginData) {
if (empty($pluginData['pi_flexform'])) {
continue;
}
$flexform = GeneralUtility::xml2array($pluginData['pi_flexform']);
$referencedPersistenceIdentifier = $this->getPersistenceIdentifierFromFlexform($flexform);
$referenceHasNewFileExtension = $this->hasNewFileExtension($referencedPersistenceIdentifier);
$possibleOldReferencedPersistenceIdentifier = $this->getOldPersistenceIdentifier($referencedPersistenceIdentifier);
$possibleNewReferencedPersistenceIdentifier = $this->getNewPersistenceIdentifier($referencedPersistenceIdentifier);
$referenceData = [
'scope' => null,
'ttContentUid' => (int)$pluginData['uid'],
'flexform' => $flexform,
'sheetIdentifiersWhichNeedsUpdate' => [],
];
$targetPersistenceIdentifier = null;
if (array_key_exists($referencedPersistenceIdentifier, $formDefinitionsInformation)) {
$targetPersistenceIdentifier = $referencedPersistenceIdentifier;
if ($referenceHasNewFileExtension) {
$referenceData['scope'] = 'referencesForNewFileExtension';
} else {
$referenceData['scope'] = 'referencesForOldFileExtension';
}
} else {
if ($referenceHasNewFileExtension) {
if (array_key_exists($possibleOldReferencedPersistenceIdentifier, $formDefinitionsInformation)) {
$targetPersistenceIdentifier = $possibleOldReferencedPersistenceIdentifier;
$referenceData['scope'] = 'referencesForNewFileExtension';
} else {
// There is no existing file for this reference
continue;
}
} else {
if (array_key_exists($possibleNewReferencedPersistenceIdentifier, $formDefinitionsInformation)) {
$targetPersistenceIdentifier = $possibleNewReferencedPersistenceIdentifier;
$referenceData['scope'] = 'referencesForOldFileExtension';
} else {
// There is no existing file for this reference
continue;
}
}
}
$referenceData['sheetIdentifiersWhichNeedsUpdate'] = $this->getSheetIdentifiersWhichNeedsUpdate(
$flexform,
$formDefinitionsInformation,
$targetPersistenceIdentifier,
$possibleOldReferencedPersistenceIdentifier,
$possibleNewReferencedPersistenceIdentifier
);
$scope = $referenceData['scope'];
$formDefinitionsInformation[$targetPersistenceIdentifier][$scope][] = $referenceData;
if ($scope === 'referencesForOldFileExtension') {
$formDefinitionsInformation[$targetPersistenceIdentifier]['hasReferencesForOldFileExtension'] = true;
$formDefinitionsInformation[$targetPersistenceIdentifier]['referencesForOldFileExtensionNeedsFlexformUpdates'] = !empty($referenceData['sheetIdentifiersWhichNeedsUpdate']);
} else {
$formDefinitionsInformation[$targetPersistenceIdentifier]['hasReferencesForNewFileExtension'] = true;
$formDefinitionsInformation[$targetPersistenceIdentifier]['referencesForNewFileExtensionNeedsFlexformUpdates'] = !empty($referenceData['sheetIdentifiersWhichNeedsUpdate']);
}
}
return $formDefinitionsInformation;
}
/**
* @param array $flexform
* @param array $formDefinitionsInformation
* @param string $targetPersistenceIdentifier
* @param string $possibleOldReferencedPersistenceIdentifier
* @param string $possibleNewReferencedPersistenceIdentifier
* @return array
*/
protected function getSheetIdentifiersWhichNeedsUpdate(
array $flexform,
array $formDefinitionsInformation,
string $targetPersistenceIdentifier,
string $possibleOldReferencedPersistenceIdentifier,
string $possibleNewReferencedPersistenceIdentifier
): array {
$sheetIdentifiersWhichNeedsUpdate = [];
$sheetIdentifiers = $this->getSheetIdentifiersForFinisherOverrides($flexform);
foreach ($sheetIdentifiers as $currentSheetIdentifier => $finisherIdentifier) {
$sheetIdentifierForOldPersistenceIdentifier = $this->buildExpectedSheetIdentifier(
$possibleOldReferencedPersistenceIdentifier,
$formDefinitionsInformation[$targetPersistenceIdentifier]['prototypeName'],
$formDefinitionsInformation[$targetPersistenceIdentifier]['formIdentifier'],
$finisherIdentifier
);
$sheetIdentifierForNewPersistenceIdentifier = $this->buildExpectedSheetIdentifier(
$possibleNewReferencedPersistenceIdentifier,
$formDefinitionsInformation[$targetPersistenceIdentifier]['prototypeName'],
$formDefinitionsInformation[$targetPersistenceIdentifier]['formIdentifier'],
$finisherIdentifier
);
if (
$currentSheetIdentifier === $sheetIdentifierForOldPersistenceIdentifier
&& !array_key_exists($sheetIdentifierForNewPersistenceIdentifier, $sheetIdentifiers)
) {
$sheetIdentifiersWhichNeedsUpdate[$currentSheetIdentifier] = $sheetIdentifierForNewPersistenceIdentifier;
}
}
return $sheetIdentifiersWhichNeedsUpdate;
}
/**
* @param array $flexform
* @return array
*/
protected function getSheetIdentifiersForFinisherOverrides(array $flexform): array
{
$sheetIdentifiers = [];
foreach ($this->getFinisherSheetsFromFlexform($flexform) as $sheetIdentifier => $sheetData) {
$itemOptionPath = array_keys($sheetData['lDEF']);
$firstSheetItemOptionPath = array_shift($itemOptionPath);
preg_match('#^settings\.finishers\.(.*)\..+$#', $firstSheetItemOptionPath, $matches);
if (!isset($matches[1])) {
continue;
}
$sheetIdentifiers[$sheetIdentifier] = $matches[1];
}
return $sheetIdentifiers;
}
/**
* @param array $flexform
* @return array
*/
protected function getFinisherSheetsFromFlexform(array $flexform): array
{
if (!isset($flexform['data'])) {
return [];
}
return array_filter(
$flexform['data'],
function ($key) {
return $key !== 'sDEF' && strlen($key) === 32;
},
ARRAY_FILTER_USE_KEY
);
}
/**
* @param array $flexform
* @return string
*/
protected function getPersistenceIdentifierFromFlexform(array $flexform): string
{
return $flexform['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'] ?? '';
}
/**
* @param array $referenceData
* @param array $sheetIdentifiersWhichNeedsUpdate
* @param string $newPersistenceIdentifier
* @return string
*/
protected function generateNewFlexformForReference(
array $referenceData,
array $sheetIdentifiersWhichNeedsUpdate,
string $newPersistenceIdentifier = ''
): string {
$flexform = $referenceData['flexform'];
if (!empty($newPersistenceIdentifier)) {
$flexform['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'] = $newPersistenceIdentifier;
}
foreach ($sheetIdentifiersWhichNeedsUpdate as $oldSheetIdentifier => $newSheetIdentifier) {
$flexform['data'][$newSheetIdentifier] = $flexform['data'][$oldSheetIdentifier];
unset($flexform['data'][$oldSheetIdentifier]);
}
return $this->flexFormTools->flexArray2Xml($flexform, true);
}
/**
* @param string $persistenceIdentifier
* @return bool
*/
protected function hasNewFileExtension(string $persistenceIdentifier): bool
{
return StringUtility::endsWith(
$persistenceIdentifier,
FormPersistenceManager::FORM_DEFINITION_FILE_EXTENSION
);
}
/**
* @param array $formDefinition
* @return bool
*/
protected function looksLikeAFormDefinition(array $formDefinition): bool
{
return isset($formDefinition['identifier'], $formDefinition['type']) && $formDefinition['type'] === 'Form';
}
/**
* @param string $persistenceIdentifier
* @return string
*/
protected function getOldPersistenceIdentifier(string $persistenceIdentifier): string
{
return preg_replace(
'
#^(.*)(\.form\.yaml)$#',
'${1}.yaml',
$persistenceIdentifier
);
}
/**
* @param string $persistenceIdentifier
* @return string
*/
protected function getNewPersistenceIdentifier(string $persistenceIdentifier): string
{
return preg_replace(
'#(?<!\.form).yaml$#',
'.form.yaml',
$persistenceIdentifier
);
}
/**
* @param string $persistenceIdentifier
* @param string $prototypeName
* @param string $formIdentifier
* @param string $finisherIdentifier
* @return string
*/
protected function buildExpectedSheetIdentifier(
string $persistenceIdentifier,
string $prototypeName,
string $formIdentifier,
string $finisherIdentifier
): string {
return md5(
implode('', [
$persistenceIdentifier,
$prototypeName,
$formIdentifier,
$finisherIdentifier
])
);
}
/**
* @param File $file
* @return array
*/
protected function getFormDefinition(File $file): array
{
try {
$rawYamlContent = $file->getContents();
$formDefinition = $this->extractMetaDataFromCouldBeFormDefinition($rawYamlContent);
if (!$this->looksLikeAFormDefinition($formDefinition)) {
$formDefinition = [];
}
} catch (\Exception $exception) {
$formDefinition = [];
}
return $formDefinition;
}
/**
* @param string $maybeRawFormDefinition
* @return array
*/
protected function extractMetaDataFromCouldBeFormDefinition(string $maybeRawFormDefinition): array
{
$metaDataProperties = ['identifier', 'type', 'label', 'prototypeName'];
$metaData = [];
foreach (explode("\n", $maybeRawFormDefinition) as $line) {
if (empty($line) || $line[0] === ' ') {
continue;
}
[$key, $value] = explode(':', $line);
if (
empty($key)
|| empty($value)
|| !in_array($key, $metaDataProperties)
) {
continue;
}
$value = trim($value, ' \'"');
$metaData[$key] = $value;
}
return $metaData;
}
/**
* @return array
*/
protected function getAllFlexformFieldsFromFormPlugins(): array
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tt_content');
$queryBuilder->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$records = $queryBuilder
->select('uid', 'pi_flexform')
->from('tt_content')
->where(
$queryBuilder->expr()->eq(
'CType',
$queryBuilder->createNamedParameter('form_formframework', \PDO::PARAM_STR)
)
)
->execute()
->fetchAll();
return $records;
}
/**
* @param int $uid
* @param string $flexform
* @param bool $updateRefindex
*/
protected function updateContentReference(
int $uid,
string $flexform,
bool $updateRefindex = false
): void {
$this->connection->update(
'tt_content',
['pi_flexform' => $flexform],
['uid' => $uid]
);
if (!$updateRefindex) {
return;
}
$this->referenceIndex->updateRefIndexTable(
'tt_content',
$uid
);
}
/**
* @return ObjectManager
*/
protected function getObjectManager(): ObjectManager
{
return GeneralUtility::makeInstance(ObjectManager::class);
}
}
@@ -0,0 +1,104 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Filelist\ContextMenu\ItemProviders\FileProvider;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager;
/**
* Purges previously added form files from items for context menus.
* @internal
*/
class FormFileProvider extends FileProvider
{
/**
* @var array
*/
protected $itemsConfiguration = [];
/**
* Lowest priority, thus gets executed last.
*
* @return int
*/
public function getPriority(): int
{
return 0;
}
/**
* @return bool
*/
public function canHandle(): bool
{
return parent::canHandle()
&& StringUtility::endsWith($this->identifier, FormPersistenceManager::FORM_DEFINITION_FILE_EXTENSION);
}
/**
* @param array $items
* @return array
*/
public function addItems(array $items): array
{
parent::initialize();
return $this->purgeItems($items);
}
/**
* Purges items that are not allowed for according command.
* According canBeEdited, canBeRenamed, ... commands will always return
* false in order to remove those form file items.
*
* Using the canRender() approach avoid adding hardcoded index name
* lookup. Thus, it's streamlined with the rest of the provides, but
* actually purges items instead of adding them.
*
* @param array $items
* @return array
*/
protected function purgeItems(array $items): array
{
foreach ($items as $name => $item) {
$type = $item['type'];
if ($type === 'submenu' && !empty($item['childItems'])) {
$item['childItems'] = $this->purgeItems($item['childItems']);
} elseif (!parent::canRender($name, $type)) {
unset($items[$name]);
}
}
return $items;
}
/**
* @return bool
*/
protected function canBeEdited(): bool
{
return false;
}
/**
* @return bool
*/
protected function canBeRenamed(): bool
{
return false;
}
}
@@ -0,0 +1,163 @@
<?php
declare(strict_types = 1);
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface;
use TYPO3\CMS\Core\Error\Exception;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\AbstractMessage;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Service\FlexFormService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException;
use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException;
use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager;
use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface;
/**
* Contains a preview rendering for the page module of CType="form_formframework"
* @internal
*/
class FormPagePreviewRenderer implements PageLayoutViewDrawItemHookInterface
{
/**
* Localisation prefix
*/
const L10N_PREFIX = 'LLL:EXT:form/Resources/Private/Language/Database.xlf:';
/**
* Preprocesses the preview rendering of the content element "form_formframework".
*
* @param \TYPO3\CMS\Backend\View\PageLayoutView $parentObject Calling parent object
* @param bool $drawItem Whether to draw the item using the default functionalities
* @param string $headerContent Header content
* @param string $itemContent Item content
* @param array $row Record row of tt_content
*/
public function preProcess(
\TYPO3\CMS\Backend\View\PageLayoutView &$parentObject,
&$drawItem,
&$headerContent,
&$itemContent,
array &$row
) {
if ($row['CType'] === 'form_formframework') {
$contentType = $parentObject->CType_labels[$row['CType']];
$itemContent .= $parentObject->linkEditContent('<strong>' . htmlspecialchars($contentType) . '</strong>', $row) . '<br />';
$flexFormData = GeneralUtility::makeInstance(FlexFormService::class)
->convertFlexFormContentToArray($row['pi_flexform']);
$persistenceIdentifier = $flexFormData['settings']['persistenceIdentifier'];
if (!empty($persistenceIdentifier)) {
try {
$formPersistenceManager = GeneralUtility::makeInstance(ObjectManager::class)->get(FormPersistenceManagerInterface::class);
try {
if (
StringUtility::endsWith($persistenceIdentifier, FormPersistenceManager::FORM_DEFINITION_FILE_EXTENSION)
|| strpos($persistenceIdentifier, 'EXT:') === 0
) {
$formDefinition = $formPersistenceManager->load($persistenceIdentifier);
$formLabel = $formDefinition['label'];
} else {
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.inaccessiblePersistenceIdentifier'),
$persistenceIdentifier
);
}
} catch (ParseErrorException $e) {
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidPersistenceIdentifier'),
$persistenceIdentifier
);
} catch (PersistenceManagerException $e) {
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.inaccessiblePersistenceIdentifier'),
$persistenceIdentifier
);
} catch (Exception $e) {
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.notExistingdPersistenceIdentifier'),
$persistenceIdentifier
);
}
} catch (NoSuchFileException $e) {
$this->addInvalidFrameworkConfigurationFlashMessage($e);
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.notExistingdPersistenceIdentifier'),
$persistenceIdentifier
);
} catch (ParseErrorException $e) {
$this->addInvalidFrameworkConfigurationFlashMessage($e);
$formLabel = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidFrameworkConfiguration'),
$persistenceIdentifier
);
} catch (\Exception $e) {
// Top level catch - FAL throws top level exceptions on missing files, eg. in getFileInfoByIdentifier() of LocalDriver
$this->addInvalidFrameworkConfigurationFlashMessage($e);
$formLabel = $e->getMessage();
}
} else {
$formLabel = $this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.noPersistenceIdentifier');
}
$itemContent .= $parentObject->linkEditContent(
$parentObject->renderText($formLabel),
$row
) . '<br />';
$drawItem = false;
}
}
/**
* @param \Exception $e
*/
protected function addInvalidFrameworkConfigurationFlashMessage(\Exception $e)
{
$messageText = sprintf(
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidFrameworkConfiguration.text'),
$e->getMessage()
);
GeneralUtility::makeInstance(ObjectManager::class)
->get(FlashMessageService::class)
->getMessageQueueByIdentifier('core.template.flashMessages')
->enqueue(
GeneralUtility::makeInstance(
FlashMessage::class,
$messageText,
$this->getLanguageService()->sL(self::L10N_PREFIX . 'tt_content.preview.invalidFrameworkConfiguration.title'),
AbstractMessage::ERROR,
true
)
);
}
/**
* @return LanguageService
*/
protected function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
}
@@ -0,0 +1,41 @@
<?php
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Form\Slot\FilePersistenceSlot;
/**
* @internal
*/
class ImportExportHook
{
/**
* @param array $params
*/
public function beforeAddSysFileRecordOnImport(array $params): void
{
$fileRecord = $params['fileRecord'];
$temporaryFile = $params['temporaryFile'];
$formPersistenceSlot = GeneralUtility::makeInstance(FilePersistenceSlot::class);
$formPersistenceSlot->allowInvocation(
FilePersistenceSlot::COMMAND_FILE_ADD,
implode(':', [$fileRecord['storage'], $fileRecord['identifier']]),
$formPersistenceSlot->getContentSignature(file_get_contents($temporaryFile))
);
}
}
@@ -0,0 +1,79 @@
<?php
namespace TYPO3\CMS\Form\Hooks;
/*
* 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!
*/
use TYPO3\CMS\Core\Database\SoftReferenceIndex;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Register new referenced formDefinitions within a plugin as a soft reference.
*
* This is used in BE to track how often a specific form is used in a content
* element. The number is shown in the form module "Manage forms".
*
* Scope: backend
* @internal
*/
class SoftReferenceParserHook extends SoftReferenceIndex
{
/**
* Main function through which all processing happens
*
* @param string $table Database table name
* @param string $field Field name for which processing occurs
* @param int $uid UID of the record
* @param string $content The content/value of the field
* @param string $spKey The softlink parser key. This is only interesting if more than one parser is grouped in the same class. That is the case with this parser.
* @param array $spParams Parameters of the softlink parser. Basically this is the content inside optional []-brackets after the softref keys. Parameters are exploded by ";
* @param string $structurePath If running from inside a FlexForm structure, this is the path of the tag.
* @return array|bool Result array on positive matches, see description above. Otherwise FALSE
*/
public function findRef($table, $field, $uid, $content, $spKey, $spParams, $structurePath = '')
{
$this->tokenID_basePrefix = $table . ':' . $uid . ':' . $field . ':' . $structurePath . ':' . $spKey;
$tokenId = $this->makeTokenID($content);
$resourceFactory = GeneralUtility::makeInstance(ResourceFactory::class);
try {
$file = $resourceFactory->retrieveFileOrFolderObject($content);
} catch (\Exception $e) {
// Top level catch to ensure useful following exception handling, because FAL throws top level exceptions.
// TYPO3\CMS\Core\Database\ReferenceIndex::getRelations() will check the return value of this hook with is_array()
// so we return false to tell getRelations() to do nothing.
return false;
}
if ($file === null) {
return false;
}
return [
'content' => '{softref:' . $tokenId . '}',
'elements' => [
$tokenId => [
'matchString' => $content,
'subst' => [
'type' => 'db',
'recordRef' => 'sys_file:' . $file->getUid(),
'tokenID' => $tokenId,
'tokenValue' => $content
],
]
]
];
}
}