From f4a73f2f10263b1a23d7b7a693bbf3adb6351fbc Mon Sep 17 00:00:00 2001 From: Helmut Hummel Date: Tue, 7 Feb 2017 21:12:16 +0100 Subject: [PATCH] [BUGFIX] Eliminate gremlins in image cropper Refactor rendering to use two templates, a layout and a partial to be more flexible. Now also force the crop to be applied, even when the editor does not open the image manipulation. This is important when only one aspect ratio is allowed to not allow the editor to save an invalid state. Eliminate several quirks in the TypeScript code, among them: * reset state when closing the cropper * reset state when modal is dismissed * correctly initialize all previews * show correct size of crop area for big images * avoid accidental dismiss of the modal Last but not least add the accidentally removed form engine wizard html fields again. Resolves: #79764 Resolves: #79731 Resolves: #79753 Resolves: #79674 Releases: master Change-Id: I0a24d6418d6263b00c3fbf31901fd7c67e9fc97e Reviewed-on: https://review.typo3.org/51642 Tested-by: TYPO3com Reviewed-by: Frans Saris Tested-by: Frans Saris Reviewed-by: Georg Ringer Tested-by: Georg Ringer --- .../Public/Less/TYPO3/_element_cropper.less | 2 +- .../Form/Element/ImageManipulationElement.php | 45 +++-- .../Form/Wizard/ImageManipulationWizard.php | 6 +- .../Private/Layouts/ImageManipulation.html | 16 ++ .../ImageManipulation/ModalTitle.html | 9 + .../ImageManipulationElement.html | 61 ++++++ ...ping.html => ImageManipulationWizard.html} | 67 +------ .../Private/TypeScript/ImageManipulation.ts | 186 +++++++++++++----- .../Public/JavaScript/ImageManipulation.js | 182 ++++++++++++----- .../Imaging/ImageManipulation/Area.php | 21 ++ .../Imaging/ImageManipulation/CropVariant.php | 24 ++- .../CropVariantCollection.php | 42 +++- .../Imaging/ImageManipulation/Ratio.php | 16 ++ .../Imaging/ImageManipulation/AreaTest.php | 93 +++++++++ 14 files changed, 577 insertions(+), 193 deletions(-) create mode 100644 typo3/sysext/backend/Resources/Private/Layouts/ImageManipulation.html create mode 100644 typo3/sysext/backend/Resources/Private/Partials/ImageManipulation/ModalTitle.html create mode 100644 typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html rename typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/{ImageCropping.html => ImageManipulationWizard.html} (66%) create mode 100644 typo3/sysext/core/Tests/Unit/Imaging/ImageManipulation/AreaTest.php diff --git a/Build/Resources/Public/Less/TYPO3/_element_cropper.less b/Build/Resources/Public/Less/TYPO3/_element_cropper.less index 0404c0148477..853a93bc30ed 100644 --- a/Build/Resources/Public/Less/TYPO3/_element_cropper.less +++ b/Build/Resources/Public/Less/TYPO3/_element_cropper.less @@ -442,4 +442,4 @@ max-width: none !important; max-height: none !important; } -} \ No newline at end of file +} diff --git a/typo3/sysext/backend/Classes/Form/Element/ImageManipulationElement.php b/typo3/sysext/backend/Classes/Form/Element/ImageManipulationElement.php index 903affac09ad..3d2b9ff8cb3f 100644 --- a/typo3/sysext/backend/Classes/Form/Element/ImageManipulationElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/ImageManipulationElement.php @@ -120,7 +120,9 @@ class ImageManipulationElement extends AbstractFormElement parent::__construct($nodeFactory, $data); // Would be great, if we could inject the view here, but since the constructor is in the interface, we can't $this->templateView = GeneralUtility::makeInstance(StandaloneView::class); - $this->templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageCropping.html')); + $this->templateView->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts/')]); + $this->templateView->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials/ImageManipulation/')]); + $this->templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html')); $this->uriBuilder = GeneralUtility::makeInstance(UriBuilder::class); } @@ -136,29 +138,30 @@ class ImageManipulationElement extends AbstractFormElement $parameterArray = $this->data['parameterArray']; $config = $this->populateConfiguration($parameterArray['fieldConf']['config']); - if ($config['readOnly']) { - $options = []; - $options['parameterArray'] = [ - 'fieldConf' => [ - 'config' => $parameterArray['fieldConf']['config'], - ], - 'itemFormElValue' => $parameterArray['itemFormElValue'], - ]; - $options['renderType'] = 'none'; - - // Early return in case the field is set to read only - return $this->nodeFactory->create($options)->render(); - } - $file = $this->getFile($this->data['databaseRow'], $config['file_field']); if (!$file) { // Early return in case we do not find a file return $resultArray; } - $config = $this->processConfiguration($config, $parameterArray['itemFormElValue'] ?? '{}'); + $config = $this->processConfiguration($config, $parameterArray['itemFormElValue'], $file); + + $fieldInformationResult = $this->renderFieldInformation(); + $fieldInformationHtml = $fieldInformationResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldInformationResult, false); + + $fieldControlResult = $this->renderFieldControl(); + $fieldControlHtml = $fieldControlResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldControlResult, false); + + $fieldWizardResult = $this->renderFieldWizard(); + $fieldWizardHtml = $fieldWizardResult['html']; + $resultArray = $this->mergeChildReturnIntoExistingResult($resultArray, $fieldWizardResult, false); $arguments = [ + 'fieldInformation' => $fieldInformationHtml, + 'fieldControl' => $fieldControlHtml, + 'fieldWizard' => $fieldWizardHtml, 'isAllowedFileExtension' => in_array(strtolower($file->getExtension()), GeneralUtility::trimExplode(',', strtolower($config['allowedExtensions'])), true), 'image' => $file, 'formEngine' => [ @@ -182,7 +185,8 @@ class ImageManipulationElement extends AbstractFormElement $arguments['formEngine']['validation'] = $this->getValidationDataAsJsonString(['required' => true]); } } - $resultArray['html'] = $this->templateView->renderSection('Element', $arguments); + $this->templateView->assignMultiple($arguments); + $resultArray['html'] = $this->templateView->render(); return $resultArray; } @@ -271,13 +275,18 @@ class ImageManipulationElement extends AbstractFormElement /** * @param array $config * @param string $elementValue + * @param File $file * @return array * @throws \TYPO3\CMS\Core\Imaging\ImageManipulation\InvalidConfigurationException */ - protected function processConfiguration(array $config, string $elementValue) + protected function processConfiguration(array $config, string &$elementValue, File $file) { $cropVariantCollection = CropVariantCollection::create($elementValue, $config['cropVariants']); + if (empty($config['readOnly'])) { + $cropVariantCollection = $cropVariantCollection->applyRatioRestrictionToSelectedCropArea($file); + } $config['cropVariants'] = $cropVariantCollection->asArray(); + $elementValue = (string)$cropVariantCollection; $config['allowedExtensions'] = implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions'], true)); return $config; } diff --git a/typo3/sysext/backend/Classes/Form/Wizard/ImageManipulationWizard.php b/typo3/sysext/backend/Classes/Form/Wizard/ImageManipulationWizard.php index a176c5e62d0d..112d956bd0d9 100644 --- a/typo3/sysext/backend/Classes/Form/Wizard/ImageManipulationWizard.php +++ b/typo3/sysext/backend/Classes/Form/Wizard/ImageManipulationWizard.php @@ -40,7 +40,9 @@ class ImageManipulationWizard { if (!$templateView) { $templateView = GeneralUtility::makeInstance(StandaloneView::class); - $templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageCropping.html')); + $templateView->setLayoutRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Layouts/')]); + $templateView->setPartialRootPaths([GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Partials/ImageManipulation/')]); + $templateView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName('EXT:backend/Resources/Private/Templates/ImageManipulation/ImageManipulationWizard.html')); } $this->templateView = $templateView; } @@ -68,7 +70,7 @@ class ImageManipulationWizard 'image' => $image, 'cropVariants' => $queryParams['cropVariants'] ]; - $content = $this->templateView->renderSection('Cropper', $viewData); + $content = $this->templateView->renderSection('Main', $viewData); $response->getBody()->write($content); return $response; diff --git a/typo3/sysext/backend/Resources/Private/Layouts/ImageManipulation.html b/typo3/sysext/backend/Resources/Private/Layouts/ImageManipulation.html new file mode 100644 index 000000000000..05351ecbc8ba --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/Layouts/ImageManipulation.html @@ -0,0 +1,16 @@ +
+ {fieldInformation -> f:format.raw()} +
+
+ +
+
+
+ {fieldControl -> f:format.raw()} +
+
+
+ {fieldWizard -> f:format.raw()} +
+
+
diff --git a/typo3/sysext/backend/Resources/Private/Partials/ImageManipulation/ModalTitle.html b/typo3/sysext/backend/Resources/Private/Partials/ImageManipulation/ModalTitle.html new file mode 100644 index 000000000000..f251983a5740 --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/Partials/ImageManipulation/ModalTitle.html @@ -0,0 +1,9 @@ + + + + + : {image.properties.title} — {image.name}{image.name} + ({image.properties.width} × {image.properties.height}) + + + diff --git a/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html b/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html new file mode 100644 index 000000000000..61d526f6a8f5 --- /dev/null +++ b/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationElement.html @@ -0,0 +1,61 @@ + + + + +
+ + +
+ +
+ +
+
+
+ + +
+ + + +
+ + + + + + + + + +
{crop.width}px
{crop.height}px
+
+
+
+
+
+
+ +
+

+ +
+ {config.allowedExtensions -> f:format.case(mode: 'upper')} +

+
+
+
+
+
+ diff --git a/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageCropping.html b/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationWizard.html similarity index 66% rename from typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageCropping.html rename to typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationWizard.html index 7a2a03dc7ea1..fd9e97ac1924 100644 --- a/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageCropping.html +++ b/typo3/sysext/backend/Resources/Private/Templates/ImageManipulation/ImageManipulationWizard.html @@ -1,74 +1,20 @@ - -
- - -
- -
- -
-
-
-
- - - -
- - - - - - - - - -
{crop.width}px
{crop.height}px
-
-
-
-
- -
-

- -
- {config.allowedExtensions -> f:format.case(mode: 'upper')} -

-
-
-
-
-
- +