‪TYPO3CMS  ‪main
ImageManipulationElement.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Psr\EventDispatcher\EventDispatcherInterface;
34 
40 {
41  private string ‪$wizardRouteName = 'ajax_wizard_image_manipulation';
42 
48  protected static ‪$defaultConfig = [
49  'file_field' => 'uid_local',
50  'allowedExtensions' => null, // default: $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
51  'cropVariants' => [
52  'default' => [
53  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.default',
54  'allowedAspectRatios' => [
55  '16:9' => [
56  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9',
57  'value' => 16 / 9,
58  ],
59  '3:2' => [
60  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.3_2',
61  'value' => 3 / 2,
62  ],
63  '4:3' => [
64  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3',
65  'value' => 4 / 3,
66  ],
67  '1:1' => [
68  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1',
69  'value' => 1.0,
70  ],
71  'NaN' => [
72  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free',
73  'value' => 0.0,
74  ],
75  ],
76  'selectedRatio' => 'NaN',
77  'cropArea' => [
78  'x' => 0.0,
79  'y' => 0.0,
80  'width' => 1.0,
81  'height' => 1.0,
82  ],
83  ],
84  ],
85  ];
86 
92  protected ‪$defaultFieldInformation = [
93  'tcaDescription' => [
94  'renderType' => 'tcaDescription',
95  ],
96  ];
97 
103  protected ‪$defaultFieldWizard = [
104  'localizationStateSelector' => [
105  'renderType' => 'localizationStateSelector',
106  ],
107  'otherLanguageThumbnails' => [
108  'renderType' => 'otherLanguageThumbnails',
109  'after' => [
110  'localizationStateSelector',
111  ],
112  ],
113  'defaultLanguageDifferences' => [
114  'renderType' => 'defaultLanguageDifferences',
115  'after' => [
116  'otherLanguageThumbnails',
117  ],
118  ],
119  ];
120 
121  public function ‪__construct(
122  private readonly ‪BackendViewFactory $backendViewFactory,
123  private readonly ‪UriBuilder $uriBuilder,
124  private readonly EventDispatcherInterface $eventDispatcher,
125  private readonly ‪ResourceFactory $resourceFactory,
126  ) {}
127 
134  public function ‪render(): array
135  {
136  $resultArray = $this->‪initializeResultArray();
137  $parameterArray = $this->data['parameterArray'];
138  $config = $this->‪populateConfiguration($parameterArray['fieldConf']['config']);
139 
140  $file = $this->‪getFile($this->data['databaseRow'], $config['file_field']);
141  if (!$file) {
142  // Early return in case we do not find a file
143  return $resultArray;
144  }
145 
146  $config = $this->‪processConfiguration($config, $parameterArray['itemFormElValue'], $file);
147 
148  $fieldInformationResult = $this->‪renderFieldInformation();
149  $fieldInformationHtml = $fieldInformationResult['html'];
150  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
151 
152  $fieldControlResult = $this->‪renderFieldControl();
153  $fieldControlHtml = $fieldControlResult['html'];
154  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
155 
156  $fieldWizardResult = $this->‪renderFieldWizard();
157  $fieldWizardHtml = $fieldWizardResult['html'];
158  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
159 
160  $arguments = [
161  'fieldInformation' => $fieldInformationHtml,
162  'fieldControl' => $fieldControlHtml,
163  'fieldWizard' => $fieldWizardHtml,
164  'isAllowedFileExtension' => in_array(strtolower($file->getExtension()), ‪GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), true),
165  'image' => $file,
166  'formEngine' => [
167  'field' => [
168  'value' => $parameterArray['itemFormElValue'],
169  'name' => $parameterArray['itemFormElName'],
170  ],
171  'validation' => '[]',
172  ],
173  'config' => $config,
174  'wizardUri' => $this->‪getWizardUri(),
175  'wizardPayload' => json_encode($this->‪getWizardPayload($config['cropVariants'], $file)),
176  'previewUrl' => $this->eventDispatcher->dispatch(
177  new ModifyImageManipulationPreviewUrlEvent($this->data['databaseRow'], $config, $file)
178  )->getPreviewUrl(),
179  ];
180 
181  if ($arguments['isAllowedFileExtension']) {
182  $resultArray['javaScriptModules'][] = ‪JavaScriptModuleInstruction::create(
183  '@typo3/backend/image-manipulation.js'
184  )->invoke('initializeTrigger');
185  $arguments['formEngine']['field']['id'] = ‪StringUtility::getUniqueId('formengine-image-manipulation-');
186  if ($config['required'] ?? false) {
187  $arguments['formEngine']['validation'] = $this->‪getValidationDataAsJsonString(['required' => true]);
188  }
189  }
190  $view = $this->backendViewFactory->create($this->data['request']);
191  $view->assignMultiple($arguments);
192  $resultArray['html'] = $this->‪wrapWithFieldsetAndLegend($view->render('Form/ImageManipulationElement'));
193 
194  return $resultArray;
195  }
196 
203  protected function ‪getFile(array $row, $fieldName)
204  {
205  $file = null;
206  $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null;
207  if (is_array($fileUid) && isset($fileUid[0]['uid'])) {
208  $fileUid = $fileUid[0]['uid'];
209  }
211  try {
212  $file = $this->resourceFactory->getFileObject($fileUid);
213  } catch (FileDoesNotExistException|\InvalidArgumentException) {
214  }
215  }
216  return $file;
217  }
218 
223  protected function ‪populateConfiguration(array $baseConfiguration)
224  {
226 
227  // If ratios are set do not add default options
228  if (isset($baseConfiguration['cropVariants'])) {
229  unset(‪$defaultConfig['cropVariants']);
230  }
231 
232  $config = array_replace_recursive(‪$defaultConfig, $baseConfiguration);
233 
234  if (!is_array($config['cropVariants'])) {
235  throw new InvalidConfigurationException('Crop variants configuration must be an array', 1485377267);
236  }
237 
238  $cropVariants = [];
239  foreach ($config['cropVariants'] as $id => $cropVariant) {
240  // Filter allowed aspect ratios
241  $cropVariant['allowedAspectRatios'] = array_filter($cropVariant['allowedAspectRatios'] ?? [], static function (array $aspectRatio): bool {
242  return !(bool)($aspectRatio['disabled'] ?? false);
243  });
244 
245  // Ignore disabled crop variants
246  if (!empty($cropVariant['disabled'])) {
247  continue;
248  }
249 
250  if (empty($cropVariant['allowedAspectRatios'])) {
251  throw new InvalidConfigurationException('Crop variants configuration ' . $id . ' contains no allowed aspect ratios', 1620147893);
252  }
253 
254  // Enforce a crop area (default is full image)
255  if (empty($cropVariant['cropArea'])) {
256  $cropVariant['cropArea'] = ‪Area::createEmpty()->‪asArray();
257  }
258 
259  $cropVariants[$id] = $cropVariant;
260  }
261 
262  $config['cropVariants'] = $cropVariants;
263 
264  // By default we allow all image extensions that can be handled by the GFX functionality
265  $config['allowedExtensions'] ??= ‪$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
266  return $config;
267  }
268 
273  protected function ‪processConfiguration(array $config, string &$elementValue, File $file)
274  {
275  $cropVariantCollection = ‪CropVariantCollection::create($elementValue, $config['cropVariants']);
276  if (empty($config['readOnly']) && !empty($file->getProperty('width'))) {
277  $cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file);
278  $elementValue = (string)$cropVariantCollection;
279  }
280  $config['cropVariants'] = $cropVariantCollection->asArray();
281  $config['allowedExtensions'] = implode(', ', ‪GeneralUtility::trimExplode(',', $config['allowedExtensions'], true));
282  return $config;
283  }
284 
285  protected function ‪getWizardUri(): string
286  {
287  return (string)$this->uriBuilder->buildUriFromRoute($this->wizardRouteName);
288  }
289 
290  protected function ‪getWizardPayload(array $cropVariants, ‪File $image): array
291  {
292  $uriArguments = [];
293  $arguments = [
294  'cropVariants' => $cropVariants,
295  'image' => $image->‪getUid(),
296  ];
297  $uriArguments['arguments'] = json_encode($arguments);
298  $uriArguments['signature'] = ‪GeneralUtility::hmac((string)($uriArguments['arguments'] ?? ''), $this->wizardRouteName);
299 
300  return $uriArguments;
301  }
302 }
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getWizardPayload
‪getWizardPayload(array $cropVariants, File $image)
Definition: ImageManipulationElement.php:287
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$wizardRouteName
‪string $wizardRouteName
Definition: ImageManipulationElement.php:41
‪TYPO3\CMS\Backend\Form\Event\ModifyImageManipulationPreviewUrlEvent
Definition: ModifyImageManipulationPreviewUrlEvent.php:26
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:73
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: ImageManipulationElement.php:90
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\render
‪array render()
Definition: ImageManipulationElement.php:131
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:104
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:37
‪TYPO3\CMS\Backend\View\BackendViewFactory
Definition: BackendViewFactory.php:35
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area
Definition: Area.php:23
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getFile
‪File null getFile(array $row, $fieldName)
Definition: ImageManipulationElement.php:200
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\create
‪static create(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:47
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:37
‪TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
Definition: FileDoesNotExistException.php:21
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getWizardUri
‪getWizardUri()
Definition: ImageManipulationElement.php:282
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area\asArray
‪asArray()
Definition: Area.php:109
‪TYPO3\CMS\Core\Resource\File\getProperty
‪getProperty(string $key)
Definition: File.php:62
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger(mixed $var)
Definition: MathUtility.php:69
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\populateConfiguration
‪array populateConfiguration(array $baseConfiguration)
Definition: ImageManipulationElement.php:220
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\wrapWithFieldsetAndLegend
‪wrapWithFieldsetAndLegend(string $innerHTML)
Definition: AbstractFormElement.php:133
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:89
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:44
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:42
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Core\Utility\GeneralUtility\hmac
‪static string hmac($input, $additionalSecret='')
Definition: GeneralUtility.php:474
‪TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException
Definition: InvalidConfigurationException.php:23
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: ImageManipulationElement.php:100
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\processConfiguration
‪array processConfiguration(array $config, string &$elementValue, File $file)
Definition: ImageManipulationElement.php:270
‪TYPO3\CMS\Core\Resource\AbstractFile\getUid
‪return MathUtility::canBeInterpretedAsInteger($size) ?(int) $size int getUid()
Definition: AbstractFile.php:195
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:133
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\__construct
‪__construct(private readonly BackendViewFactory $backendViewFactory, private readonly UriBuilder $uriBuilder, private readonly EventDispatcherInterface $eventDispatcher, private readonly ResourceFactory $resourceFactory,)
Definition: ImageManipulationElement.php:118
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:24
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:24
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:105
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement
Definition: ImageManipulationElement.php:40
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode(string $delim, string $string, bool $removeEmptyValues=false, int $limit=0)
Definition: GeneralUtility.php:817
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static getUniqueId(string $prefix='')
Definition: StringUtility.php:57
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultConfig
‪static array $defaultConfig
Definition: ImageManipulationElement.php:47
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪initializeResultArray()
Definition: AbstractNode.php:77
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area\createEmpty
‪static Area createEmpty()
Definition: Area.php:81