1d2455dc6f0f78ecb6da09bebc9380d86c808d5c
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / Form / Element / ImageManipulationElement.php
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
17 use TYPO3\CMS\Backend\Form\FormEngine;
18 use TYPO3\CMS\Backend\Utility\BackendUtility;
19 use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException;
20 use TYPO3\CMS\Core\Resource\File;
21 use TYPO3\CMS\Core\Resource\ResourceFactory;
22 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
23 use TYPO3\CMS\Core\Utility\GeneralUtility;
24 use TYPO3\CMS\Core\Utility\MathUtility;
25 use TYPO3\CMS\Extbase\Utility\ArrayUtility;
26 use TYPO3\CMS\Extbase\Utility\DebuggerUtility;
27
28 /**
29 * Generation of crop image TCEform elements
30 */
31 class ImageManipulationElement extends AbstractFormElement {
32
33 /**
34 * Default element configuration
35 *
36 * @var array
37 */
38 protected $defaultConfig = array(
39 'file_field' => 'uid_local',
40 'enableZoom' => FALSE,
41 'allowedExtensions' => NULL, // default: $GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext']
42 'ratios' => array(
43 '1.7777777777777777' => 'LLL:EXT:lang/locallang_wizards.xlf:imwizard.ratio.16_9',
44 '1.3333333333333333' => 'LLL:EXT:lang/locallang_wizards.xlf:imwizard.ratio.4_3',
45 '1' => 'LLL:EXT:lang/locallang_wizards.xlf:imwizard.ratio.1_1',
46 'NaN' => 'LLL:EXT:lang/locallang_wizards.xlf:imwizard.ratio.free',
47 )
48 );
49
50 /**
51 * Handler for unknown types.
52 *
53 * @param string $table The table name of the record
54 * @param string $field The field name which this element is supposed to edit
55 * @param array $row The record data array where the value(s) for the field can be found
56 * @param array $additionalInformation An array with additional configuration options.
57 * @return string The HTML code for the TCEform field
58 */
59 public function render($table, $field, $row, &$additionalInformation) {
60 // If ratios are set do not add default options
61 if (isset($additionalInformation['fieldConf']['config']['ratios'])) {
62 unset($this->defaultConfig['ratios']);
63 }
64 $config = ArrayUtility::arrayMergeRecursiveOverrule($this->defaultConfig, $additionalInformation['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 ($this->isGlobalReadonly() || $config['readOnly']) {
72 $formEngineDummy = new FormEngine();
73 $noneElement = GeneralUtility::makeInstance(NoneElement::class, $formEngineDummy);
74 $elementConfiguration = array(
75 'fieldConf' => array(
76 'config' => $config,
77 ),
78 'itemFormElValue' => $additionalInformation['itemFormElValue'],
79 );
80 return $noneElement->render('', '', '', $elementConfiguration);
81 }
82
83 $file = $this->getFile($row, $config['file_field']);
84 if (!$file) {
85 return '';
86 }
87
88 $content = '';
89 $preview = '';
90 if (GeneralUtility::inList(mb_strtolower($config['allowedExtensions']), mb_strtolower($file->getExtension()))) {
91
92 // Get preview
93 $preview = $this->getPreview($file, $additionalInformation['itemFormElValue']);
94
95 // Check if ratio labels hold translation strings
96 $languageService = $this->getLanguageService();
97 foreach ((array)$config['ratios'] as $ratio => $label) {
98 $config['ratios'][$ratio] = $languageService->sL($label, TRUE);
99 }
100
101 $formFieldId = str_replace('.', '', uniqid('formengine-image-manipulation-', TRUE));
102 $wizardData = array(
103 'file' => $file->getUid(),
104 'zoom' => $config['enableZoom'] ? '1' : '0',
105 'ratios' => json_encode($config['ratios']),
106 );
107 $wizardData['token'] = GeneralUtility::hmac(implode('|', $wizardData), 'ImageManipulationWizard');
108
109 $buttonAttributes = array(
110 'data-url' => BackendUtility::getAjaxUrl('ImageManipulationWizard::getHtmlForImageManipulationWizard', $wizardData),
111 'data-severity' => 'notice',
112 'data-image-name' => $file->getNameWithoutExtension(),
113 'data-image-uid' => $file->getUid(),
114 'data-file-field' => $config['file_field'],
115 'data-field' => $formFieldId,
116 );
117
118 $button = '<button class="btn btn-default t3js-image-manipulation-trigger"';
119 foreach ($buttonAttributes as $key => $value) {
120 $button .= ' ' . $key . '="' . htmlspecialchars($value) . '"';
121 }
122 $button .= '><span class="t3-icon fa fa-crop"></span>';
123 $button .= $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.open-editor', TRUE);
124 $button .= '</button>';
125
126 $inputField = '<input type="hidden" '
127 . 'id="' . $formFieldId . '" '
128 . 'name="' . $additionalInformation['itemFormElName'] . '" '
129 . 'value="' . htmlspecialchars($additionalInformation['itemFormElValue']) . '" />';
130
131 $content .= $inputField . $button;
132
133 $content .= $this->getImageManipulationInfoTable($additionalInformation['itemFormElValue']);
134
135 /** @var $pageRenderer \TYPO3\CMS\Core\Page\PageRenderer */
136 $pageRenderer = $GLOBALS['SOBE']->doc->getPageRenderer();
137 $pageRenderer->loadRequireJsModule(
138 'TYPO3/CMS/Backend/ImageManipulation',
139 'function(ImageManipulation){ImageManipulation.initializeTrigger()}' // Initialize after load
140 );
141 }
142
143 $content .= '<p class="text-muted"><em>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.supported-types-message', TRUE) . '<br />';
144 $content .= mb_strtoupper(implode(', ', GeneralUtility::trimExplode(',', $config['allowedExtensions'])));
145 $content .= '</em></p>';
146
147 $item = '<div class="media">';
148 $item .= $preview;
149 $item .= '<div class="media-body">' . $content . '</div>';
150 $item .= '</div>';
151
152 return $item;
153 }
154
155 /**
156 * Get file object
157 *
158 * @param array $row
159 * @param string $fieldName
160 * @return NULL|\TYPO3\CMS\Core\Resource\File
161 */
162 protected function getFile(array $row, $fieldName) {
163 $file = NULL;
164 $fileUid = !empty($row[$fieldName]) ? $row[$fieldName] : NULL;
165
166 if (strpos($fileUid, 'sys_file_') === 0) {
167 $fileUid = substr($fileUid, 9);
168 }
169 if (MathUtility::canBeInterpretedAsInteger($fileUid)) {
170 try {
171 $file = ResourceFactory::getInstance()->getFileObject($fileUid);
172 } catch (FileDoesNotExistException $e) {
173 }
174 }
175 return $file;
176 }
177
178 /**
179 * Get preview image if cropping is set
180 *
181 * @param File $file
182 * @param string $crop
183 * @return string
184 */
185 public function getPreview(File $file, $crop) {
186 $preview = '';
187 if ($crop) {
188 $imageSetup = array('width' => '150m', 'height' => '200m', 'crop' => $crop);
189 $processedImage = $file->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGECROPSCALEMASK, $imageSetup);
190 // Only use a thumbnail if the processing process was successful by checking if image width is set
191 if ($processedImage->getProperty('width')) {
192 $imageUrl = $processedImage->getPublicUrl(TRUE);
193 $preview = '<img src="' . $imageUrl . '" ' .
194 'class="media-object thumbnail" ' .
195 'width="' . $processedImage->getProperty('width') . '" ' .
196 'height="' . $processedImage->getProperty('height') . '" >';
197 }
198 }
199 return '<div class="media-left t3js-image-manipulation-preview' . ($preview ? '' : ' hide'). '">' . $preview . '</div>';
200 }
201
202 /**
203 * Get image manipulation info table
204 *
205 * @param string $rawImageManipulationValue
206 * @return string
207 */
208 protected function getImageManipulationInfoTable($rawImageManipulationValue) {
209 $content = '';
210 $imageManipulation = NULL;
211 $x = $y = $width = $height = 0;
212
213 // Determine cropping values
214 if ($rawImageManipulationValue) {
215 $imageManipulation = json_decode($rawImageManipulationValue);
216 if (is_object($imageManipulation)) {
217 $x = (int)$imageManipulation->x;
218 $y = (int)$imageManipulation->y;
219 $width = (int)$imageManipulation->width;
220 $height = (int)$imageManipulation->height;
221 } else {
222 $imageManipulation = NULL;
223 }
224 }
225 $languageService = $this->getLanguageService();
226
227 $content .= '<div class="table-fit-block table-spacer-wrap">';
228 $content .= '<table class="table table-no-borders t3js-image-manipulation-info'. ($imageManipulation === NULL ? ' hide' : '') . '">';
229 $content .= '<tr><td>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.crop-x', TRUE) . '</td>';
230 $content .= '<td class="t3js-image-manipulation-info-crop-x">' . $x . 'px</td></tr>';
231 $content .= '<tr><td>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.crop-y', TRUE) . '</td>';
232 $content .= '<td class="t3js-image-manipulation-info-crop-y">' . $y . 'px</td></tr>';
233 $content .= '<tr><td>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.crop-width', TRUE) . '</td>';
234 $content .= '<td class="t3js-image-manipulation-info-crop-width">' . $width . 'px</td></tr>';
235 $content .= '<tr><td>' . $languageService->sL('LLL:EXT:lang/locallang_wizards.xlf:imwizard.crop-height', TRUE) . '</td>';
236 $content .= '<td class="t3js-image-manipulation-info-crop-height">' . $height . 'px</td></tr>';
237 $content .= '</table>';
238 $content .= '</div>';
239
240 return $content;
241 }
242 }