[TASK] Remove second parameter of sL - Part 2/3
[Packages/TYPO3.CMS.git] / typo3 / sysext / recordlist / Classes / Controller / AbstractLinkBrowserController.php
1 <?php
2 namespace TYPO3\CMS\Recordlist\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\Backend\Routing\Router;
20 use TYPO3\CMS\Backend\Routing\UriBuilder;
21 use TYPO3\CMS\Backend\Template\DocumentTemplate;
22 use TYPO3\CMS\Backend\Utility\BackendUtility;
23 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
24 use TYPO3\CMS\Core\Service\DependencyOrderingService;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Lang\LanguageService;
27 use TYPO3\CMS\Recordlist\LinkHandler\LinkHandlerInterface;
28
29 /**
30 * Script class for the Link Browser window.
31 */
32 abstract class AbstractLinkBrowserController
33 {
34 /**
35 * @var DocumentTemplate
36 */
37 protected $doc;
38
39 /**
40 * @var array
41 */
42 protected $parameters;
43
44 /**
45 * URL of current request
46 *
47 * @var string
48 */
49 protected $thisScript = '';
50
51 /**
52 * @var LinkHandlerInterface[]
53 */
54 protected $linkHandlers = [];
55
56 /**
57 * All parts of the current link
58 *
59 * Comprised of url information and additional link parameters.
60 *
61 * @var string[]
62 */
63 protected $currentLinkParts = [];
64
65 /**
66 * Link handler responsible for the current active link
67 *
68 * @var LinkHandlerInterface $currentLinkHandler
69 */
70 protected $currentLinkHandler;
71
72 /**
73 * The ID of the currently active link handler
74 *
75 * @var string
76 */
77 protected $currentLinkHandlerId;
78
79 /**
80 * Link handler to be displayed
81 *
82 * @var LinkHandlerInterface $displayedLinkHandler
83 */
84 protected $displayedLinkHandler;
85
86 /**
87 * The ID of the displayed link handler
88 *
89 * This is read from the 'act' GET parameter
90 *
91 * @var string
92 */
93 protected $displayedLinkHandlerId = '';
94
95 /**
96 * List of available link attribute fields
97 *
98 * @var string[]
99 */
100 protected $linkAttributeFields = [];
101
102 /**
103 * Values of the link attributes
104 *
105 * @var string[]
106 */
107 protected $linkAttributeValues = [];
108
109 /**
110 * @var array
111 */
112 protected $hookObjects = [];
113
114 /**
115 * Constructor
116 */
117 public function __construct()
118 {
119 $this->initHookObjects();
120 $this->init();
121 }
122
123 /**
124 * Initialize the controller
125 *
126 * @return void
127 */
128 protected function init()
129 {
130 $this->getLanguageService()->includeLLFile('EXT:lang/locallang_browse_links.xlf');
131 }
132
133 /**
134 * Initialize hook objects implementing the interface
135 *
136 * @throws \UnexpectedValueException
137 * @return void
138 */
139 protected function initHookObjects()
140 {
141 if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'])
142 && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'])
143 ) {
144 $hooks = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
145 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks']
146 );
147 foreach ($hooks as $key => $hook) {
148 $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
149 }
150 }
151 }
152
153 /**
154 * Injects the request object for the current request or subrequest
155 * As this controller goes only through the main() method, it is rather simple for now
156 *
157 * @param ServerRequestInterface $request the current request
158 * @param ResponseInterface $response the prepared response object
159 * @return ResponseInterface the response with the content
160 */
161 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
162 {
163 $this->determineScriptUrl($request);
164 $this->initVariables($request);
165 $this->loadLinkHandlers();
166 $this->initCurrentUrl();
167
168 $menuData = $this->buildMenuArray();
169 $renderLinkAttributeFields = $this->renderLinkAttributeFields();
170 $browserContent = $this->displayedLinkHandler->render($request);
171
172 $this->initDocumentTemplate();
173 $content = $this->doc->startPage('Link Browser');
174 $content .= $this->doc->getFlashMessages();
175
176 if (!empty($this->currentLinkParts)) {
177 $content .= $this->renderCurrentUrl();
178 }
179
180 $content .= '<div class="link-browser-section link-browser-tabs">' . $this->doc->getTabMenuRaw($menuData) . '</div>';
181 $content .= $renderLinkAttributeFields;
182
183 $content .= $browserContent;
184 $content .= $this->doc->endPage();
185
186 $response->getBody()->write($this->doc->insertStylesAndJS($content));
187 return $response;
188 }
189
190 /**
191 * Sets the script url depending on being a module or script request
192 *
193 * @param ServerRequestInterface $request
194 *
195 * @throws \TYPO3\CMS\Backend\Routing\Exception\ResourceNotFoundException
196 * @throws \TYPO3\CMS\Backend\Routing\Exception\RouteNotFoundException
197 */
198 protected function determineScriptUrl(ServerRequestInterface $request)
199 {
200 if ($routePath = $request->getQueryParams()['route']) {
201 $router = GeneralUtility::makeInstance(Router::class);
202 $route = $router->match($routePath);
203 $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
204 $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
205 } elseif ($moduleName = $request->getQueryParams()['M']) {
206 $this->thisScript = BackendUtility::getModuleUrl($moduleName);
207 } else {
208 $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
209 }
210 }
211
212 /**
213 * @param ServerRequestInterface $request
214 */
215 protected function initVariables(ServerRequestInterface $request)
216 {
217 $queryParams = $request->getQueryParams();
218 $this->displayedLinkHandlerId = isset($queryParams['act']) ? $queryParams['act'] : '';
219 $this->parameters = isset($queryParams['P']) ? $queryParams['P'] : [];
220 $this->linkAttributeValues = isset($queryParams['linkAttributes']) ? $queryParams['linkAttributes'] : [];
221 }
222
223 /**
224 * @return void
225 * @throws \UnexpectedValueException
226 */
227 protected function loadLinkHandlers()
228 {
229 $linkHandlers = $this->getLinkHandlers();
230 if (empty($linkHandlers)) {
231 throw new \UnexpectedValueException('No link handlers are configured. Check page TSconfig TCEMAIN.linkHandler.', 1442787911);
232 }
233
234 $lang = $this->getLanguageService();
235 foreach ($linkHandlers as $identifier => $configuration) {
236 $identifier = rtrim($identifier, '.');
237 /** @var LinkHandlerInterface $handler */
238 $handler = GeneralUtility::makeInstance($configuration['handler']);
239 $handler->initialize(
240 $this,
241 $identifier,
242 isset($configuration['configuration.']) ? $configuration['configuration.'] : []
243 );
244
245 $this->linkHandlers[$identifier] = [
246 'handlerInstance' => $handler,
247 'label' => htmlspecialchars($lang->sL($configuration['label'])),
248 'displayBefore' => isset($configuration['displayBefore']) ? GeneralUtility::trimExplode(',', $configuration['displayBefore']) : [],
249 'displayAfter' => isset($configuration['displayAfter']) ? GeneralUtility::trimExplode(',', $configuration['displayAfter']) : [],
250 'scanBefore' => isset($configuration['scanBefore']) ? GeneralUtility::trimExplode(',', $configuration['scanBefore']) : [],
251 'scanAfter' => isset($configuration['scanAfter']) ? GeneralUtility::trimExplode(',', $configuration['scanAfter']) : [],
252 'addParams' => isset($configuration['addParams']) ? $configuration['addParams'] : '',
253 ];
254 }
255 }
256
257 /**
258 * Reads the configured link handlers from page TSconfig
259 *
260 * @return array
261 */
262 protected function getLinkHandlers()
263 {
264 $pageTSconfig = BackendUtility::getPagesTSconfig($this->getCurrentPageId());
265 $pageTSconfig = $this->getBackendUser()->getTSConfig('TCEMAIN.linkHandler.', $pageTSconfig);
266 $linkHandlers = (array)$pageTSconfig['properties'];
267
268 foreach ($this->hookObjects as $hookObject) {
269 if (method_exists($hookObject, 'modifyLinkHandlers')) {
270 $linkHandlers = $hookObject->modifyLinkHandlers($linkHandlers, $this->currentLinkParts);
271 }
272 }
273
274 return $linkHandlers;
275 }
276
277 /**
278 * Initialize $this->currentLinkParts and $this->currentLinkHandler
279 *
280 * @return void
281 */
282 protected function initCurrentUrl()
283 {
284 if (empty($this->currentLinkParts)) {
285 return;
286 }
287
288 $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
289
290 // find responsible handler for current link
291 foreach ($orderedHandlers as $key => $configuration) {
292 /** @var LinkHandlerInterface $handler */
293 $handler = $configuration['handlerInstance'];
294 if ($handler->canHandleLink($this->currentLinkParts)) {
295 $this->currentLinkHandler = $handler;
296 $this->currentLinkHandlerId = $key;
297 break;
298 }
299 }
300 // reset the link if we have no handler for it
301 if (!$this->currentLinkHandler) {
302 $this->currentLinkParts = [];
303 }
304
305 // overwrite any preexisting
306 foreach ($this->currentLinkParts as $key => $part) {
307 if ($key !== 'url') {
308 $this->linkAttributeValues[$key] = $part;
309 }
310 }
311 }
312
313 /**
314 * Initialize document template object
315 *
316 * @return void
317 */
318 protected function initDocumentTemplate()
319 {
320 $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
321 $this->doc->bodyTagId = 'typo3-browse-links-php';
322 $this->doc->divClass = 'link-browser';
323
324 foreach ($this->getBodyTagAttributes() as $attributeName => $value) {
325 $this->doc->bodyTagAdditions .= ' ' . $attributeName . '="' . htmlspecialchars($value) . '"';
326 }
327
328 // Finally, add the accumulated JavaScript to the template object:
329 // also unset the default jumpToUrl() function before
330 unset($this->doc->JScodeArray['jumpToUrl']);
331 }
332
333 /**
334 * Render the currently set URL
335 *
336 * @return string
337 */
338 protected function renderCurrentUrl()
339 {
340 return '<!-- Print current URL -->
341 <div class="link-browser-section link-browser-current-link">
342 <strong>' .
343 htmlspecialchars($this->getLanguageService()->getLL('currentLink')) .
344 ': ' .
345 htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) .
346 '</strong>
347 </div>';
348 }
349
350 /**
351 * Returns an array definition of the top menu
352 *
353 * @return mixed[][]
354 */
355 protected function buildMenuArray()
356 {
357 $allowedItems = $this->getAllowedItems();
358 if ($this->displayedLinkHandlerId && !in_array($this->displayedLinkHandlerId, $allowedItems, true)) {
359 $this->displayedLinkHandlerId = '';
360 }
361
362 $allowedHandlers = array_flip($allowedItems);
363 $menuDef = array();
364 foreach ($this->linkHandlers as $identifier => $configuration) {
365 if (!isset($allowedHandlers[$identifier])) {
366 continue;
367 }
368
369 /** @var LinkHandlerInterface $handlerInstance */
370 $handlerInstance = $configuration['handlerInstance'];
371 $isActive = $this->displayedLinkHandlerId === $identifier || !$this->displayedLinkHandlerId && $handlerInstance === $this->currentLinkHandler;
372 if ($isActive) {
373 $this->displayedLinkHandler = $handlerInstance;
374 if (!$this->displayedLinkHandlerId) {
375 $this->displayedLinkHandlerId = $this->currentLinkHandlerId;
376 }
377 }
378
379 if ($configuration['addParams']) {
380 $addParams = $configuration['addParams'];
381 } else {
382 $parameters = GeneralUtility::implodeArrayForUrl('', $this->getUrlParameters(['act' => $identifier]));
383 $addParams = 'onclick="jumpToUrl(' . GeneralUtility::quoteJSvalue('?' . ltrim($parameters, '&')) . ');return false;"';
384 }
385 $menuDef[$identifier] = [
386 'isActive' => $isActive,
387 'label' => $configuration['label'],
388 'url' => '#',
389 'addParams' => $addParams,
390 'before' => $configuration['displayBefore'],
391 'after' => $configuration['displayAfter']
392 ];
393 }
394
395 $menuDef = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($menuDef);
396
397 // if there is no active tab
398 if (!$this->displayedLinkHandler) {
399 // empty the current link
400 $this->currentLinkParts = [];
401 $this->currentLinkHandler = null;
402 $this->currentLinkHandler = '';
403 // select first tab
404 reset($menuDef);
405 $this->displayedLinkHandlerId = key($menuDef);
406 $this->displayedLinkHandler = $this->linkHandlers[$this->displayedLinkHandlerId]['handlerInstance'];
407 $menuDef[$this->displayedLinkHandlerId]['isActive'] = true;
408 }
409
410 return $menuDef;
411 }
412
413 /**
414 * Get the allowed items or tabs
415 *
416 * @return string[]
417 */
418 protected function getAllowedItems()
419 {
420 $allowedItems = array_keys($this->linkHandlers);
421
422 foreach ($this->hookObjects as $hookObject) {
423 if (method_exists($hookObject, 'modifyAllowedItems')) {
424 $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
425 }
426 }
427
428 // Initializing the action value, possibly removing blinded values etc:
429 $blindLinkOptions = isset($this->parameters['params']['blindLinkOptions'])
430 ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkOptions'])
431 : [];
432 $allowedItems = array_diff($allowedItems, $blindLinkOptions);
433
434 return $allowedItems;
435 }
436
437 /**
438 * Get the allowed link attributes
439 *
440 * @return string[]
441 */
442 protected function getAllowedLinkAttributes()
443 {
444 $allowedLinkAttributes = $this->displayedLinkHandler->getLinkAttributes();
445
446 // Removing link fields if configured
447 $blindLinkFields = isset($this->parameters['params']['blindLinkFields'])
448 ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkFields'], true)
449 : [];
450 $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
451
452 return $allowedLinkAttributes;
453 }
454
455 /**
456 * Renders the link attributes for the selected link handler
457 *
458 * @return string
459 */
460 public function renderLinkAttributeFields()
461 {
462 $fieldRenderingDefinitions = $this->getLinkAttributeFieldDefinitions();
463
464 $fieldRenderingDefinitions = $this->displayedLinkHandler->modifyLinkAttributes($fieldRenderingDefinitions);
465
466 $this->linkAttributeFields = $this->getAllowedLinkAttributes();
467
468 $content = '';
469 foreach ($this->linkAttributeFields as $attribute) {
470 $content .= $fieldRenderingDefinitions[$attribute];
471 }
472
473 // add update button if appropriate
474 if (!empty($this->currentLinkParts) && $this->displayedLinkHandler === $this->currentLinkHandler && $this->currentLinkHandler->isUpdateSupported()) {
475 $content .= '
476 <form action="" name="lparamsform" id="lparamsform" class="form-horizontal">
477 <div class="form-group form-group-sm">
478 <input class="btn btn-default t3js-linkCurrent" type="submit" value="' . htmlspecialchars($this->getLanguageService()->getLL('update')) . '" />
479 </div>
480 </form>';
481 }
482
483 return '<div class="link-browser-section link-browser-attributes">' . $content . '</div>';
484 }
485
486 /**
487 * Create an array of link attribute field rendering definitions
488 *
489 * @return string[]
490 */
491 protected function getLinkAttributeFieldDefinitions()
492 {
493 $lang = $this->getLanguageService();
494
495 $fieldRenderingDefinitions = [];
496 $fieldRenderingDefinitions['target'] = '
497 <!--
498 Selecting target for link:
499 -->
500 <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform form-horizontal">
501 <div class="form-group form-group-sm" id="typo3-linkTarget">
502 <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
503 <div class="col-xs-3">
504 <input type="text" name="ltarget" class="t3js-linkTarget form-control"
505 value="' . htmlspecialchars($this->linkAttributeValues['target']) . '" />
506 </div>
507 <div class="col-xs-5">
508 <select name="ltarget_type" class="t3js-targetPreselect form-control">
509 <option value=""></option>
510 <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
511 <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
512 </select>
513 </div>
514 </div>
515 </form>';
516
517 $fieldRenderingDefinitions['title'] = '
518 <!--
519 Selecting title for link:
520 -->
521 <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform form-horizontal">
522 <div class="form-group form-group-sm" id="typo3-linkTitle">
523 <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('title')) . '</label>
524 <div class="col-xs-8">
525 <input type="text" name="ltitle" class="form-control"
526 value="' . htmlspecialchars($this->linkAttributeValues['title']) . '" />
527 </div>
528 </div>
529 </form>
530 ';
531
532 $fieldRenderingDefinitions['class'] = '
533 <!--
534 Selecting class for link:
535 -->
536 <form action="" name="lclassform" id="lclassform" class="t3js-dummyform form-horizontal">
537 <div class="form-group form-group-sm" id="typo3-linkClass">
538 <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('class')) . '</label>
539 <div class="col-xs-8">
540 <input type="text" name="lclass" class="form-control"
541 value="' . htmlspecialchars($this->linkAttributeValues['class']) . '" /></td>
542 </div>
543 </div>
544 </form>
545 ';
546
547 $fieldRenderingDefinitions['params'] = '
548 <!--
549 Selecting params for link:
550 -->
551 <form action="" name="lparamsform" id="lparamsform" class="t3js-dummyform form-horizontal">
552 <div class="form-group form-group-sm" id="typo3-linkParams">
553 <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('params')) . '</label>
554 <div class="col-xs-8">
555 <input type="text" name="lparams" class="form-control"
556 value="' . htmlspecialchars($this->linkAttributeValues['params']) . '" />
557 </div>
558 </div>
559 </form>
560 ';
561
562 return $fieldRenderingDefinitions;
563 }
564
565 /**
566 * @param array $overrides
567 *
568 * @return array Array of parameters which have to be added to URLs
569 */
570 public function getUrlParameters(array $overrides = null)
571 {
572 return [
573 'act' => isset($overrides['act']) ? $overrides['act'] : $this->displayedLinkHandlerId
574 ];
575 }
576
577 /**
578 * Get attributes for the body tag
579 *
580 * @return string[] Array of body-tag attributes
581 */
582 protected function getBodyTagAttributes()
583 {
584 $parameters = [];
585 $parameters['uid'] = $this->parameters['uid'];
586 $parameters['pid'] = $this->parameters['pid'];
587 $parameters['itemName'] = $this->parameters['itemName'];
588 $parameters['formName'] = $this->parameters['formName'];
589 $parameters['params']['allowedExtensions'] = isset($this->parameters['params']['allowedExtensions']) ? $this->parameters['params']['allowedExtensions'] : '';
590 $parameters['params']['blindLinkOptions'] = isset($this->parameters['params']['blindLinkOptions']) ? $this->parameters['params']['blindLinkOptions'] : '';
591 $parameters['params']['blindLinkFields'] = isset($this->parameters['params']['blindLinkFields']) ? $this->parameters['params']['blindLinkFields']: '';
592 $addPassOnParams = GeneralUtility::implodeArrayForUrl('P', $parameters);
593
594 $attributes = $this->displayedLinkHandler->getBodyTagAttributes();
595 return array_merge(
596 $attributes,
597 [
598 'data-this-script-url' => strpos($this->thisScript, '?') === false ? $this->thisScript . '?' : $this->thisScript . '&',
599 'data-url-parameters' => json_encode($this->getUrlParameters()),
600 'data-parameters' => json_encode($this->parameters),
601 'data-add-on-params' => $addPassOnParams,
602 'data-link-attribute-fields' => json_encode($this->linkAttributeFields)
603 ]
604 );
605 }
606
607 /**
608 * Return the ID of current page
609 *
610 * @return int
611 */
612 abstract protected function getCurrentPageId();
613
614 /**
615 * @return array
616 */
617 public function getParameters()
618 {
619 return $this->parameters;
620 }
621
622 /**
623 * Retrieve the configuration
624 *
625 * @return array
626 */
627 public function getConfiguration()
628 {
629 return [];
630 }
631
632 /**
633 * @return string
634 */
635 public function getDisplayedLinkHandlerId()
636 {
637 return $this->displayedLinkHandlerId;
638 }
639
640 /**
641 * @return string
642 */
643 public function getScriptUrl()
644 {
645 return $this->thisScript;
646 }
647
648 /**
649 * @return LanguageService
650 */
651 protected function getLanguageService()
652 {
653 return $GLOBALS['LANG'];
654 }
655
656 /**
657 * @return BackendUserAuthentication
658 */
659 protected function getBackendUser()
660 {
661 return $GLOBALS['BE_USER'];
662 }
663 }