TYPO3 CMS  TYPO3_8-7
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 
28 
33 {
37  protected $doc;
38 
42  protected $parameters;
43 
49  protected $thisScript = '';
50 
54  protected $linkHandlers = [];
55 
63  protected $currentLinkParts = [];
64 
71 
78 
85 
93  protected $displayedLinkHandlerId = '';
94 
100  protected $linkAttributeFields = [];
101 
107  protected $linkAttributeValues = [];
108 
112  protected $hookObjects = [];
113 
117  public function __construct()
118  {
119  $this->initHookObjects();
120  $this->init();
121  }
122 
126  protected function init()
127  {
128  $this->getLanguageService()->includeLLFile('EXT:lang/Resources/Private/Language/locallang_browse_links.xlf');
129  }
130 
136  protected function initHookObjects()
137  {
138  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'])
139  && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks'])
140  ) {
141  $hooks = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies(
142  $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['LinkBrowser']['hooks']
143  );
144  foreach ($hooks as $key => $hook) {
145  $this->hookObjects[] = GeneralUtility::makeInstance($hook['handler']);
146  }
147  }
148  }
149 
158  public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
159  {
160  $this->determineScriptUrl($request);
161  $this->initVariables($request);
162  $this->loadLinkHandlers();
163  $this->initCurrentUrl();
164 
165  $menuData = $this->buildMenuArray();
166  $renderLinkAttributeFields = $this->renderLinkAttributeFields();
167  $browserContent = $this->displayedLinkHandler->render($request);
168 
169  $this->initDocumentTemplate();
170  $content = $this->doc->startPage('Link Browser');
171  $content .= $this->doc->getFlashMessages();
172 
173  if (!empty($this->currentLinkParts)) {
174  $content .= $this->renderCurrentUrl();
175  }
176 
177  $options = '';
178  foreach ($menuData as $id => $def) {
179  $class = $def['isActive'] ? ' class="active"' : '';
180 
181  $options .= '<li' . $class . '>'
182  . '<a href="' . htmlspecialchars($def['url']) . '" ' . $def['addParams'] . '>' . htmlspecialchars($def['label']) . '</a>'
183  . '</li>';
184  }
185  $content .= '<div class="element-browser-panel element-browser-tabs"><ul class="nav nav-tabs" role="tablist">' .
186  $options . '</ul></div>';
187 
188  $content .= $renderLinkAttributeFields;
189 
190  $content .= $browserContent;
191  $content .= $this->doc->endPage();
192 
193  $response->getBody()->write($this->doc->insertStylesAndJS($content));
194  return $response;
195  }
196 
205  protected function determineScriptUrl(ServerRequestInterface $request)
206  {
207  if ($routePath = $request->getQueryParams()['route']) {
208  $router = GeneralUtility::makeInstance(Router::class);
209  $route = $router->match($routePath);
210  $uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
211  $this->thisScript = (string)$uriBuilder->buildUriFromRoute($route->getOption('_identifier'));
212  } elseif ($moduleName = $request->getQueryParams()['M']) {
213  $this->thisScript = BackendUtility::getModuleUrl($moduleName);
214  } else {
215  $this->thisScript = GeneralUtility::getIndpEnv('SCRIPT_NAME');
216  }
217  }
218 
222  protected function initVariables(ServerRequestInterface $request)
223  {
224  $queryParams = $request->getQueryParams();
225  $this->displayedLinkHandlerId = isset($queryParams['act']) ? $queryParams['act'] : '';
226  $this->parameters = isset($queryParams['P']) ? $queryParams['P'] : [];
227  $this->linkAttributeValues = isset($queryParams['linkAttributes']) ? $queryParams['linkAttributes'] : [];
228  }
229 
233  protected function loadLinkHandlers()
234  {
235  $linkHandlers = $this->getLinkHandlers();
236  if (empty($linkHandlers)) {
237  throw new \UnexpectedValueException('No link handlers are configured. Check page TSconfig TCEMAIN.linkHandler.', 1442787911);
238  }
239 
240  $lang = $this->getLanguageService();
241  foreach ($linkHandlers as $identifier => $configuration) {
242  $identifier = rtrim($identifier, '.');
243 
244  if (empty($configuration['handler'])) {
245  throw new \UnexpectedValueException(sprintf('Missing handler for link handler "%1$s", check page TSconfig TCEMAIN.linkHandler.%1$s.handler', $identifier), 1494579849);
246  }
247 
249  $handler = GeneralUtility::makeInstance($configuration['handler']);
250  $handler->initialize(
251  $this,
252  $identifier,
253  isset($configuration['configuration.']) ? $configuration['configuration.'] : []
254  );
255 
256  $label = !empty($configuration['label']) ? $lang->sL($configuration['label']) : '';
257  $label = $label ?: $lang->sL('LLL:EXT:recordlist/Resources/Private/Language/locallang.xlf:error.linkHandlerTitleMissing');
258  $this->linkHandlers[$identifier] = [
259  'handlerInstance' => $handler,
260  'label' => htmlspecialchars($label),
261  'displayBefore' => isset($configuration['displayBefore']) ? GeneralUtility::trimExplode(',', $configuration['displayBefore']) : [],
262  'displayAfter' => isset($configuration['displayAfter']) ? GeneralUtility::trimExplode(',', $configuration['displayAfter']) : [],
263  'scanBefore' => isset($configuration['scanBefore']) ? GeneralUtility::trimExplode(',', $configuration['scanBefore']) : [],
264  'scanAfter' => isset($configuration['scanAfter']) ? GeneralUtility::trimExplode(',', $configuration['scanAfter']) : [],
265  'addParams' => isset($configuration['addParams']) ? $configuration['addParams'] : '',
266  ];
267  }
268  }
269 
275  protected function getLinkHandlers()
276  {
277  $pageTSconfig = BackendUtility::getPagesTSconfig($this->getCurrentPageId());
278  $pageTSconfig = $this->getBackendUser()->getTSConfig('TCEMAIN.linkHandler.', $pageTSconfig);
279  $linkHandlers = (array)$pageTSconfig['properties'];
280 
281  foreach ($this->hookObjects as $hookObject) {
282  if (method_exists($hookObject, 'modifyLinkHandlers')) {
283  $linkHandlers = $hookObject->modifyLinkHandlers($linkHandlers, $this->currentLinkParts);
284  }
285  }
286 
287  return $linkHandlers;
288  }
289 
293  protected function initCurrentUrl()
294  {
295  if (empty($this->currentLinkParts)) {
296  return;
297  }
298 
299  $orderedHandlers = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($this->linkHandlers, 'scanBefore', 'scanAfter');
300 
301  // find responsible handler for current link
302  foreach ($orderedHandlers as $key => $configuration) {
304  $handler = $configuration['handlerInstance'];
305  if ($handler->canHandleLink($this->currentLinkParts)) {
306  $this->currentLinkHandler = $handler;
307  $this->currentLinkHandlerId = $key;
308  break;
309  }
310  }
311  // reset the link if we have no handler for it
312  if (!$this->currentLinkHandler) {
313  $this->currentLinkParts = [];
314  }
315 
316  // overwrite any preexisting
317  foreach ($this->currentLinkParts as $key => $part) {
318  if ($key !== 'url') {
319  $this->linkAttributeValues[$key] = $part;
320  }
321  }
322  }
323 
327  protected function initDocumentTemplate()
328  {
329  $this->doc = GeneralUtility::makeInstance(DocumentTemplate::class);
330  $this->doc->divClass = 'element-browser';
331 
332  foreach ($this->getBodyTagAttributes() as $attributeName => $value) {
333  $this->doc->bodyTagAdditions .= ' ' . $attributeName . '="' . htmlspecialchars($value) . '"';
334  }
335 
336  // Finally, add the accumulated JavaScript to the template object:
337  // also unset the default jumpToUrl() function before
338  unset($this->doc->JScodeArray['jumpToUrl']);
339  }
340 
346  protected function renderCurrentUrl()
347  {
348  return '<!-- Print current URL -->
349  <div class="element-browser-panel element-browser-title">' .
350  htmlspecialchars($this->getLanguageService()->getLL('currentLink')) .
351  ': ' .
352  htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) .
353  '</div>';
354  }
355 
361  protected function buildMenuArray()
362  {
363  $allowedItems = $this->getAllowedItems();
364  if ($this->displayedLinkHandlerId && !in_array($this->displayedLinkHandlerId, $allowedItems, true)) {
365  $this->displayedLinkHandlerId = '';
366  }
367 
368  $allowedHandlers = array_flip($allowedItems);
369  $menuDef = [];
370  foreach ($this->linkHandlers as $identifier => $configuration) {
371  if (!isset($allowedHandlers[$identifier])) {
372  continue;
373  }
374 
376  $handlerInstance = $configuration['handlerInstance'];
377  $isActive = $this->displayedLinkHandlerId === $identifier || !$this->displayedLinkHandlerId && $handlerInstance === $this->currentLinkHandler;
378  if ($isActive) {
379  $this->displayedLinkHandler = $handlerInstance;
380  if (!$this->displayedLinkHandlerId) {
381  $this->displayedLinkHandlerId = $this->currentLinkHandlerId;
382  }
383  }
384 
385  if ($configuration['addParams']) {
386  $addParams = $configuration['addParams'];
387  } else {
388  $parameters = GeneralUtility::implodeArrayForUrl('', $this->getUrlParameters(['act' => $identifier]));
389  $addParams = 'onclick="jumpToUrl(' . htmlspecialchars(GeneralUtility::quoteJSvalue('?' . ltrim($parameters, '&'))) . ');return false;"';
390  }
391  $menuDef[$identifier] = [
392  'isActive' => $isActive,
393  'label' => $configuration['label'],
394  'url' => '#',
395  'addParams' => $addParams,
396  'before' => $configuration['displayBefore'],
397  'after' => $configuration['displayAfter']
398  ];
399  }
400 
401  $menuDef = GeneralUtility::makeInstance(DependencyOrderingService::class)->orderByDependencies($menuDef);
402 
403  // if there is no active tab
404  if (!$this->displayedLinkHandler) {
405  // empty the current link
406  $this->currentLinkParts = [];
407  $this->currentLinkHandler = null;
408  $this->currentLinkHandler = '';
409  // select first tab
410  reset($menuDef);
411  $this->displayedLinkHandlerId = key($menuDef);
412  $this->displayedLinkHandler = $this->linkHandlers[$this->displayedLinkHandlerId]['handlerInstance'];
413  $menuDef[$this->displayedLinkHandlerId]['isActive'] = true;
414  }
415 
416  return $menuDef;
417  }
418 
424  protected function getAllowedItems()
425  {
426  $allowedItems = array_keys($this->linkHandlers);
427 
428  foreach ($this->hookObjects as $hookObject) {
429  if (method_exists($hookObject, 'modifyAllowedItems')) {
430  $allowedItems = $hookObject->modifyAllowedItems($allowedItems, $this->currentLinkParts);
431  }
432  }
433 
434  // Initializing the action value, possibly removing blinded values etc:
435  $blindLinkOptions = isset($this->parameters['params']['blindLinkOptions'])
436  ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkOptions'])
437  : [];
438  $allowedItems = array_diff($allowedItems, $blindLinkOptions);
439 
440  return $allowedItems;
441  }
442 
448  protected function getAllowedLinkAttributes()
449  {
450  $allowedLinkAttributes = $this->displayedLinkHandler->getLinkAttributes();
451 
452  // Removing link fields if configured
453  $blindLinkFields = isset($this->parameters['params']['blindLinkFields'])
454  ? GeneralUtility::trimExplode(',', $this->parameters['params']['blindLinkFields'], true)
455  : [];
456  $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
457 
458  return $allowedLinkAttributes;
459  }
460 
466  public function renderLinkAttributeFields()
467  {
468  $fieldRenderingDefinitions = $this->getLinkAttributeFieldDefinitions();
469 
470  $fieldRenderingDefinitions = $this->displayedLinkHandler->modifyLinkAttributes($fieldRenderingDefinitions);
471 
472  $this->linkAttributeFields = $this->getAllowedLinkAttributes();
473 
474  $content = '';
475  foreach ($this->linkAttributeFields as $attribute) {
476  $content .= $fieldRenderingDefinitions[$attribute];
477  }
478 
479  // add update button if appropriate
480  if (!empty($this->currentLinkParts) && $this->displayedLinkHandler === $this->currentLinkHandler && $this->currentLinkHandler->isUpdateSupported()) {
481  $content .= '
482  <form action="" name="lparamsform" id="lparamsform" class="form-horizontal">
483  <div class="form-group form-group-sm">
484  <div class="col-xs-12">
485  <input class="btn btn-default t3js-linkCurrent" type="submit" value="' . htmlspecialchars($this->getLanguageService()->getLL('update')) . '" />
486  </div>
487  </div>
488  </form>';
489  }
490 
491  return '<div class="element-browser-panel element-browser-attributes">' . $content . '</div>';
492  }
493 
499  protected function getLinkAttributeFieldDefinitions()
500  {
501  $lang = $this->getLanguageService();
502 
503  $fieldRenderingDefinitions = [];
504  $fieldRenderingDefinitions['target'] = '
505  <!-- Selecting target for link: -->
506  <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform form-horizontal">
507  <div class="form-group form-group-sm" id="typo3-linkTarget">
508  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
509  <div class="col-xs-3">
510  <input type="text" name="ltarget" class="t3js-linkTarget form-control"
511  value="' . htmlspecialchars($this->linkAttributeValues['target']) . '" />
512  </div>
513  <div class="col-xs-5">
514  <select name="ltarget_type" class="t3js-targetPreselect form-control">
515  <option value=""></option>
516  <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
517  <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
518  </select>
519  </div>
520  </div>
521  </form>';
522 
523  $fieldRenderingDefinitions['title'] = '
524  <!-- Selecting title for link: -->
525  <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform form-horizontal">
526  <div class="form-group form-group-sm" id="typo3-linkTitle">
527  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('title')) . '</label>
528  <div class="col-xs-8">
529  <input type="text" name="ltitle" class="form-control"
530  value="' . htmlspecialchars($this->linkAttributeValues['title']) . '" />
531  </div>
532  </div>
533  </form>';
534 
535  $fieldRenderingDefinitions['class'] = '
536  <!-- Selecting class for link: -->
537  <form action="" name="lclassform" id="lclassform" class="t3js-dummyform form-horizontal">
538  <div class="form-group form-group-sm" id="typo3-linkClass">
539  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('class')) . '</label>
540  <div class="col-xs-8">
541  <input type="text" name="lclass" class="form-control"
542  value="' . htmlspecialchars($this->linkAttributeValues['class']) . '" />
543  </div>
544  </div>
545  </form>';
546 
547  $fieldRenderingDefinitions['params'] = '
548  <!-- Selecting params for link: -->
549  <form action="" name="lparamsform" id="lparamsform" class="t3js-dummyform form-horizontal">
550  <div class="form-group form-group-sm" id="typo3-linkParams">
551  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('params')) . '</label>
552  <div class="col-xs-8">
553  <input type="text" name="lparams" class="form-control"
554  value="' . htmlspecialchars($this->linkAttributeValues['params']) . '" />
555  </div>
556  </div>
557  </form>';
558 
559  return $fieldRenderingDefinitions;
560  }
561 
567  public function getUrlParameters(array $overrides = null)
568  {
569  return [
570  'act' => isset($overrides['act']) ? $overrides['act'] : $this->displayedLinkHandlerId,
571  'P' => isset($overrides['P']) ? $overrides['P'] : $this->parameters,
572  ];
573  }
574 
580  protected function getBodyTagAttributes()
581  {
582  $parameters = [];
583  $parameters['uid'] = $this->parameters['uid'];
584  $parameters['pid'] = $this->parameters['pid'];
585  $parameters['itemName'] = $this->parameters['itemName'];
586  $parameters['formName'] = $this->parameters['formName'];
587  $parameters['params']['allowedExtensions'] = isset($this->parameters['params']['allowedExtensions']) ? $this->parameters['params']['allowedExtensions'] : '';
588  $parameters['params']['blindLinkOptions'] = isset($this->parameters['params']['blindLinkOptions']) ? $this->parameters['params']['blindLinkOptions'] : '';
589  $parameters['params']['blindLinkFields'] = isset($this->parameters['params']['blindLinkFields']) ? $this->parameters['params']['blindLinkFields']: '';
590  $addPassOnParams = GeneralUtility::implodeArrayForUrl('P', $parameters);
591 
592  $attributes = $this->displayedLinkHandler->getBodyTagAttributes();
593  return array_merge(
594  $attributes,
595  [
596  'data-this-script-url' => strpos($this->thisScript, '?') === false ? $this->thisScript . '?' : $this->thisScript . '&',
597  'data-url-parameters' => json_encode($this->getUrlParameters()),
598  'data-parameters' => json_encode($this->parameters),
599  'data-add-on-params' => $addPassOnParams,
600  'data-link-attribute-fields' => json_encode($this->linkAttributeFields)
601  ]
602  );
603  }
604 
610  abstract protected function getCurrentPageId();
611 
615  public function getParameters()
616  {
617  return $this->parameters;
618  }
619 
625  public function getConfiguration()
626  {
627  return [];
628  }
629 
633  public function getDisplayedLinkHandlerId()
634  {
636  }
637 
641  public function getScriptUrl()
642  {
643  return $this->thisScript;
644  }
645 
649  protected function getLanguageService()
650  {
651  return $GLOBALS['LANG'];
652  }
653 
657  protected function getBackendUser()
658  {
659  return $GLOBALS['BE_USER'];
660  }
661 }
static getPagesTSconfig($id, $rootLine=null, $returnPartArray=false)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
static makeInstance($className,... $constructorArguments)
static implodeArrayForUrl($name, array $theArray, $str='', $skipBlank=false, $rawurlencodeParamName=false)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']