‪TYPO3CMS  11.5
AbstractLinkBrowserController.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
17 
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
29 use TYPO3\CMS\Core\Page\PageRenderer;
34 
40 {
44  protected ‪$moduleTemplate;
45 
49  protected ‪$parameters;
50 
56  protected ‪$thisScript = '';
57 
61  protected ‪$linkHandlers = [];
62 
70  protected ‪$currentLinkParts = [];
71 
78 
84  protected ‪$currentLinkHandlerId;
85 
92 
100  protected ‪$displayedLinkHandlerId = '';
101 
107  protected ‪$linkAttributeFields = [];
108 
114  protected ‪$linkAttributeValues = [];
115 
119  protected ‪$hookObjects = [];
120 
122  protected PageRenderer ‪$pageRenderer;
123  protected ‪UriBuilder ‪$uriBuilder;
126 
127  public function ‪__construct(
129  PageRenderer ‪$pageRenderer,
133  ) {
134  $this->dependencyOrderingService = ‪$dependencyOrderingService;
135  $this->pageRenderer = ‪$pageRenderer;
136  $this->uriBuilder = ‪$uriBuilder;
137  $this->linkService = ‪$linkService;
138  $this->moduleTemplateFactory = ‪$moduleTemplateFactory;
139  $this->‪initHookObjects();
140  $this->‪init();
141  }
142 
146  protected function ‪init()
147  {
148  $this->‪getLanguageService()->‪includeLLFile('EXT:recordlist/Resources/Private/Language/locallang_browse_links.xlf');
149  }
150 
156  protected function ‪initHookObjects()
157  {
158  $hooks = $this->dependencyOrderingService->orderByDependencies(
159  ‪$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'] ?? []
160  );
161  foreach ($hooks as $key => $hook) {
162  $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
163  }
164  }
165 
173  public function ‪mainAction(ServerRequestInterface $request): ResponseInterface
174  {
175  $this->moduleTemplate = $this->moduleTemplateFactory->create($request);
176  $this->moduleTemplate->getDocHeaderComponent()->disable();
177  $view = $this->moduleTemplate->getView();
178  $view->setTemplate('LinkBrowser');
179  $view->getRequest()->setControllerExtensionName('recordlist');
180  $view->setTemplateRootPaths(['EXT:recordlist/Resources/Private/Templates/LinkBrowser/']);
181  $view->setPartialRootPaths(['EXT:recordlist/Resources/Private/Partials/LinkBrowser/']);
182  $view->setLayoutRootPaths(['EXT:backend/Resources/Private/Layouts/', 'EXT:recordlist/Resources/Private/Layouts/']);
183  $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_misc.xlf');
184  $this->pageRenderer->addInlineLanguageLabelFile('EXT:core/Resources/Private/Language/locallang_core.xlf');
185 
186  $this->‪determineScriptUrl($request);
187  $this->‪initVariables($request);
188  $this->‪loadLinkHandlers();
189  $this->‪initCurrentUrl();
190 
191  $menuData = $this->‪buildMenuArray();
192  $renderLinkAttributeFields = $this->‪renderLinkAttributeFields();
193  if (method_exists($this->displayedLinkHandler, 'setView')) {
194  $this->displayedLinkHandler->setView($view);
195  }
196  $browserContent = $this->displayedLinkHandler->render($request);
197 
198  $this->‪initDocumentTemplate();
199  $this->moduleTemplate->setTitle('Link Browser');
200 
201  if (!empty($this->currentLinkParts)) {
202  $this->‪renderCurrentUrl();
203  }
204 
205  $view->assign('menuItems', $menuData);
206  $view->assign('linkAttributes', $renderLinkAttributeFields);
207  $view->assign('contentOnly', $request->getQueryParams()['contentOnly'] ?? false);
208 
209  if ($request->getQueryParams()['contentOnly'] ?? false) {
210  return new HtmlResponse($view->render());
211  }
212  if ($browserContent) {
213  $view->assign('content', $browserContent);
214  }
215  return new ‪HtmlResponse($this->moduleTemplate->renderContent());
216  }
217 
226  protected function ‪determineScriptUrl(ServerRequestInterface $request)
227  {
228  if ($route = $request->getAttribute('route')) {
229  $this->thisScript = (string)$this->uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
230  } else {
232  $normalizedParams = $request->getAttribute('normalizedParams');
233  $this->thisScript = $normalizedParams->getScriptName();
234  }
235  }
236 
240  protected function ‪initVariables(ServerRequestInterface $request)
241  {
242  $queryParams = $request->getQueryParams();
243  $this->displayedLinkHandlerId = $queryParams['act'] ?? '';
244  $this->parameters = $queryParams['P'] ?? [];
245  $this->linkAttributeValues = $queryParams['linkAttributes'] ?? [];
246  }
247 
251  protected function ‪loadLinkHandlers()
252  {
253  ‪$linkHandlers = $this->getLinkHandlers();
254  if (empty(‪$linkHandlers)) {
255  throw new \UnexpectedValueException('No link handlers are configured. Check page TSconfig TCEMAIN.linkHandler.', 1442787911);
256  }
257 
258  $lang = $this->‪getLanguageService();
259  foreach ($linkHandlers as $identifier => $configuration) {
260  $identifier = rtrim($identifier, '.');
261 
262  if (empty($configuration['handler'])) {
263  throw new \UnexpectedValueException(sprintf('Missing handler for link handler "%1$s", check page TSconfig TCEMAIN.linkHandler.%1$s.handler', $identifier), 1494579849);
264  }
265 
267  $handler = GeneralUtility::makeInstance($configuration['handler']);
268  $handler->initialize(
269  $this,
270  $identifier,
271  $configuration['configuration.'] ?? []
272  );
273 
274  $label = !empty($configuration['label']) ? $lang->sL($configuration['label']) : '';
275  $label = $label ?: $lang->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang.xlf:error.linkHandlerTitleMissing');
276  $this->linkHandlers[$identifier] = [
277  'handlerInstance' => $handler,
278  'label' => htmlspecialchars($label),
279  'displayBefore' => isset($configuration['displayBefore']) ? ‪GeneralUtility::trimExplode(',', $configuration['displayBefore']) : [],
280  'displayAfter' => isset($configuration['displayAfter']) ? GeneralUtility::trimExplode(',', $configuration['displayAfter']) : [],
281  'scanBefore' => isset($configuration['scanBefore']) ? GeneralUtility::trimExplode(',', $configuration['scanBefore']) : [],
282  'scanAfter' => isset($configuration['scanAfter']) ? GeneralUtility::trimExplode(',', $configuration['scanAfter']) : [],
283  'addParams' => $configuration['addParams'] ?? '',
284  ];
285  }
286  }
287 
293  protected function getLinkHandlers()
294  {
295  ‪$linkHandlers = (array)(BackendUtility::getPagesTSconfig($this->‪getCurrentPageId())['TCEMAIN.']['linkHandler.'] ?? []);
296  foreach ($this->hookObjects as $hookObject) {
297  if (method_exists($hookObject, 'modifyLinkHandlers')) {
298  ‪$linkHandlers = $hookObject->modifyLinkHandlers(‪$linkHandlers, $this->currentLinkParts);
299  }
300  }
301 
302  return ‪$linkHandlers;
303  }
304 
308  protected function ‪initCurrentUrl()
309  {
310  if (empty($this->currentLinkParts)) {
311  return;
312  }
313 
314  $orderedHandlers = $this->dependencyOrderingService->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
315 
316  // find responsible handler for current link
317  foreach ($orderedHandlers as $key => $configuration) {
319  $handler = $configuration['handlerInstance'];
320  if ($handler->canHandleLink($this->currentLinkParts)) {
321  $this->currentLinkHandler = $handler;
322  $this->currentLinkHandlerId = $key;
323  break;
324  }
325  }
326  // reset the link if we have no handler for it
327  if (!$this->currentLinkHandler) {
328  $this->currentLinkParts = [];
329  }
330 
331  // overwrite any preexisting
332  foreach ($this->currentLinkParts as $key => $part) {
333  if ($key !== 'url') {
334  $this->linkAttributeValues[$key] = $part;
335  }
336  }
337  }
338 
342  protected function ‪initDocumentTemplate()
343  {
344  $bodyTag = $this->moduleTemplate->getBodyTag();
345  $bodyTag = str_replace('>', ' ' . GeneralUtility::implodeAttributes($this->‪getBodyTagAttributes(), true, true) . '>', $bodyTag);
346  $this->moduleTemplate->setBodyTag($bodyTag);
347  }
348 
352  protected function ‪renderCurrentUrl()
353  {
354  $this->moduleTemplate->getView()->assign('currentUrl', $this->currentLinkHandler->formatCurrentUrl());
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 = $this->dependencyOrderingService->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  // select first tab
404  $this->displayedLinkHandlerId = (string)array_key_first($menuDef);
405  $this->displayedLinkHandler = $this->linkHandlers[‪$this->displayedLinkHandlerId]['handlerInstance'];
406  $menuDef[‪$this->displayedLinkHandlerId]['isActive'] = true;
407  }
408 
409  return $menuDef;
410  }
411 
417  protected function ‪getAllowedItems()
418  {
419  $allowedItems = array_keys($this->linkHandlers);
420 
421  foreach ($this->hookObjects as $hookObject) {
422  if (method_exists($hookObject, 'modifyAllowedItems')) {
423  $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
424  }
425  }
426 
427  // Initializing the action value, possibly removing blinded values etc:
428  $blindLinkOptions = isset($this->parameters['params']['blindLinkOptions'])
429  ? ‪GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkOptions'])
430  : [];
431  $allowedItems = array_diff($allowedItems, $blindLinkOptions);
432 
433  return $allowedItems;
434  }
435 
441  protected function ‪getAllowedLinkAttributes()
442  {
443  $allowedLinkAttributes = $this->displayedLinkHandler->getLinkAttributes();
444 
445  // Removing link fields if configured
446  $blindLinkFields = isset($this->parameters['params']['blindLinkFields'])
447  ? ‪GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkFields'], true)
448  : [];
449  $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
450 
451  return $allowedLinkAttributes;
452  }
453 
459  protected function ‪renderLinkAttributeFields()
460  {
461  $fieldRenderingDefinitions = $this->‪getLinkAttributeFieldDefinitions();
462 
463  $fieldRenderingDefinitions = $this->displayedLinkHandler->modifyLinkAttributes($fieldRenderingDefinitions);
464 
465  $this->linkAttributeFields = $this->‪getAllowedLinkAttributes();
466 
467  $content = '';
468  foreach ($this->linkAttributeFields as $attribute) {
469  $content .= $fieldRenderingDefinitions[$attribute] ?? '';
470  }
471 
472  // add update button if appropriate
473  if (!empty($this->currentLinkParts) && $this->displayedLinkHandler === $this->currentLinkHandler && $this->currentLinkHandler->isUpdateSupported()) {
474  $this->moduleTemplate->getView()->assign('showUpdateParametersButton', true);
475  }
476  return $content;
477  }
478 
484  protected function ‪getLinkAttributeFieldDefinitions()
485  {
486  $lang = $this->‪getLanguageService();
487 
488  $fieldRenderingDefinitions = [];
489  $fieldRenderingDefinitions['target'] = '
490  <!-- Selecting target for link: -->
491  <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform">
492  <div class="row mb-3" id="typo3-linkTarget">
493  <label class="col-sm-3 col-form-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
494  <div class="col-sm-4">
495  <input type="text" name="ltarget" class="t3js-linkTarget form-control"
496  value="' . htmlspecialchars($this->linkAttributeValues['target'] ?? '') . '" />
497  </div>
498  <div class="col-sm-5">
499  <select name="ltarget_type" class="t3js-targetPreselect form-select">
500  <option value=""></option>
501  <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
502  <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
503  </select>
504  </div>
505  </div>
506  </form>';
507 
508  $fieldRenderingDefinitions['title'] = '
509  <!-- Selecting title for link: -->
510  <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform">
511  <div class="row mb-3" id="typo3-linkTitle">
512  <label class="col-sm-3 col-form-label">' . htmlspecialchars($lang->getLL('title')) . '</label>
513  <div class="col-sm-9">
514  <input type="text" name="ltitle" class="form-control"
515  value="' . htmlspecialchars($this->linkAttributeValues['title'] ?? '') . '" />
516  </div>
517  </div>
518  </form>';
519 
520  $fieldRenderingDefinitions['class'] = '
521  <!-- Selecting class for link: -->
522  <form action="" name="lclassform" id="lclassform" class="t3js-dummyform">
523  <div class="row mb-3" id="typo3-linkClass">
524  <label class="col-sm-3 col-form-label">' . htmlspecialchars($lang->getLL('class')) . '</label>
525  <div class="col-sm-9">
526  <input type="text" name="lclass" class="form-control"
527  value="' . htmlspecialchars($this->linkAttributeValues['class'] ?? '') . '" />
528  </div>
529  </div>
530  </form>';
531 
532  $fieldRenderingDefinitions['params'] = '
533  <!-- Selecting params for link: -->
534  <form action="" name="lparamsform" id="lparamsform" class="t3js-dummyform">
535  <div class="row" id="typo3-linkParams">
536  <label class="col-sm-3 col-form-label">' . htmlspecialchars($lang->getLL('params')) . '</label>
537  <div class="col-sm-9">
538  <input type="text" name="lparams" class="form-control"
539  value="' . htmlspecialchars($this->linkAttributeValues['params'] ?? '') . '" />
540  </div>
541  </div>
542  </form>';
543 
544  return $fieldRenderingDefinitions;
545  }
546 
551  public function ‪getUrlParameters(?array $overrides = null)
552  {
553  return [
554  'act' => $overrides['act'] ?? ‪$this->displayedLinkHandlerId,
555  'P' => $overrides['P'] ?? ‪$this->parameters,
556  ];
557  }
558 
564  protected function ‪getBodyTagAttributes()
565  {
566  $attributes = $this->displayedLinkHandler->getBodyTagAttributes();
567  return array_merge(
568  $attributes,
569  [
570  'data-url-parameters' => json_encode($this->‪getUrlParameters()) ?: '',
571  'data-parameters' => json_encode($this->parameters) ?: '',
572  'data-link-attribute-fields' => json_encode($this->linkAttributeFields) ?: '',
573  ]
574  );
575  }
576 
582  abstract protected function ‪getCurrentPageId();
583 
587  public function ‪getParameters()
588  {
589  return ‪$this->parameters;
590  }
591 
597  public function ‪getConfiguration()
598  {
599  return [];
600  }
601 
605  protected function ‪getDisplayedLinkHandlerId()
606  {
608  }
609 
613  public function ‪getScriptUrl()
614  {
615  return ‪$this->thisScript;
616  }
617 
621  protected function ‪getLanguageService()
622  {
623  return ‪$GLOBALS['LANG'];
624  }
625 
629  protected function ‪getBackendUser()
630  {
631  return ‪$GLOBALS['BE_USER'];
632  }
633 }
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static list< string > trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:999
‪TYPO3\CMS\Backend\Template\ModuleTemplateFactory
Definition: ModuleTemplateFactory.php:29
‪TYPO3\CMS\Backend\Template\ModuleTemplate
Definition: ModuleTemplate.php:46
‪TYPO3\CMS\Backend\Routing\UriBuilder
Definition: UriBuilder.php:40
‪TYPO3\CMS\Core\Utility\HttpUtility\buildQueryString
‪static string buildQueryString(array $parameters, string $prependCharacter='', bool $skipEmptyParameters=false)
Definition: HttpUtility.php:171
‪TYPO3\CMS\Recordlist\Controller
Definition: AbstractLinkBrowserController.php:16
‪TYPO3\CMS\Core\Service\DependencyOrderingService
Definition: DependencyOrderingService.php:32
‪TYPO3\CMS\Core\Authentication\BackendUserAuthentication
Definition: BackendUserAuthentication.php:62
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Utility\HttpUtility
Definition: HttpUtility.php:22
‪TYPO3\CMS\Core\Localization\LanguageService\includeLLFile
‪array includeLLFile($fileRef)
Definition: LanguageService.php:271
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Core\Http\NormalizedParams
Definition: NormalizedParams.php:35
‪TYPO3\CMS\Core\Http\HtmlResponse
Definition: HtmlResponse.php:26