first commit
This commit is contained in:
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
|
||||
],
|
||||
]
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user