‪TYPO3CMS  9.5
AbstractLinkBrowserController.php
Go to the documentation of this file.
1 <?php
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;
30 
36 {
38 
42  protected ‪$deprecatedPublicMethods = [
43  'renderLinkAttributeFields' => 'Using AbstractLinkBrowserController::renderLinkAttributeFields() is deprecated and will not be possible anymore in TYPO3 v10.0.',
44  'getDisplayedLinkHandlerId' => 'Using AbstractLinkBrowzerController::getDisplayedLinkHandlerId() is deprecated and will not be possible anymore in TYPO3 v10.0.',
45  ];
46 
50  protected ‪$doc;
51 
55  protected ‪$parameters;
56 
62  protected ‪$thisScript = '';
63 
67  protected ‪$linkHandlers = [];
68 
76  protected ‪$currentLinkParts = [];
77 
83  protected ‪$currentLinkHandler;
84 
90  protected ‪$currentLinkHandlerId;
91 
97  protected ‪$displayedLinkHandler;
98 
106  protected ‪$displayedLinkHandlerId = '';
107 
113  protected ‪$linkAttributeFields = [];
114 
120  protected ‪$linkAttributeValues = [];
121 
125  protected ‪$hookObjects = [];
126 
130  public function ‪__construct()
131  {
132  $this->‪initHookObjects();
133  $this->‪init();
134  }
135 
139  protected function ‪init()
140  {
141  $this->‪getLanguageService()->‪includeLLFile('EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf');
142  }
143 
149  protected function ‪initHookObjects()
150  {
151  $hooks = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
152  ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'] ?? []
153  );
154  foreach ($hooks as $key => $hook) {
155  $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
156  }
157  }
158 
166  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
167  {
168  $this->‪determineScriptUrl($request);
169  $this->‪initVariables($request);
170  $this->‪loadLinkHandlers();
171  $this->‪initCurrentUrl();
172 
173  $menuData = $this->‪buildMenuArray();
174  $renderLinkAttributeFields = $this->‪renderLinkAttributeFields();
175  $browserContent = $this->displayedLinkHandler->render($request);
176 
177  $this->‪initDocumentTemplate();
178  $content = $this->doc->startPage('Link Browser');
179  $content .= $this->doc->getFlashMessages();
180 
181  if (!empty($this->currentLinkParts)) {
182  $content .= $this->‪renderCurrentUrl();
183  }
184 
185  $options = '';
186  foreach ($menuData as $id => $def) {
187  $class = $def['isActive'] ? ' class="active"' : '';
188 
189  $options .= '<li' . $class . '>'
190  . '<a href="' . htmlspecialchars($def['url']) . '" ' . $def['addParams'] . '>' . htmlspecialchars($def['label']) . '</a>'
191  . '</li>';
192  }
193 
194  $content .= '<div class="element-browser-panel element-browser-tabs"><ul class="nav nav-tabs" role="tablist">' .
195  $options . '</ul></div>';
196 
197  $content .= $renderLinkAttributeFields;
198 
199  $content .= $browserContent;
200  $content .= $this->doc->endPage();
201 
202  return new ‪HtmlResponse($this->doc->insertStylesAndJS($content));
203  }
204 
213  protected function ‪determineScriptUrl(ServerRequestInterface $request)
214  {
215  if ($routePath = $request->getQueryParams()['route']) {
216  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
217  $this->thisScript = (string)$uriBuilder->buildUriFromRoutePath($routePath);
218  } else {
219  $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
220  }
221  }
222 
226  protected function ‪initVariables(ServerRequestInterface $request)
227  {
228  $queryParams = $request->getQueryParams();
229  $this->displayedLinkHandlerId = $queryParams['act'] ?? '';
230  $this->parameters = $queryParams['P'] ?? [];
231  $this->linkAttributeValues = $queryParams['linkAttributes'] ?? [];
232  }
233 
237  protected function ‪loadLinkHandlers()
238  {
240  if (empty(‪$linkHandlers)) {
241  throw new \UnexpectedValueException('No link handlers are configured. Check page TSconfig TCEMAIN.linkHandler.', 1442787911);
242  }
243 
244  $lang = $this->‪getLanguageService();
245  foreach (‪$linkHandlers as $identifier => $configuration) {
246  $identifier = rtrim($identifier, '.');
247 
248  if (empty($configuration['handler'])) {
249  throw new \UnexpectedValueException(sprintf('Missing handler for link handler "%1$s", check page TSconfig TCEMAIN.linkHandler.%1$s.handler', $identifier), 1494579849);
250  }
251 
253  $handler = GeneralUtility::makeInstance($configuration['handler']);
254  $handler->initialize(
255  $this,
256  $identifier,
257  $configuration['configuration.'] ?? []
258  );
259 
260  $label = !empty($configuration['label']) ? $lang->sL($configuration['label']) : '';
261  $label = $label ?: $lang->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang.xlf:error.linkHandlerTitleMissing');
262  $this->linkHandlers[$identifier] = [
263  'handlerInstance' => $handler,
264  'label' => htmlspecialchars($label),
265  'displayBefore' => isset($configuration['displayBefore']) ? GeneralUtility::trimExplode(',', $configuration['displayBefore']) : [],
266  'displayAfter' => isset($configuration['displayAfter']) ? ‪GeneralUtility::trimExplode(',', $configuration['displayAfter']) : [],
267  'scanBefore' => isset($configuration['scanBefore']) ? ‪GeneralUtility::trimExplode(',', $configuration['scanBefore']) : [],
268  'scanAfter' => isset($configuration['scanAfter']) ? ‪GeneralUtility::trimExplode(',', $configuration['scanAfter']) : [],
269  'addParams' => $configuration['addParams'] ?? '',
270  ];
271  }
272  }
273 
279  protected function ‪getLinkHandlers()
280  {
281  ‪$linkHandlers = (array)(‪BackendUtility::getPagesTSconfig($this->‪getCurrentPageId())['TCEMAIN.']['linkHandler.'] ?? []);
282  foreach ($this->hookObjects as $hookObject) {
283  if (method_exists($hookObject, 'modifyLinkHandlers')) {
284  ‪$linkHandlers = $hookObject->modifyLinkHandlers(‪$linkHandlers, $this->currentLinkParts);
285  }
286  }
287 
288  return ‪$linkHandlers;
289  }
290 
294  protected function ‪initCurrentUrl()
295  {
296  if (empty($this->currentLinkParts)) {
297  return;
298  }
299 
300  $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
301 
302  // find responsible handler for current link
303  foreach ($orderedHandlers as $key => $configuration) {
305  $handler = $configuration['handlerInstance'];
306  if ($handler->canHandleLink($this->currentLinkParts)) {
307  $this->currentLinkHandler = $handler;
308  $this->currentLinkHandlerId = $key;
309  break;
310  }
311  }
312  // reset the link if we have no handler for it
313  if (!$this->currentLinkHandler) {
314  $this->currentLinkParts = [];
315  }
316 
317  // overwrite any preexisting
318  foreach ($this->currentLinkParts as $key => $part) {
319  if ($key !== 'url') {
320  $this->linkAttributeValues[$key] = $part;
321  }
322  }
323  }
324 
328  protected function ‪initDocumentTemplate()
329  {
330  $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
331  $this->doc->divClass = 'element-browser';
332 
333  foreach ($this->‪getBodyTagAttributes() as $attributeName => $value) {
334  $this->doc->bodyTagAdditions .= ' ' . $attributeName . '="' . htmlspecialchars($value) . '"';
335  }
336 
337  // Finally, add the accumulated JavaScript to the template object:
338  // also unset the default jumpToUrl() function before
339  unset($this->doc->JScodeArray['jumpToUrl']);
340  }
341 
347  protected function ‪renderCurrentUrl()
348  {
349  return '<!-- Print current URL -->
350  <div class="element-browser-panel element-browser-title">' .
351  htmlspecialchars($this->‪getLanguageService()->getLL('currentLink')) .
352  ': ' .
353  htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) .
354  '</div>';
355  }
356 
362  protected function ‪buildMenuArray()
363  {
364  $allowedItems = $this->‪getAllowedItems();
365  if ($this->displayedLinkHandlerId && !in_array($this->displayedLinkHandlerId, $allowedItems, true)) {
366  $this->displayedLinkHandlerId = '';
367  }
368 
369  $allowedHandlers = array_flip($allowedItems);
370  $menuDef = [];
371  foreach ($this->linkHandlers as $identifier => $configuration) {
372  if (!isset($allowedHandlers[$identifier])) {
373  continue;
374  }
375 
377  $handlerInstance = $configuration['handlerInstance'];
378  $isActive = $this->displayedLinkHandlerId === $identifier || !$this->displayedLinkHandlerId && $handlerInstance === ‪$this->currentLinkHandler;
379  if ($isActive) {
380  $this->displayedLinkHandler = $handlerInstance;
381  if (!$this->displayedLinkHandlerId) {
382  $this->displayedLinkHandlerId = ‪$this->currentLinkHandlerId;
383  }
384  }
385 
386  $menuDef[$identifier] = [
387  'isActive' => $isActive,
388  'label' => $configuration['label'],
389  'url' => $this->thisScript . ‪HttpUtility::buildQueryString($this->‪getUrlParameters(['act' => $identifier]), '&'),
390  'addParams' => $configuration['addParams'] ?? '',
391  'before' => $configuration['displayBefore'],
392  'after' => $configuration['displayAfter']
393  ];
394  }
395 
396  $menuDef = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($menuDef);
397 
398  // if there is no active tab
399  if (!$this->displayedLinkHandler) {
400  // empty the current link
401  $this->currentLinkParts = [];
402  $this->currentLinkHandler = null;
403  $this->currentLinkHandler = '';
404  // select first tab
405  reset($menuDef);
406  $this->displayedLinkHandlerId = key($menuDef);
407  $this->displayedLinkHandler = $this->linkHandlers[‪$this->displayedLinkHandlerId]['handlerInstance'];
408  $menuDef[‪$this->displayedLinkHandlerId]['isActive'] = true;
409  }
410 
411  return $menuDef;
412  }
413 
419  protected function ‪getAllowedItems()
420  {
421  $allowedItems = array_keys($this->linkHandlers);
422 
423  foreach ($this->hookObjects as $hookObject) {
424  if (method_exists($hookObject, 'modifyAllowedItems')) {
425  $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
426  }
427  }
428 
429  // Initializing the action value, possibly removing blinded values etc:
430  $blindLinkOptions = isset($this->parameters['params']['blindLinkOptions'])
431  ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkOptions'])
432  : [];
433  $allowedItems = array_diff($allowedItems, $blindLinkOptions);
434 
435  return $allowedItems;
436  }
437 
443  protected function ‪getAllowedLinkAttributes()
444  {
445  $allowedLinkAttributes = $this->displayedLinkHandler->getLinkAttributes();
446 
447  // Removing link fields if configured
448  $blindLinkFields = isset($this->parameters['params']['blindLinkFields'])
449  ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkFields'], true)
450  : [];
451  $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
452 
453  return $allowedLinkAttributes;
454  }
455 
461  protected function ‪renderLinkAttributeFields()
462  {
463  $fieldRenderingDefinitions = $this->‪getLinkAttributeFieldDefinitions();
464 
465  $fieldRenderingDefinitions = $this->displayedLinkHandler->modifyLinkAttributes($fieldRenderingDefinitions);
466 
467  $this->linkAttributeFields = $this->‪getAllowedLinkAttributes();
468 
469  $content = '';
470  foreach ($this->linkAttributeFields as $attribute) {
471  $content .= $fieldRenderingDefinitions[$attribute];
472  }
473 
474  // add update button if appropriate
475  if (!empty($this->currentLinkParts) && $this->displayedLinkHandler === $this->currentLinkHandler && $this->currentLinkHandler->isUpdateSupported()) {
476  $content .= '
477  <form action="" name="lparamsform" id="lparamsform" class="form-horizontal">
478  <div class="form-group form-group-sm">
479  <div class="col-xs-12">
480  <input class="btn btn-default t3js-linkCurrent" type="submit" value="' . htmlspecialchars($this->‪getLanguageService()->getLL('update')) . '" />
481  </div>
482  </div>
483  </form>';
484  }
485 
486  return '<div class="element-browser-panel element-browser-attributes">' . $content . '</div>';
487  }
488 
494  protected function ‪getLinkAttributeFieldDefinitions()
495  {
496  $lang = $this->‪getLanguageService();
497 
498  $fieldRenderingDefinitions = [];
499  $fieldRenderingDefinitions['target'] = '
500  <!-- Selecting target for link: -->
501  <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform form-horizontal">
502  <div class="form-group form-group-sm" id="typo3-linkTarget">
503  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
504  <div class="col-xs-3">
505  <input type="text" name="ltarget" class="t3js-linkTarget form-control"
506  value="' . htmlspecialchars($this->linkAttributeValues['target']) . '" />
507  </div>
508  <div class="col-xs-5">
509  <select name="ltarget_type" class="t3js-targetPreselect form-control">
510  <option value=""></option>
511  <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
512  <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
513  </select>
514  </div>
515  </div>
516  </form>';
517 
518  $fieldRenderingDefinitions['title'] = '
519  <!-- Selecting title for link: -->
520  <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform form-horizontal">
521  <div class="form-group form-group-sm" id="typo3-linkTitle">
522  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('title')) . '</label>
523  <div class="col-xs-8">
524  <input type="text" name="ltitle" class="form-control"
525  value="' . htmlspecialchars($this->linkAttributeValues['title']) . '" />
526  </div>
527  </div>
528  </form>';
529 
530  $fieldRenderingDefinitions['class'] = '
531  <!-- Selecting class for link: -->
532  <form action="" name="lclassform" id="lclassform" class="t3js-dummyform form-horizontal">
533  <div class="form-group form-group-sm" id="typo3-linkClass">
534  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('class')) . '</label>
535  <div class="col-xs-8">
536  <input type="text" name="lclass" class="form-control"
537  value="' . htmlspecialchars($this->linkAttributeValues['class']) . '" />
538  </div>
539  </div>
540  </form>';
541 
542  $fieldRenderingDefinitions['params'] = '
543  <!-- Selecting params for link: -->
544  <form action="" name="lparamsform" id="lparamsform" class="t3js-dummyform form-horizontal">
545  <div class="form-group form-group-sm" id="typo3-linkParams">
546  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('params')) . '</label>
547  <div class="col-xs-8">
548  <input type="text" name="lparams" class="form-control"
549  value="' . htmlspecialchars($this->linkAttributeValues['params']) . '" />
550  </div>
551  </div>
552  </form>';
553 
554  return $fieldRenderingDefinitions;
555  }
556 
562  public function ‪getUrlParameters(array $overrides = null)
563  {
564  return [
565  'act' => $overrides['act'] ?? ‪$this->displayedLinkHandlerId,
566  'P' => $overrides['P'] ?? ‪$this->parameters,
567  ];
568  }
569 
575  protected function ‪getBodyTagAttributes()
576  {
577  ‪$parameters = [];
578  ‪$parameters['uid'] = $this->parameters['uid'];
579  ‪$parameters['pid'] = $this->parameters['pid'];
580  ‪$parameters['itemName'] = $this->parameters['itemName'];
581  ‪$parameters['formName'] = $this->parameters['formName'];
582  ‪$parameters['params']['allowedExtensions'] = $this->parameters['params']['allowedExtensions'] ?? '';
583  ‪$parameters['params']['blindLinkOptions'] = $this->parameters['params']['blindLinkOptions'] ?? '';
584  ‪$parameters['params']['blindLinkFields'] = $this->parameters['params']['blindLinkFields'] ?? '';
585  $addPassOnParams = ‪HttpUtility::buildQueryString(['P' => ‪$parameters], '&');
586 
587  $attributes = $this->displayedLinkHandler->getBodyTagAttributes();
588  return array_merge(
589  $attributes,
590  [
591  'data-this-script-url' => strpos($this->thisScript, '?') === false ? $this->thisScript . '?' : $this->thisScript . '&',
592  'data-url-parameters' => json_encode($this->‪getUrlParameters()),
593  'data-parameters' => json_encode($this->parameters),
594  'data-add-on-params' => $addPassOnParams,
595  'data-link-attribute-fields' => json_encode($this->linkAttributeFields)
596  ]
597  );
598  }
599 
605  abstract protected function ‪getCurrentPageId();
606 
610  public function ‪getParameters()
611  {
612  return ‪$this->parameters;
613  }
614 
620  public function ‪getConfiguration()
621  {
622  return [];
623  }
624 
628  protected function ‪getDisplayedLinkHandlerId()
629  {
631  }
632 
636  public function ‪getScriptUrl()
637  {
639  }
640 
644  protected function ‪getLanguageService()
645  {
646  return ‪$GLOBALS['LANG'];
647  }
648 
652  protected function ‪getBackendUser()
653  {
654  return ‪$GLOBALS['BE_USER'];
655  }
656 }
‪TYPO3\CMS\Core\Localization\LanguageService\includeLLFile
‪mixed includeLLFile($fileRef, $setGlobal=true, $mergeLocalOntoDefault=false)
Definition: LanguageService.php:260
‪TYPO3\CMS\Backend\Template\DocumentTemplate
Definition: DocumentTemplate.php:48
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:35
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:160
‪TYPO3\CMS\Recordlist\Controller
Definition: AbstractLinkBrowserController.php:2
‪TYPO3\CMS\Core\Service\DependencyOrderingService
Definition: DependencyOrderingService.php:31
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:45
‪TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait
Definition: PublicMethodDeprecationTrait.php:68
‪TYPO3\CMS\Backend\Utility\BackendUtility
Definition: BackendUtility.php:72
‪TYPO3\CMS\Backend\Utility\BackendUtility\getPagesTSconfig
‪static array getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
Definition: BackendUtility.php:864
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:21
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:29
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:25