‪TYPO3CMS  9.5
BrowseLinksController.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
18 use Psr\Http\Message\ServerRequestInterface;
26 
32 {
34 
38  protected ‪$deprecatedPublicMethods = [
39  'renderLinkAttributeFields' => 'Using BrowseLinksController::renderLinkAttributeFields() is deprecated and will not be possible anymore in TYPO3 v10.0.',
40  'getPageConfigLabel' => 'Using BrowseLinksController::getPageConfigLabel() is deprecated and will not be possible anymore in TYPO3 v10.0.',
41  'getDisplayedLinkHandlerId' => 'Using BrowseLinksController::getDisplayedLinkHandlerId() is deprecated and will not be possible anymore in TYPO3 v10.0.',
42  ];
43 
47  protected ‪$editorId;
48 
54  protected ‪$contentsLanguage;
55 
62 
66  protected ‪$buttonConfig = [];
67 
71  protected ‪$thisConfig = [];
72 
76  protected ‪$classesAnchorDefault = [];
77 
81  protected ‪$classesAnchorDefaultTitle = [];
82 
86  protected ‪$classesAnchorClassTitle = [];
87 
91  protected ‪$classesAnchorDefaultTarget = [];
92 
97 
101  protected ‪$defaultLinkTarget = '';
102 
106  protected ‪$additionalAttributes = [];
107 
111  protected ‪$siteUrl = '';
112 
116  protected function ‪init()
117  {
118  parent::init();
119 
120  $this->contentLanguageService = GeneralUtility::makeInstance(LanguageService::class);
121  }
122 
126  protected function ‪initVariables(ServerRequestInterface $request)
127  {
128  parent::initVariables($request);
129 
130  $queryParameters = $request->getQueryParams();
131 
132  $this->siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
133 
134  $this->currentLinkParts = $queryParameters['P']['curUrl'] ?? [];
135  $this->editorId = $queryParameters['editorId'];
136  $this->contentsLanguage = $queryParameters['contentsLanguage'];
137  $this->RTEtsConfigParams = $queryParameters['RTEtsConfigParams'] ?? null;
138 
139  $this->contentLanguageService->init($this->contentsLanguage);
140 
141  $tcaFieldConf = ['enableRichtext' => true];
142  if (!empty($queryParameters['P']['richtextConfigurationName'])) {
143  $tcaFieldConf['richtextConfiguration'] = $queryParameters['P']['richtextConfigurationName'];
144  }
145 
147  $richtextConfigurationProvider = GeneralUtility::makeInstance(Richtext::class);
148  $this->thisConfig = $richtextConfigurationProvider->getConfiguration(
149  $this->parameters['table'],
150  $this->parameters['fieldName'],
151  (int)$this->parameters['pid'],
152  $this->parameters['recordType'],
153  $tcaFieldConf
154  );
155  $this->buttonConfig = $this->thisConfig['buttons']['link'] ?? [];
156  }
157 
161  protected function ‪initDocumentTemplate()
162  {
163  parent::initDocumentTemplate();
164 
165  $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
166  $pageRenderer->loadRequireJsModule(
167  'TYPO3/CMS/RteCkeditor/RteLinkBrowser',
168  'function(RteLinkBrowser) {
169  RteLinkBrowser.initialize(' . GeneralUtility::quoteJSvalue($this->editorId) . ');
170  }'
171  );
172  }
173 
177  protected function ‪initCurrentUrl()
178  {
179  if (empty($this->currentLinkParts)) {
180  return;
181  }
182 
183  if (!empty($this->currentLinkParts['url'])) {
184  $linkService = GeneralUtility::makeInstance(LinkService::class);
185  $data = $linkService->resolve($this->currentLinkParts['url']);
186  $this->currentLinkParts['type'] = $data['type'];
187  unset($data['type']);
188  $this->currentLinkParts['url'] = $data;
189  if (!empty($this->currentLinkParts['url']['parameters'])) {
190  $this->currentLinkParts['params'] = '&' . $this->currentLinkParts['url']['parameters'];
191  }
192  }
193 
194  parent::initCurrentUrl();
195  }
196 
202  protected function ‪renderLinkAttributeFields()
203  {
204  // Processing the classes configuration
205  if (!empty($this->buttonConfig['properties']['class']['allowedClasses'])) {
206  $classesAnchorArray = is_array($this->buttonConfig['properties']['class']['allowedClasses']) ? $this->buttonConfig['properties']['class']['allowedClasses'] : GeneralUtility::trimExplode(',', $this->buttonConfig['properties']['class']['allowedClasses'], true);
207  // Collecting allowed classes and configured default values
208  $classesAnchor = [
209  'all' => []
210  ];
211 
212  if (is_array($this->thisConfig['classesAnchor'])) {
213  $readOnlyTitle = $this->‪isReadonlyTitle();
214  foreach ($this->thisConfig['classesAnchor'] as $label => $conf) {
215  if (in_array($conf['class'], $classesAnchorArray, true)) {
216  $classesAnchor['all'][] = $conf['class'];
217  if ($conf['type'] === $this->displayedLinkHandlerId) {
218  $classesAnchor[$conf['type']][] = $conf['class'];
219  if ($this->buttonConfig[$conf['type']]['properties']['class']['default'] == $conf['class']) {
220  $this->classesAnchorDefault[$conf['type']] = $conf['class'];
221  if ($conf['titleText']) {
222  $this->classesAnchorDefaultTitle[$conf['type']] = $this->contentLanguageService->sL(trim($conf['titleText']));
223  }
224  if (isset($conf['target'])) {
225  $this->classesAnchorDefaultTarget[$conf['type']] = trim($conf['target']);
226  }
227  }
228  }
229  if ($readOnlyTitle && $conf['titleText']) {
230  $this->classesAnchorClassTitle[$conf['class']] = ($this->classesAnchorDefaultTitle[$conf['type']] = $this->contentLanguageService->sL(trim($conf['titleText'])));
231  }
232  }
233  }
234  }
235  if (isset($this->linkAttributeValues['class'])
236  && isset($classesAnchor[$this->displayedLinkHandlerId])
237  && !in_array($this->linkAttributeValues['class'], $classesAnchor[$this->displayedLinkHandlerId], true)
238  ) {
239  unset($this->linkAttributeValues['class']);
240  }
241  // Constructing the class selector options
242  foreach ($classesAnchorArray as $class) {
243  if (!in_array($class, $classesAnchor['all']) || in_array($class, $classesAnchor['all']) && is_array($classesAnchor[$this->displayedLinkHandlerId]) && in_array($class, $classesAnchor[$this->displayedLinkHandlerId])) {
244  $selected = '';
245  if ($this->linkAttributeValues['class'] === $class || !$this->linkAttributeValues['class'] && $this->classesAnchorDefault[$this->displayedLinkHandlerId] == $class) {
246  $selected = 'selected="selected"';
247  }
248  $classLabel = !empty($this->thisConfig['classes'][$class]['name'])
249  ? $this->‪getPageConfigLabel($this->thisConfig['classes'][$class]['name'], 0)
250  : $class;
251  $classStyle = !empty($this->thisConfig['classes'][$class]['value'])
252  ? $this->thisConfig['classes'][$class]['value']
253  : '';
254  $title = $this->classesAnchorClassTitle[$class] ?? $this->classesAnchorDefaultTitle[$class] ?? '';
255  $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] .= '<option ' . $selected . ' value="' . htmlspecialchars($class) . '"'
256  . ($classStyle ? ' style="' . htmlspecialchars($classStyle) . '"' : '')
257  . 'data-link-title="' . htmlspecialchars($title) . '"'
258  . '>' . htmlspecialchars($classLabel)
259  . '</option>';
260  }
261  }
262  if ($this->classesAnchorJSOptions[$this->displayedLinkHandlerId] && !($this->buttonConfig['properties']['class']['required'] || $this->buttonConfig[$this->displayedLinkHandlerId]['properties']['class']['required'])) {
263  $selected = '';
264  if (!$this->linkAttributeValues['class'] && !$this->classesAnchorDefault[$this->displayedLinkHandlerId]) {
265  $selected = 'selected="selected"';
266  }
267  $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] = '<option ' . $selected . ' value=""></option>' . $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId];
268  }
269  }
270  // Default target
271  $this->defaultLinkTarget = $this->classesAnchorDefault[‪$this->displayedLinkHandlerId] && $this->classesAnchorDefaultTarget[‪$this->displayedLinkHandlerId]
272  ? $this->classesAnchorDefaultTarget[‪$this->displayedLinkHandlerId]
273  : ($this->buttonConfig[‪$this->displayedLinkHandlerId]['properties']['target']['default'] ?? $this->buttonConfig['properties']['target']['default'] ?? '');
274 
275  // todo: find new name for this option
276  // Initializing additional attributes
277  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rte_ckeditor']['plugins']['TYPO3Link']['additionalAttributes']) {
278  $addAttributes = GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rte_ckeditor']['plugins']['TYPO3Link']['additionalAttributes'], true);
279  foreach ($addAttributes as $attribute) {
280  $this->additionalAttributes[$attribute] = $this->linkAttributeValues[$attribute] ?? '';
281  }
282  }
283  return parent::renderLinkAttributeFields();
284  }
285 
293  protected function ‪getPageConfigLabel($string, $JScharCode = true)
294  {
295  if (strpos($string, 'LLL:') !== 0) {
296  $label = $string;
297  } else {
298  $label = $this->‪getLanguageService()->‪sL(trim($string));
299  }
300  $label = str_replace(['\\\'', '"'], ['\'', '\\"'], $label);
301  return $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label;
302  }
303 
307  protected function ‪renderCurrentUrl()
308  {
309  $removeLink = ' <a href="#" class="t3js-removeCurrentLink">' . htmlspecialchars($this->‪getLanguageService()->getLL('removeLink')) . '</a>';
310  return '
311  <div class="element-browser-panel element-browser-title">' .
312  htmlspecialchars($this->‪getLanguageService()->getLL('currentLink')) .
313  ': ' .
314  htmlspecialchars($this->currentLinkHandler->formatCurrentUrl()) .
315  '<span class="pull-right">' . $removeLink . '</span>' .
316  '</div>';
317  }
318 
324  protected function ‪getAllowedItems()
325  {
326  $allowedItems = parent::getAllowedItems();
327 
328  $blindLinkOptions = isset($this->thisConfig['blindLinkOptions'])
329  ? GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkOptions'], true)
330  : [];
331  $allowedItems = array_diff($allowedItems, $blindLinkOptions);
332 
333  if (is_array($this->buttonConfig['options']) && $this->buttonConfig['options']['removeItems']) {
334  $allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->buttonConfig['options']['removeItems'], true));
335  }
336 
337  return $allowedItems;
338  }
339 
345  protected function ‪getAllowedLinkAttributes()
346  {
347  $allowedLinkAttributes = parent::getAllowedLinkAttributes();
348 
349  $blindLinkFields = isset($this->thisConfig['blindLinkFields'])
350  ? GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkFields'], true)
351  : [];
352  $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
353 
354  return $allowedLinkAttributes;
355  }
356 
362  protected function ‪getLinkAttributeFieldDefinitions()
363  {
364  $fieldRenderingDefinitions = parent::getLinkAttributeFieldDefinitions();
365  $fieldRenderingDefinitions['title'] = $this->‪getTitleField();
366  $fieldRenderingDefinitions['class'] = $this->‪getClassField();
367  $fieldRenderingDefinitions['target'] = $this->‪getTargetField();
368  $fieldRenderingDefinitions['rel'] = $this->‪getRelField();
369  if (empty($this->buttonConfig['queryParametersSelector']['enabled'])) {
370  unset($fieldRenderingDefinitions['params']);
371  }
372  return $fieldRenderingDefinitions;
373  }
374 
380  protected function ‪getRelField()
381  {
382  if (empty($this->buttonConfig['relAttribute']['enabled'])) {
383  return '';
384  }
385 
386  $currentRel = '';
387  if ($this->displayedLinkHandler === $this->currentLinkHandler
388  && !empty($this->currentLinkParts)
389  && isset($this->linkAttributeValues['rel'])
390  && is_string($this->linkAttributeValues['rel'])
391  ) {
392  $currentRel = $this->linkAttributeValues['rel'];
393  }
394 
395  return '
396  <form action="" name="lrelform" id="lrelform" class="t3js-dummyform form-horizontal">
397  <div class="form-group form-group-sm">
398  <label class="col-xs-4 control-label">' .
399  htmlspecialchars($this->‪getLanguageService()->getLL('linkRelationship')) .
400  '</label>
401  <div class="col-xs-8">
402  <input type="text" name="lrel" class="form-control" value="' . htmlspecialchars($currentRel) . '" />
403  </div>
404  </div>
405  </form>
406  ';
407  }
408 
414  protected function ‪getTargetField()
415  {
416  $targetSelectorConfig = [];
417  if (is_array($this->buttonConfig['targetSelector'])) {
418  $targetSelectorConfig = $this->buttonConfig['targetSelector'];
419  }
420  $target = $this->linkAttributeValues['target'] ?: ‪$this->defaultLinkTarget;
421  $lang = $this->‪getLanguageService();
422  $targetSelector = '';
423 
424  if (!$targetSelectorConfig['disabled']) {
425  $targetSelector = '
426  <select name="ltarget_type" class="t3js-targetPreselect form-control">
427  <option value=""></option>
428  <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
429  <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
430  </select>
431  ';
432  }
433 
434  return '
435  <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform form-horizontal">
436  <div class="form-group form-group-sm" ' . ($targetSelectorConfig['disabled'] ? ' style="display: none;"' : '') . '>
437  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
438  <div class="col-xs-4">
439  <input type="text" name="ltarget" class="t3js-linkTarget form-control"
440  value="' . htmlspecialchars($target) . '" />
441  </div>
442  <div class="col-xs-4">
443  ' . $targetSelector . '
444  </div>
445  </div>
446  </form>
447  ';
448  }
449 
455  protected function ‪getTitleField()
456  {
457  if ($this->linkAttributeValues['title']) {
458  $title = $this->linkAttributeValues['title'];
459  } else {
460  $title = $this->classesAnchorDefaultTitle[‪$this->displayedLinkHandlerId] ?: '';
461  }
462  $readOnlyTitle = $this->‪isReadonlyTitle();
463 
464  if ($readOnlyTitle) {
465  $currentClass = $this->linkAttributeFields['class'];
466  if (!$currentClass) {
467  $currentClass = empty($this->classesAnchorDefault[$this->displayedLinkHandlerId]) ? '' : $this->classesAnchorDefault[‪$this->displayedLinkHandlerId];
468  }
469  $title = $this->classesAnchorClassTitle[$currentClass] ?? $this->classesAnchorDefaultTitle[‪$this->displayedLinkHandlerId] ?? '';
470  }
471  return '
472  <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform form-horizontal">
473  <div class="form-group form-group-sm">
474  <label class="col-xs-4 control-label">
475  ' . htmlspecialchars($this->‪getLanguageService()->getLL('title')) . '
476  </label>
477  <div class="col-xs-8">
478  <input ' . ($readOnlyTitle ? 'disabled' : '') . ' type="text" name="ltitle" class="form-control t3js-linkTitle"
479  value="' . htmlspecialchars($title) . '" />
480  </div>
481  </div>
482  </form>
483  ';
484  }
485 
491  protected function ‪getClassField()
492  {
493  $selectClass = '';
494  if ($this->classesAnchorJSOptions[$this->displayedLinkHandlerId]) {
495  $selectClass = '
496  <form action="" name="lclassform" id="lclassform" class="t3js-dummyform form-horizontal">
497  <div class="form-group form-group-sm">
498  <label class="col-xs-4 control-label">
499  ' . htmlspecialchars($this->‪getLanguageService()->getLL('class')) . '
500  </label>
501  <div class="col-xs-8">
502  <select name="lclass" class="t3js-class-selector form-control">
503  ' . $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] . '
504  </select>
505  </div>
506  </div>
507  </form>
508  ';
509  }
510  return $selectClass;
511  }
512 
518  protected function ‪getCurrentPageId()
519  {
520  return (int)$this->parameters['pid'];
521  }
522 
530  public function ‪getConfiguration()
531  {
532  return ‪$this->buttonConfig;
533  }
534 
540  protected function ‪getBodyTagAttributes()
541  {
542  ‪$parameters = parent::getBodyTagAttributes();
543  ‪$parameters['data-site-url'] = ‪$this->siteUrl;
544  ‪$parameters['data-default-link-target'] = ‪$this->defaultLinkTarget;
545  return ‪$parameters;
546  }
547 
553  public function ‪getUrlParameters(array $overrides = null)
554  {
555  return [
556  'act' => $overrides['act'] ?? ‪$this->displayedLinkHandlerId,
557  'P' => $overrides['P'] ?? ‪$this->parameters,
558  'editorId' => ‪$this->editorId,
559  'contentsLanguage' => ‪$this->contentsLanguage
560  ];
561  }
562 
563  protected function ‪isReadonlyTitle(): bool
564  {
565  if (isset($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['title']['readOnly'])) {
566  return (bool)$this->buttonConfig[‪$this->displayedLinkHandlerId]['properties']['title']['readOnly'];
567  }
568 
569  return (bool)($this->buttonConfig['properties']['title']['readOnly'] ?? false);
570  }
571 }
‪TYPO3\CMS\Core\Localization\LanguageService\sL
‪string sL($input)
Definition: LanguageService.php:158
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:35
‪TYPO3\CMS\Core\Compatibility\PublicMethodDeprecationTrait
Definition: PublicMethodDeprecationTrait.php:68
‪TYPO3\CMS\RteCKEditor\Controller
Definition: BrowseLinksController.php:3
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:29
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45
‪TYPO3\CMS\Core\Configuration\Richtext
Definition: Richtext.php:31