TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
ImageManipulationElement.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Backend\Form\Element;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
24 
29 {
35  protected $defaultConfig = [
36  'file_field' => 'uid_local',
37  'enableZoom' => false,
38  'allowedExtensions' => null, // default: $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
39  'ratios' => [
40  '1.7777777777777777' => 'LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.16_9',
41  '1.3333333333333333' => 'LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.4_3',
42  '1' => 'LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.1_1',
43  'NaN' => 'LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.ratio.free',
44  ]
45  ];
46 
52  public function render()
53  {
54  $resultArray = $this->initializeResultArray();
55  $languageService = $this->getLanguageService();
56 
57  $row = $this->data['databaseRow'];
58  $parameterArray = $this->data['parameterArray'];
59 
60  // If ratios are set do not add default options
61  if (isset($parameterArray['fieldConf']['config']['ratios'])) {
62  unset($this->defaultConfig['ratios']);
63  }
64  $config = array_replace_recursive($this->defaultConfig, $parameterArray['fieldConf']['config']);
65 
66  // By default we allow all image extensions that can be handled by the GFX functionality
67  if ($config['allowedExtensions'] === null) {
68  $config['allowedExtensions'] = $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'];
69  }
70 
71  if ($config['readOnly']) {
72  $options = [];
73  $options['parameterArray'] = [
74  'fieldConf' => [
75  'config' => $config,
76  ],
77  'itemFormElValue' => $parameterArray['itemFormElValue'],
78  ];
79  $options['renderType'] = 'none';
80  return $this->nodeFactory->create($options)->render();
81  }
82 
83  $file = $this->getFile($row, $config['file_field']);
84  if (!$file) {
85  return $resultArray;
86  }
87 
88  $content = '';
89  $preview = '';
90  if (GeneralUtility::inList(strtolower($config['allowedExtensions']), strtolower($file->getExtension()))) {
91 
92  // Get preview
93  $preview = $this->getPreview($file, $parameterArray['itemFormElValue']);
94 
95  // Check if ratio labels hold translation strings
96  foreach ((array)$config['ratios'] as $ratio => $label) {
97  $config['ratios'][$ratio] = htmlspecialchars($languageService->sL($label));
98  }
99 
100  $formFieldId = StringUtility::getUniqueId('formengine-image-manipulation-');
101  $wizardData = [
102  'zoom' => $config['enableZoom'] ? '1' : '0',
103  'ratios' => json_encode($config['ratios']),
104  'file' => $file->getUid(),
105  ];
106  $wizardData['token'] = GeneralUtility::hmac(implode('|', $wizardData), 'ImageManipulationWizard');
107 
109  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
110  $buttonAttributes = [
111  'data-url' => $uriBuilder->buildUriFromRoute('ajax_wizard_image_manipulation', $wizardData),
112  'data-severity' => 'notice',
113  'data-image-name' => $file->getNameWithoutExtension(),
114  'data-image-uid' => $file->getUid(),
115  'data-file-field' => $config['file_field'],
116  'data-field' => $formFieldId,
117  ];
118 
119  $button = '<button class="btn btn-default t3js-image-manipulation-trigger"';
120  $button .= GeneralUtility::implodeAttributes($buttonAttributes, true, true);
121  $button .= '><span class="t3-icon fa fa-crop"></span>';
122  $button .= htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.open-editor'));
123  $button .= '</button>';
124 
125  $attributes = [];
126  $attributes['type'] = 'hidden';
127  $attributes['id'] = $formFieldId;
128  $attributes['name'] = $parameterArray['itemFormElName'];
129  $attributes['value'] = $parameterArray['itemFormElValue'];
130 
131  $evalList = GeneralUtility::trimExplode(',', $config['eval'], true);
132  if (in_array('required', $evalList, true)) {
133  $attributes['data-formengine-validation-rules'] = $this->getValidationDataAsJsonString(['required' => true]);
134  }
135 
136  $inputField = '<input ' . GeneralUtility::implodeAttributes($attributes, true, true) . '" />';
137 
138  $content .= $inputField . $button;
139 
140  $content .= $this->getImageManipulationInfoTable($parameterArray['itemFormElValue']);
141 
142  $resultArray['requireJsModules'][] = [
143  'TYPO3/CMS/Backend/ImageManipulation' => 'function(ImageManipulation){ImageManipulation.initializeTrigger()}'
144  ];
145  }
146 
147  $content .= '<p class="text-muted"><em>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.supported-types-message')) . '<br />';
148  $content .= strtoupper(implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions'])));
149  $content .= '</em></p>';
150 
151  $item = '<div class="media">';
152  $item .= $preview;
153  $item .= '<div class="media-body">' . $content . '</div>';
154  $item .= '</div>';
155 
156  $resultArray['html'] = $item;
157  return $resultArray;
158  }
159 
167  protected function getFile(array $row, $fieldName)
168  {
169  $file = null;
170  $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : null;
171  if (strpos($fileUid, 'sys_file_') === 0) {
172  if (strpos($fileUid, '|')) {
173  // @todo: uid_local is a group field that was resolved to table_uid|target - split here again
174  // @todo: this will vanish if group fields are moved to array
175  $fileUid = explode('|', $fileUid);
176  $fileUid = $fileUid[0];
177  }
178  $fileUid = substr($fileUid, 9);
179  }
181  try {
182  $file = ResourceFactory::getInstance()->getFileObject($fileUid);
183  } catch (FileDoesNotExistException $e) {
184  } catch (\InvalidArgumentException $e) {
185  }
186  }
187  return $file;
188  }
189 
197  public function getPreview(File $file, $crop)
198  {
199  $thumbnail = '';
200  $maxWidth = 150;
201  $maxHeight = 200;
202  if ($crop) {
203  $imageSetup = ['maxWidth' => $maxWidth, 'maxHeight' => $maxHeight, 'crop' => $crop];
204  $processedImage = $file->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $imageSetup);
205  // Only use a thumbnail if the processing process was successful by checking if image width is set
206  if ($processedImage->getProperty('width')) {
207  $imageUrl = $processedImage->getPublicUrl(true);
208  $thumbnail = '<img src="' . $imageUrl . '" ' .
209  'class="thumbnail thumbnail-status" ' .
210  'width="' . $processedImage->getProperty('width') . '" ' .
211  'height="' . $processedImage->getProperty('height') . '" >';
212  }
213  }
214 
215  $preview = '<div class="media-left">';
216  $preview .= '<div class="t3js-image-manipulation-preview media-object' . ($thumbnail ? '' : ' hide') . '" ';
217  // Set preview width/height needed by cropper
218  $preview .= 'data-preview-width="' . $maxWidth . '" data-preview-height="' . $maxHeight . '">';
219  $preview .= $thumbnail;
220  $preview .= '</div></div>';
221 
222  return $preview;
223  }
224 
231  protected function getImageManipulationInfoTable($rawImageManipulationValue)
232  {
233  $content = '';
234  $imageManipulation = null;
235  $x = $y = $width = $height = 0;
236 
237  // Determine cropping values
238  if ($rawImageManipulationValue) {
239  $imageManipulation = json_decode($rawImageManipulationValue);
240  if (is_object($imageManipulation)) {
241  $x = (int)$imageManipulation->x;
242  $y = (int)$imageManipulation->y;
243  $width = (int)$imageManipulation->width;
244  $height = (int)$imageManipulation->height;
245  } else {
246  $imageManipulation = null;
247  }
248  }
249  $languageService = $this->getLanguageService();
250 
251  $content .= '<div class="table-fit-block table-spacer-wrap">';
252  $content .= '<table class="table table-no-borders t3js-image-manipulation-info' . ($imageManipulation === null ? ' hide' : '') . '">';
253  $content .= '<tr><td>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop-x')) . '</td>';
254  $content .= '<td class="t3js-image-manipulation-info-crop-x">' . $x . 'px</td></tr>';
255  $content .= '<tr><td>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop-y')) . '</td>';
256  $content .= '<td class="t3js-image-manipulation-info-crop-y">' . $y . 'px</td></tr>';
257  $content .= '<tr><td>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop-width')) . '</td>';
258  $content .= '<td class="t3js-image-manipulation-info-crop-width">' . $width . 'px</td></tr>';
259  $content .= '<tr><td>' . htmlspecialchars($languageService->sL('LLL:EXT:lang/Resources/Private/Language/locallang_wizards.xlf:imwizard.crop-height')) . '</td>';
260  $content .= '<td class="t3js-image-manipulation-info-crop-height">' . $height . 'px</td></tr>';
261  $content .= '</table>';
262  $content .= '</div>';
263 
264  return $content;
265  }
266 }
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
process($taskType, array $configuration)
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static hmac($input, $additionalSecret= '')