‪TYPO3CMS  11.5
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 
33 
39 {
43  private ‪$wizardRouteName = 'ajax_wizard_image_manipulation';
44 
50  protected static ‪$defaultConfig = [
51  'file_field' => 'uid_local',
52  'allowedExtensions' => null, // default: $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
53  'cropVariants' => [
54  'default' => [
55  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop_variant.default',
56  'allowedAspectRatios' => [
57  '16:9' => [
58  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9',
59  'value' => 16 / 9,
60  ],
61  '3:2' => [
62  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.3_2',
63  'value' => 3 / 2,
64  ],
65  '4:3' => [
66  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3',
67  'value' => 4 / 3,
68  ],
69  '1:1' => [
70  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1',
71  'value' => 1.0,
72  ],
73  'NaN' => [
74  'title' => 'LLL:EXT:core/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free',
75  'value' => 0.0,
76  ],
77  ],
78  'selectedRatio' => 'NaN',
79  'cropArea' => [
80  'x' => 0.0,
81  'y' => 0.0,
82  'width' => 1.0,
83  'height' => 1.0,
84  ],
85  ],
86  ],
87  ];
88 
94  protected ‪$defaultFieldInformation = [
95  'tcaDescription' => [
96  'renderType' => 'tcaDescription',
97  ],
98  ];
99 
105  protected ‪$defaultFieldWizard = [
106  'localizationStateSelector' => [
107  'renderType' => 'localizationStateSelector',
108  ],
109  'otherLanguageThumbnails' => [
110  'renderType' => 'otherLanguageThumbnails',
111  'after' => [
112  'localizationStateSelector',
113  ],
114  ],
115  'defaultLanguageDifferences' => [
116  'renderType' => 'defaultLanguageDifferences',
117  'after' => [
118  'otherLanguageThumbnails',
119  ],
120  ],
121  ];
122 
126  protected ‪$templateView;
127 
131  protected ‪$uriBuilder;
132 
137  public function ‪__construct(‪NodeFactory ‪$nodeFactory, array ‪$data)
138  {
139  parent::__construct(‪$nodeFactory, ‪$data);
140  // Would be great, if we could inject the view here, but since the constructor is in the interface, we can't
141  $this->templateView = GeneralUtility::makeInstance(StandaloneView::class);
142  $this->templateView->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts/')]);
143  $this->templateView->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials/ImageManipulation/')]);
144  $this->templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html'));
145  $this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
146  }
147 
154  public function ‪render()
155  {
156  $resultArray = $this->‪initializeResultArray();
157  $parameterArray = $this->data['parameterArray'];
158  $config = $this->‪populateConfiguration($parameterArray['fieldConf']['config']);
159 
160  $file = $this->‪getFile($this->data['databaseRow'], $config['file_field']);
161  if (!$file) {
162  // Early return in case we do not find a file
163  return $resultArray;
164  }
165 
166  $config = $this->‪processConfiguration($config, $parameterArray['itemFormElValue'], $file);
167 
168  $fieldInformationResult = $this->‪renderFieldInformation();
169  $fieldInformationHtml = $fieldInformationResult['html'];
170  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false);
171 
172  $fieldControlResult = $this->‪renderFieldControl();
173  $fieldControlHtml = $fieldControlResult['html'];
174  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false);
175 
176  $fieldWizardResult = $this->‪renderFieldWizard();
177  $fieldWizardHtml = $fieldWizardResult['html'];
178  $resultArray = $this->‪mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false);
179 
180  $arguments = [
181  'fieldInformation' => $fieldInformationHtml,
182  'fieldControl' => $fieldControlHtml,
183  'fieldWizard' => $fieldWizardHtml,
184  'isAllowedFileExtension' => in_array(strtolower($file->getExtension()), ‪GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), true),
185  'image' => $file,
186  'formEngine' => [
187  'field' => [
188  'value' => $parameterArray['itemFormElValue'],
189  'name' => $parameterArray['itemFormElName'],
190  ],
191  'validation' => '[]',
192  ],
193  'config' => $config,
194  'wizardUri' => $this->‪getWizardUri(),
195  'wizardPayload' => json_encode($this->‪getWizardPayload($config['cropVariants'], $file)),
196  'previewUrl' => $this->‪getPreviewUrl($this->data['databaseRow'], $file),
197  ];
198 
199  if ($arguments['isAllowedFileExtension']) {
200  $resultArray['requireJsModules'][] = ‪JavaScriptModuleInstruction::forRequireJS(
201  'TYPO3/CMS/Backend/ImageManipulation'
202  )->invoke('initializeTrigger');
203  $arguments['formEngine']['field']['id'] = ‪StringUtility::getUniqueId('formengine-image-manipulation-');
204  if (GeneralUtility::inList($config['eval'] ?? '', 'required')) {
205  $arguments['formEngine']['validation'] = $this->‪getValidationDataAsJsonString(['required' => true]);
206  }
207  }
208  $this->templateView->assignMultiple($arguments);
209  $resultArray['html'] = $this->templateView->render();
210 
211  return $resultArray;
212  }
213 
221  protected function ‪getFile(array $row, $fieldName)
222  {
223  $file = null;
224  $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null;
225  if (is_array($fileUid) && isset($fileUid[0]['uid'])) {
226  $fileUid = $fileUid[0]['uid'];
227  }
229  try {
230  $file = GeneralUtility::makeInstance(ResourceFactory::class)->getFileObject($fileUid);
231  } catch (FileDoesNotExistException|\InvalidArgumentException $e) {
232  }
233  }
234  return $file;
235  }
236 
242  protected function ‪getPreviewUrl(array $databaseRow, ‪File $file): string
243  {
244  $previewUrl = '';
245  // Hook to generate a preview URL
246  $hookParameters = [
247  'databaseRow' => $databaseRow,
248  'file' => $file,
249  'previewUrl' => $previewUrl,
250  ];
251  foreach (‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['Backend/Form/Element/ImageManipulationElement']['previewUrl'] ?? [] as $listener) {
252  $previewUrl = GeneralUtility::callUserFunction($listener, $hookParameters, $this);
253  }
254  return $previewUrl;
255  }
256 
262  protected function ‪populateConfiguration(array $baseConfiguration)
263  {
265 
266  // If ratios are set do not add default options
267  if (isset($baseConfiguration['cropVariants'])) {
268  unset(‪$defaultConfig['cropVariants']);
269  }
270 
271  $config = array_replace_recursive(‪$defaultConfig, $baseConfiguration);
272 
273  if (!is_array($config['cropVariants'])) {
274  throw new ‪InvalidConfigurationException('Crop variants configuration must be an array', 1485377267);
275  }
276 
277  $cropVariants = [];
278  foreach ($config['cropVariants'] as $id => $cropVariant) {
279  // Filter allowed aspect ratios
280  $cropVariant['allowedAspectRatios'] = array_filter($cropVariant['allowedAspectRatios'] ?? [], static function ($aspectRatio) {
281  return !(bool)($aspectRatio['disabled'] ?? false);
282  });
283 
284  // Ignore disabled crop variants
285  if (!empty($cropVariant['disabled'])) {
286  continue;
287  }
288 
289  if (empty($cropVariant['allowedAspectRatios'])) {
290  throw new ‪InvalidConfigurationException('Crop variants configuration ' . $id . ' contains no allowed aspect ratios', 1620147893);
291  }
292 
293  // Enforce a crop area (default is full image)
294  if (empty($cropVariant['cropArea'])) {
295  $cropVariant['cropArea'] = ‪Area::createEmpty()->‪asArray();
296  }
297 
298  $cropVariants[$id] = $cropVariant;
299  }
300 
301  $config['cropVariants'] = $cropVariants;
302 
303  // By default we allow all image extensions that can be handled by the GFX functionality
304  $config['allowedExtensions'] ??= ‪$GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
305  return $config;
306  }
307 
315  protected function ‪processConfiguration(array $config, string &$elementValue, ‪File $file)
316  {
317  $cropVariantCollection = ‪CropVariantCollection::create($elementValue, $config['cropVariants']);
318  if (empty($config['readOnly']) && !empty($file->‪getProperty('width'))) {
319  $cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file);
320  $elementValue = (string)$cropVariantCollection;
321  }
322  $config['cropVariants'] = $cropVariantCollection->asArray();
323  $config['allowedExtensions'] = implode(', ', ‪GeneralUtility::trimExplode(',', $config['allowedExtensions'], true));
324  return $config;
325  }
326 
330  protected function ‪getWizardUri(): string
331  {
332  return (string)$this->uriBuilder->buildUriFromRoute($this->wizardRouteName);
333  }
334 
340  protected function ‪getWizardPayload(array $cropVariants, ‪File $image): array
341  {
342  $uriArguments = [];
343  $arguments = [
344  'cropVariants' => $cropVariants,
345  'image' => $image->‪getUid(),
346  ];
347  $uriArguments['arguments'] = json_encode($arguments);
348  $uriArguments['signature'] = GeneralUtility::hmac((string)($uriArguments['arguments'] ?? ''), $this->wizardRouteName);
349 
350  return $uriArguments;
351  }
352 }
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$wizardRouteName
‪string $wizardRouteName
Definition: ImageManipulationElement.php:42
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$templateView
‪StandaloneView $templateView
Definition: ImageManipulationElement.php:121
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldInformation
‪array renderFieldInformation()
Definition: AbstractFormElement.php:76
‪TYPO3\CMS\Core\Utility\MathUtility\canBeInterpretedAsInteger
‪static bool canBeInterpretedAsInteger($var)
Definition: MathUtility.php:74
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultFieldInformation
‪array $defaultFieldInformation
Definition: ImageManipulationElement.php:91
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\render
‪array render()
Definition: ImageManipulationElement.php:148
‪TYPO3\CMS\Backend\Form\AbstractNode\mergeChildReturnIntoExistingResult
‪array mergeChildReturnIntoExistingResult(array $existing, array $childReturn, bool $mergeHtml=true)
Definition: AbstractNode.php:120
‪TYPO3\CMS\Backend\Form\AbstractNode\initializeResultArray
‪array initializeResultArray()
Definition: AbstractNode.php:91
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getWizardPayload
‪array getWizardPayload(array $cropVariants, File $image)
Definition: ImageManipulationElement.php:334
‪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:215
‪TYPO3\CMS\Backend\Form\AbstractNode\$nodeFactory
‪NodeFactory $nodeFactory
Definition: AbstractNode.php:36
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement
Definition: AbstractFormElement.php:35
‪TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException
Definition: FileDoesNotExistException.php:21
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Backend\Form\Element
Definition: AbstractFormElement.php:16
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\forRequireJS
‪static self forRequireJS(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:49
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\populateConfiguration
‪array populateConfiguration(array $baseConfiguration)
Definition: ImageManipulationElement.php:256
‪TYPO3\CMS\Backend\Form\AbstractNode\getValidationDataAsJsonString
‪string getValidationDataAsJsonString(array $config)
Definition: AbstractNode.php:156
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldControl
‪array renderFieldControl()
Definition: AbstractFormElement.php:92
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Backend\Form\AbstractNode\$data
‪array $data
Definition: AbstractNode.php:43
‪TYPO3\CMS\Core\Resource\ResourceFactory
Definition: ResourceFactory.php:41
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:24
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getPreviewUrl
‪string getPreviewUrl(array $databaseRow, File $file)
Definition: ImageManipulationElement.php:236
‪TYPO3\CMS\Core\Resource\AbstractFile\getUid
‪int getUid()
Definition: AbstractFile.php:203
‪TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException
Definition: InvalidConfigurationException.php:23
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultFieldWizard
‪array $defaultFieldWizard
Definition: ImageManipulationElement.php:101
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\__construct
‪__construct(NodeFactory $nodeFactory, array $data)
Definition: ImageManipulationElement.php:131
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\processConfiguration
‪array processConfiguration(array $config, string &$elementValue, File $file)
Definition: ImageManipulationElement.php:309
‪TYPO3\CMS\Backend\Form\NodeFactory
Definition: NodeFactory.php:37
‪TYPO3\CMS\Core\Utility\StringUtility\getUniqueId
‪static string getUniqueId($prefix='')
Definition: StringUtility.php:128
‪TYPO3\CMS\Fluid\View\StandaloneView
Definition: StandaloneView.php:31
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$uriBuilder
‪UriBuilder $uriBuilder
Definition: ImageManipulationElement.php:125
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Resource\File\getProperty
‪mixed getProperty($key)
Definition: File.php:65
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection
Definition: CropVariantCollection.php:23
‪TYPO3\CMS\Core\Utility\MathUtility
Definition: MathUtility.php:22
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area\asArray
‪array asArray()
Definition: Area.php:119
‪TYPO3\CMS\Core\Utility\StringUtility
Definition: StringUtility.php:22
‪TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection\create
‪static CropVariantCollection create(string $jsonString, array $tcaConfig=[])
Definition: CropVariantCollection.php:42
‪TYPO3\CMS\Backend\Form\Element\AbstractFormElement\renderFieldWizard
‪array renderFieldWizard()
Definition: AbstractFormElement.php:108
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement
Definition: ImageManipulationElement.php:39
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\$defaultConfig
‪static array $defaultConfig
Definition: ImageManipulationElement.php:48
‪TYPO3\CMS\Backend\Form\Element\ImageManipulationElement\getWizardUri
‪string getWizardUri()
Definition: ImageManipulationElement.php:324
‪TYPO3\CMS\Core\Imaging\ImageManipulation\Area\createEmpty
‪static Area createEmpty()
Definition: Area.php:90