5d151542ee9c35e6631b8886909eaa4776a906fd
[Packages/TYPO3.CMS.git] / typo3 / sysext / rtehtmlarea / Classes / Controller / SelectImageController.php
1 <?php
2 namespace TYPO3\CMS\Rtehtmlarea\Controller;
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 Psr\Http\Message\ResponseInterface;
18 use Psr\Http\Message\ServerRequestInterface;
19 use TYPO3\CMS\Core\Configuration\Richtext;
20 use TYPO3\CMS\Core\Resource\ResourceFactory;
21 use TYPO3\CMS\Core\Resource\Service\MagicImageService;
22 use TYPO3\CMS\Core\Service\DependencyOrderingService;
23 use TYPO3\CMS\Core\Utility\ArrayUtility;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Recordlist\Controller\AbstractLinkBrowserController;
26 use TYPO3\CMS\Recordlist\LinkHandler\LinkHandlerInterface;
27
28 /**
29 * Script class to select images in RTE
30 */
31 class SelectImageController extends AbstractLinkBrowserController
32 {
33 /**
34 * These file extensions are allowed in the "plain" image selection mode.
35 *
36 * @const
37 */
38 const PLAIN_MODE_IMAGE_FILE_EXTENSIONS = 'jpg,jpeg,gif,png';
39
40 /**
41 * Active with TYPO3 Element Browser: Contains the name of the form field for which this window
42 * opens - thus allows us to make references back to the main window in which the form is.
43 * Example value: "data[pages][39][bodytext]|||tt_content|"
44 * or "data[tt_content][NEW3fba56fde763d][image]|||gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai|"
45 *
46 * Values:
47 * 0: form field name reference, eg. "data[tt_content][123][image]"
48 * 1: htmlArea RTE parameters: editorNo:contentTypo3Language
49 * 2: RTE config parameters: RTEtsConfigParams
50 * 3: allowed types. Eg. "tt_content" or "gif,jpg,jpeg,tif,bmp,pcx,tga,png,pdf,ai"
51 *
52 * $pArr = explode('|', $this->bparams);
53 * $formFieldName = $pArr[0];
54 * $allowedTablesOrFileTypes = $pArr[3];
55 *
56 * @var string
57 */
58 protected $bparams;
59
60 /**
61 * RTE configuration
62 *
63 * @var array
64 */
65 protected $RTEProperties = [];
66
67 /**
68 * Used with the Rich Text Editor.
69 * Example value: "tt_content:NEW3fba58c969f5c:bodytext:23:text:23:"
70 *
71 * @var string
72 */
73 protected $RTEtsConfigParams;
74
75 /**
76 * @var int
77 */
78 protected $editorNo;
79
80 /**
81 * TYPO3 language code of the content language
82 *
83 * @var int
84 */
85 protected $contentTypo3Language;
86
87 /**
88 * @var array
89 */
90 protected $buttonConfig = [];
91
92 /**
93 * Initialize controller
94 */
95 protected function init()
96 {
97 parent::init();
98 $this->getLanguageService()->includeLLFile('EXT:rtehtmlarea/Resources/Private/Language/locallang_dialogs.xlf');
99 }
100
101 /**
102 * @param ServerRequestInterface $request
103 */
104 protected function initVariables(ServerRequestInterface $request)
105 {
106 parent::initVariables($request);
107
108 $queryParameters = $request->getQueryParams();
109 $this->bparams = isset($queryParameters['bparams']) ? $queryParameters['bparams'] : '';
110 $this->currentLinkParts['currentImage'] = !empty($queryParameters['fileUid']) ? $queryParameters['fileUid'] : 0;
111
112 // Process bparams
113 $pArr = explode('|', $this->bparams);
114 $pRteArr = explode(':', $pArr[1]);
115 $this->editorNo = $pRteArr[0];
116 $this->contentTypo3Language = $pRteArr[1];
117 $this->RTEtsConfigParams = $pArr[2];
118 if (!$this->editorNo) {
119 $this->editorNo = GeneralUtility::_GP('editorNo');
120 $this->contentTypo3Language = GeneralUtility::_GP('contentTypo3Language');
121 $this->RTEtsConfigParams = GeneralUtility::_GP('RTEtsConfigParams');
122 }
123 $pArr[1] = implode(':', [$this->editorNo, $this->contentTypo3Language]);
124 $pArr[2] = $this->RTEtsConfigParams;
125 $pArr[3] = $this->displayedLinkHandlerId === 'plain'
126 ? self::PLAIN_MODE_IMAGE_FILE_EXTENSIONS
127 : '';
128 $this->bparams = implode('|', $pArr);
129
130 // @todo: This needs refactoring to enable sane config in flex form, either transfer parts of 'config', or use data providers
131 $RTEtsConfigParts = explode(':', $this->RTEtsConfigParams);
132 $table = $RTEtsConfigParts[0];
133 $field = $RTEtsConfigParts[2];
134 $recordType = $RTEtsConfigParts[3];
135 $tcaConfigOfField = $GLOBALS['TCA'][$table][$field]['config'] ?? [];
136 $columnsOverridesConfigOfField = $GLOBALS['TCA'][$table]['types'][$recordType]['columnsOverrides'][$field]['config'] ?? [];
137 if (!empty($columnsOverridesConfigOfField)) {
138 ArrayUtility::mergeRecursiveWithOverrule($tcaConfigOfField, $columnsOverridesConfigOfField);
139 }
140 $richtextConfigurationProvider = GeneralUtility::makeInstance(Richtext::class);
141 $richtextConfiguration = $richtextConfigurationProvider->getConfiguration(
142 $RTEtsConfigParts[0],
143 $RTEtsConfigParts[2],
144 $RTEtsConfigParts[3],
145 $RTEtsConfigParts[4],
146 $tcaConfigOfField
147 );
148 $this->RTEProperties = $richtextConfiguration;
149
150 $this->buttonConfig = isset($this->RTEProperties['buttons.']['image.'])
151 ? $this->RTEProperties['buttons.']['image.']
152 : [];
153 }
154
155 /**
156 * Initialize hook objects implementing the interface
157 *
158 * @throws \UnexpectedValueException
159 */
160 protected function initHookObjects()
161 {
162 if (
163 isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks'])
164 && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks'])
165 ) {
166 $hooks = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
167 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['RteImageSelector']['hooks']
168 );
169 foreach ($hooks as $key => $hook) {
170 $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
171 }
172 }
173 }
174
175 /**
176 * Reads the configured image handlers from page TSconfig
177 *
178 * @return array
179 * @throws \UnexpectedValueException
180 */
181 protected function getLinkHandlers()
182 {
183 $imageHandler = $this->buttonConfig['options.']['imageHandler.'];
184
185 foreach ($this->hookObjects as $hookObject) {
186 if (method_exists($hookObject, 'modifyImageHandlers')) {
187 $imageHandler = $hookObject->modifyImageHandlers($imageHandler, $this->currentLinkParts);
188 }
189 }
190
191 if (empty($imageHandler)) {
192 throw new \UnexpectedValueException('No image handlers are configured. Check page TSconfig RTE.default.buttons.image.options.imageHandler.', 1455499673);
193 }
194
195 return $imageHandler;
196 }
197
198 /**
199 * Initialize $this->currentLinkParts and $this->currentLinkHandler
200 */
201 protected function initCurrentUrl()
202 {
203 if (empty($this->currentLinkParts)) {
204 return;
205 }
206
207 $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
208
209 // find responsible handler for current image
210 foreach ($orderedHandlers as $key => $configuration) {
211 /** @var LinkHandlerInterface $handler */
212 $handler = $configuration['handlerInstance'];
213 if ($handler->canHandleLink($this->currentLinkParts)) {
214 $this->currentLinkHandler = $handler;
215 $this->currentLinkHandlerId = $key;
216 break;
217 }
218 }
219 // reset the image reference if we have no handler for it
220 if (!$this->currentLinkHandler) {
221 $this->currentLinkParts = [];
222 }
223 }
224
225 /**
226 * Render the currently set URL
227 *
228 * @return string
229 */
230 protected function renderCurrentUrl()
231 {
232 return '<!-- Print current URL -->
233 <div class="link-browser-section link-browser-current-link">
234 <strong>' .
235 htmlspecialchars($this->getLanguageService()->getLL('currentImage')) .
236 ': ' .
237 htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) .
238 '</strong>' .
239 '</div>';
240 }
241
242 /**
243 * Get the allowed items or tabs
244 *
245 * @return string[]
246 */
247 protected function getAllowedItems()
248 {
249 $allowedItems = array_keys($this->linkHandlers);
250
251 foreach ($this->hookObjects as $hookObject) {
252 if (method_exists($hookObject, 'modifyAllowedItems')) {
253 $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
254 }
255 }
256
257 return $allowedItems;
258 }
259
260 /**
261 * @param array $overrides
262 *
263 * @return array Array of parameters which have to be added to URLs
264 */
265 public function getUrlParameters(array $overrides = null)
266 {
267 return [
268 'act' => isset($overrides['act']) ? $overrides['act'] : $this->displayedLinkHandlerId,
269 'bparams' => $this->bparams,
270 'editorNo' => $this->editorNo
271 ];
272 }
273
274 /**
275 * @return array
276 */
277 public function getButtonConfiguration()
278 {
279 return $this->buttonConfig;
280 }
281
282 /**
283 * @return array
284 */
285 public function getRteProperties()
286 {
287 return $this->RTEProperties;
288 }
289
290 /**
291 * Compile the final tags to be inserted into RTE
292 *
293 * @param ServerRequestInterface $request
294 * @param ResponseInterface $response
295 * @return ResponseInterface
296 */
297 public function buildImageMarkup(ServerRequestInterface $request, ResponseInterface $response)
298 {
299 $this->initVariables($request);
300 $uidList = GeneralUtility::_GP('uidList');
301 // handle ajax request for
302 $uids = explode('|', $uidList);
303 $tags = [];
304 foreach ($uids as $uid) {
305 $fileObject = ResourceFactory::getInstance()->getFileObject((int)$uid);
306 // Get default values for alt and title attributes from file properties
307 $altText = $fileObject->getProperty('alternative');
308 $titleText = $fileObject->getProperty('title');
309 if ($this->displayedLinkHandlerId === 'magic') {
310 // Create the magic image service
311 $magicImageService = GeneralUtility::makeInstance(MagicImageService::class);
312 $magicImageService->setMagicImageMaximumDimensions($this->RTEProperties);
313 // Create the magic image
314 $imageConfiguration = [
315 'width' => GeneralUtility::_GP('cWidth'),
316 'height' => GeneralUtility::_GP('cHeight')
317 ];
318 $fileObject = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
319 $width = $fileObject->getProperty('width');
320 $height = $fileObject->getProperty('height');
321 } else {
322 $width = $fileObject->getProperty('width');
323 $height = $fileObject->getProperty('height');
324 if (!$width || !$height) {
325 $filePath = $fileObject->getForLocalProcessing(false);
326 $imageInfo = @getimagesize($filePath);
327 $width = $imageInfo[0];
328 $height = $imageInfo[1];
329 }
330 }
331 $imageUrl = $fileObject->getPublicUrl();
332 // If file is local, make the url absolute
333 if (strpos($imageUrl, 'http') !== 0) {
334 $imageUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $imageUrl;
335 }
336 $tags[] = '<img src="' . htmlspecialchars($imageUrl) . '" width="' . htmlspecialchars($width) . '" height="' . htmlspecialchars($height) . '"'
337 . (isset($this->buttonConfig['properties.']['class.']['default'])
338 ? ' class="' . trim($this->buttonConfig['properties.']['class.']['default']) . '"'
339 : '')
340 . ' alt = "' . ($altText ? htmlspecialchars($altText) : '') . '"'
341 . ($titleText ? ' title="' . htmlspecialchars($titleText) . '"' : '')
342 . ' data-htmlarea-file-uid="' . (int)$uid . '" />';
343 }
344 $finalHtmlCode = implode(' ', $tags);
345
346 $response->getBody()->write(json_encode(['images' => $finalHtmlCode]));
347 return $response;
348 }
349
350 /**
351 * Return the ID of current page
352 *
353 * @return int
354 * @throws \RuntimeException
355 */
356 protected function getCurrentPageId()
357 {
358 throw new \RuntimeException('Invalid method call. This function is not supported for image handlers', 1455499679);
359 }
360 }