‪TYPO3CMS  10.4
BrowseLinksController.php
Go to the documentation of this file.
1 <?php
2 
3 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 
19 
20 use Psr\Http\Message\ServerRequestInterface;
27 
33 {
37  protected ‪$editorId;
38 
44  protected ‪$contentsLanguage;
45 
52 
56  protected ‪$buttonConfig = [];
57 
61  protected ‪$thisConfig = [];
62 
66  protected ‪$classesAnchorDefault = [];
67 
71  protected ‪$classesAnchorDefaultTitle = [];
72 
77 
81  protected ‪$classesAnchorDefaultTarget = [];
82 
86  protected ‪$classesAnchorJSOptions = [];
87 
91  protected ‪$defaultLinkTarget = '';
92 
96  protected ‪$additionalAttributes = [];
97 
101  protected ‪$siteUrl = '';
102 
106  protected function ‪init()
107  {
108  parent::init();
109  $this->contentLanguageService = GeneralUtility::makeInstance(LanguageService::class);
110  }
111 
115  protected function ‪initVariables(ServerRequestInterface $request)
116  {
117  parent::initVariables($request);
118 
119  $queryParameters = $request->getQueryParams();
120 
121  $this->siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
122 
123  $this->currentLinkParts = $queryParameters['P']['curUrl'] ?? [];
124  $this->editorId = $queryParameters['editorId'];
125  $this->contentsLanguage = $queryParameters['contentsLanguage'];
126 
127  $this->contentLanguageService = ‪LanguageService::create($this->contentsLanguage);
128 
129  $tcaFieldConf = ['enableRichtext' => true];
130  if (!empty($queryParameters['P']['richtextConfigurationName'])) {
131  $tcaFieldConf['richtextConfiguration'] = $queryParameters['P']['richtextConfigurationName'];
132  }
133 
135  $richtextConfigurationProvider = GeneralUtility::makeInstance(Richtext::class);
136  $this->thisConfig = $richtextConfigurationProvider->getConfiguration(
137  $this->parameters['table'],
138  $this->parameters['fieldName'],
139  (int)$this->parameters['pid'],
140  $this->parameters['recordType'],
141  $tcaFieldConf
142  );
143  $this->buttonConfig = $this->thisConfig['buttons']['link'] ?? [];
144  }
145 
146  protected function ‪initDocumentTemplate()
147  {
148  parent::initDocumentTemplate();
149  $pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
150  $pageRenderer->loadRequireJsModule(
151  'TYPO3/CMS/RteCkeditor/RteLinkBrowser',
152  'function(RteLinkBrowser) {
153  RteLinkBrowser.initialize(' . GeneralUtility::quoteJSvalue($this->editorId) . ');
154  }'
155  );
156  }
157 
161  protected function ‪initCurrentUrl()
162  {
163  if (empty($this->currentLinkParts)) {
164  return;
165  }
166 
167  if (!empty($this->currentLinkParts['url'])) {
168  $linkService = GeneralUtility::makeInstance(LinkService::class);
169  $data = $linkService->resolve($this->currentLinkParts['url']);
170  $this->currentLinkParts['type'] = $data['type'];
171  unset($data['type']);
172  $this->currentLinkParts['url'] = $data;
173  if (!empty($this->currentLinkParts['url']['parameters'])) {
174  $this->currentLinkParts['params'] = '&' . $this->currentLinkParts['url']['parameters'];
175  }
176  }
177 
178  parent::initCurrentUrl();
179  }
180 
186  protected function ‪renderLinkAttributeFields()
187  {
188  // Processing the classes configuration
189  if (!empty($this->buttonConfig['properties']['class']['allowedClasses'])) {
190  $classesAnchorArray = is_array($this->buttonConfig['properties']['class']['allowedClasses']) ? $this->buttonConfig['properties']['class']['allowedClasses'] : ‪GeneralUtility::trimExplode(',', $this->buttonConfig['properties']['class']['allowedClasses'], true);
191  // Collecting allowed classes and configured default values
192  $classesAnchor = [
193  'all' => []
194  ];
195 
196  if (is_array($this->thisConfig['classesAnchor'])) {
197  $readOnlyTitle = $this->‪isReadonlyTitle();
198  foreach ($this->thisConfig['classesAnchor'] as $label => $conf) {
199  if (in_array($conf['class'], $classesAnchorArray, true)) {
200  $classesAnchor['all'][] = $conf['class'];
201  if ($conf['type'] === $this->displayedLinkHandlerId) {
202  $classesAnchor[$conf['type']][] = $conf['class'];
203  if ($this->buttonConfig[$conf['type']]['properties']['class']['default'] == $conf['class']) {
204  $this->classesAnchorDefault[$conf['type']] = $conf['class'];
205  if ($conf['titleText']) {
206  $this->classesAnchorDefaultTitle[$conf['type']] = $this->contentLanguageService->sL(trim($conf['titleText']));
207  }
208  if (isset($conf['target'])) {
209  $this->classesAnchorDefaultTarget[$conf['type']] = trim($conf['target']);
210  }
211  }
212  }
213  if ($readOnlyTitle && $conf['titleText']) {
214  $this->classesAnchorClassTitle[$conf['class']] = ($this->classesAnchorDefaultTitle[$conf['type']] = $this->contentLanguageService->sL(trim($conf['titleText'])));
215  }
216  }
217  }
218  }
219  if (isset($this->linkAttributeValues['class'])
220  && isset($classesAnchor[$this->displayedLinkHandlerId])
221  && !in_array($this->linkAttributeValues['class'], $classesAnchor[$this->displayedLinkHandlerId], true)
222  ) {
223  unset($this->linkAttributeValues['class']);
224  }
225  // Constructing the class selector options
226  foreach ($classesAnchorArray as $class) {
227  if (!in_array($class, $classesAnchor['all']) || in_array($class, $classesAnchor['all']) && is_array($classesAnchor[$this->displayedLinkHandlerId]) && in_array($class, $classesAnchor[$this->displayedLinkHandlerId])) {
228  $selected = '';
229  if ($this->linkAttributeValues['class'] === $class || !$this->linkAttributeValues['class'] && $this->classesAnchorDefault[$this->displayedLinkHandlerId] == $class) {
230  $selected = 'selected="selected"';
231  }
232  $classLabel = !empty($this->thisConfig['classes'][$class]['name'])
233  ? $this->‪getPageConfigLabel($this->thisConfig['classes'][$class]['name'], false)
234  : $class;
235  $classStyle = !empty($this->thisConfig['classes'][$class]['value'])
236  ? $this->thisConfig['classes'][$class]['value']
237  : '';
238  $title = $this->classesAnchorClassTitle[$class] ?? $this->classesAnchorDefaultTitle[$class] ?? '';
239  $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] .= '<option ' . $selected . ' value="' . htmlspecialchars($class) . '"'
240  . ($classStyle ? ' style="' . htmlspecialchars($classStyle) . '"' : '')
241  . 'data-link-title="' . htmlspecialchars($title) . '"'
242  . '>' . htmlspecialchars($classLabel)
243  . '</option>';
244  }
245  }
246  if ($this->classesAnchorJSOptions[$this->displayedLinkHandlerId] && !($this->buttonConfig['properties']['class']['required'] || $this->buttonConfig[$this->displayedLinkHandlerId]['properties']['class']['required'])) {
247  $selected = '';
248  if (!$this->linkAttributeValues['class'] && !$this->classesAnchorDefault[$this->displayedLinkHandlerId]) {
249  $selected = 'selected="selected"';
250  }
251  $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] = '<option ' . $selected . ' value=""></option>' . $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId];
252  }
253  }
254  // Default target
255  $this->defaultLinkTarget = $this->classesAnchorDefault[‪$this->displayedLinkHandlerId] && $this->classesAnchorDefaultTarget[‪$this->displayedLinkHandlerId]
256  ? $this->classesAnchorDefaultTarget[‪$this->displayedLinkHandlerId]
257  : ($this->buttonConfig[‪$this->displayedLinkHandlerId]['properties']['target']['default'] ?? $this->buttonConfig['properties']['target']['default'] ?? '');
258 
259  // todo: find new name for this option
260  // Initializing additional attributes
261  if (‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rte_ckeditor']['plugins']['TYPO3Link']['additionalAttributes']) {
262  $addAttributes = ‪GeneralUtility::trimExplode(',', ‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['rte_ckeditor']['plugins']['TYPO3Link']['additionalAttributes'], true);
263  foreach ($addAttributes as $attribute) {
264  $this->additionalAttributes[$attribute] = $this->linkAttributeValues[$attribute] ?? '';
265  }
266  }
267  return parent::renderLinkAttributeFields();
268  }
269 
277  protected function ‪getPageConfigLabel($string, $JScharCode = true)
278  {
279  if (strpos($string, 'LLL:') !== 0) {
280  $label = $string;
281  } else {
282  $label = $this->‪getLanguageService()->‪sL(trim($string));
283  }
284  $label = str_replace(['\\\'', '"'], ['\'', '\\"'], $label);
285  return $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label;
286  }
287 
288  protected function ‪renderCurrentUrl()
289  {
290  $this->moduleTemplate->getView()->assign('removeCurrentLink', true);
291  parent::renderCurrentUrl();
292  }
293 
299  protected function ‪getAllowedItems()
300  {
301  $allowedItems = parent::getAllowedItems();
302 
303  $blindLinkOptions = isset($this->thisConfig['blindLinkOptions'])
304  ? ‪GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkOptions'], true)
305  : [];
306  $allowedItems = array_diff($allowedItems, $blindLinkOptions);
307 
308  if (is_array($this->buttonConfig['options']) && $this->buttonConfig['options']['removeItems']) {
309  $allowedItems = array_diff($allowedItems, ‪GeneralUtility::trimExplode(',', $this->buttonConfig['options']['removeItems'], true));
310  }
311 
312  return $allowedItems;
313  }
314 
320  protected function ‪getAllowedLinkAttributes()
321  {
322  $allowedLinkAttributes = parent::getAllowedLinkAttributes();
323 
324  $blindLinkFields = isset($this->thisConfig['blindLinkFields'])
325  ? ‪GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkFields'], true)
326  : [];
327  $allowedLinkAttributes = array_diff($allowedLinkAttributes, $blindLinkFields);
328 
329  return $allowedLinkAttributes;
330  }
331 
337  protected function ‪getLinkAttributeFieldDefinitions()
338  {
339  $fieldRenderingDefinitions = parent::getLinkAttributeFieldDefinitions();
340  $fieldRenderingDefinitions['title'] = $this->‪getTitleField();
341  $fieldRenderingDefinitions['class'] = $this->‪getClassField();
342  $fieldRenderingDefinitions['target'] = $this->‪getTargetField();
343  $fieldRenderingDefinitions['rel'] = $this->‪getRelField();
344  if (empty($this->buttonConfig['queryParametersSelector']['enabled'])) {
345  unset($fieldRenderingDefinitions['params']);
346  }
347  return $fieldRenderingDefinitions;
348  }
349 
355  protected function ‪getRelField()
356  {
357  if (empty($this->buttonConfig['relAttribute']['enabled'])) {
358  return '';
359  }
360 
361  $currentRel = '';
362  if ($this->displayedLinkHandler === $this->currentLinkHandler
363  && !empty($this->currentLinkParts)
364  && isset($this->linkAttributeValues['rel'])
365  && is_string($this->linkAttributeValues['rel'])
366  ) {
367  $currentRel = $this->linkAttributeValues['rel'];
368  }
369 
370  return '
371  <form action="" name="lrelform" id="lrelform" class="t3js-dummyform form-horizontal">
372  <div class="form-group form-group-sm">
373  <label class="col-xs-4 control-label">' .
374  htmlspecialchars($this->‪getLanguageService()->getLL('linkRelationship')) .
375  '</label>
376  <div class="col-xs-8">
377  <input type="text" name="lrel" class="form-control" value="' . htmlspecialchars($currentRel) . '" />
378  </div>
379  </div>
380  </form>
381  ';
382  }
383 
389  protected function ‪getTargetField()
390  {
391  $targetSelectorConfig = [];
392  if (is_array($this->buttonConfig['targetSelector'])) {
393  $targetSelectorConfig = $this->buttonConfig['targetSelector'];
394  }
395  $target = $this->linkAttributeValues['target'] ?: ‪$this->defaultLinkTarget;
396  $lang = $this->‪getLanguageService();
397  $targetSelector = '';
398 
399  if (!$targetSelectorConfig['disabled']) {
400  $targetSelector = '
401  <select name="ltarget_type" class="t3js-targetPreselect form-control">
402  <option value=""></option>
403  <option value="_top">' . htmlspecialchars($lang->getLL('top')) . '</option>
404  <option value="_blank">' . htmlspecialchars($lang->getLL('newWindow')) . '</option>
405  </select>
406  ';
407  }
408 
409  return '
410  <form action="" name="ltargetform" id="ltargetform" class="t3js-dummyform form-horizontal">
411  <div class="form-group form-group-sm" ' . ($targetSelectorConfig['disabled'] ? ' style="display: none;"' : '') . '>
412  <label class="col-xs-4 control-label">' . htmlspecialchars($lang->getLL('target')) . '</label>
413  <div class="col-xs-4">
414  <input type="text" name="ltarget" class="t3js-linkTarget form-control"
415  value="' . htmlspecialchars($target) . '" />
416  </div>
417  <div class="col-xs-4">
418  ' . $targetSelector . '
419  </div>
420  </div>
421  </form>
422  ';
423  }
424 
430  protected function ‪getTitleField()
431  {
432  if ($this->linkAttributeValues['title']) {
433  $title = $this->linkAttributeValues['title'];
434  } else {
435  $title = $this->classesAnchorDefaultTitle[‪$this->displayedLinkHandlerId] ?: '';
436  }
437  $readOnlyTitle = $this->‪isReadonlyTitle();
438 
439  if ($readOnlyTitle) {
440  $currentClass = $this->linkAttributeFields['class'];
441  if (!$currentClass) {
442  $currentClass = empty($this->classesAnchorDefault[$this->displayedLinkHandlerId]) ? '' : $this->classesAnchorDefault[‪$this->displayedLinkHandlerId];
443  }
444  $title = $this->classesAnchorClassTitle[$currentClass] ?? $this->classesAnchorDefaultTitle[‪$this->displayedLinkHandlerId] ?? '';
445  }
446  return '
447  <form action="" name="ltitleform" id="ltitleform" class="t3js-dummyform form-horizontal">
448  <div class="form-group form-group-sm">
449  <label class="col-xs-4 control-label">
450  ' . htmlspecialchars($this->‪getLanguageService()->getLL('title')) . '
451  </label>
452  <div class="col-xs-8">
453  <input ' . ($readOnlyTitle ? 'disabled' : '') . ' type="text" name="ltitle" class="form-control t3js-linkTitle"
454  value="' . htmlspecialchars($title) . '" />
455  </div>
456  </div>
457  </form>
458  ';
459  }
460 
466  protected function ‪getClassField()
467  {
468  $selectClass = '';
469  if ($this->classesAnchorJSOptions[$this->displayedLinkHandlerId]) {
470  $selectClass = '
471  <form action="" name="lclassform" id="lclassform" class="t3js-dummyform form-horizontal">
472  <div class="form-group form-group-sm">
473  <label class="col-xs-4 control-label">
474  ' . htmlspecialchars($this->‪getLanguageService()->getLL('class')) . '
475  </label>
476  <div class="col-xs-8">
477  <select name="lclass" class="t3js-class-selector form-control">
478  ' . $this->classesAnchorJSOptions[‪$this->displayedLinkHandlerId] . '
479  </select>
480  </div>
481  </div>
482  </form>
483  ';
484  }
485  return $selectClass;
486  }
487 
493  protected function ‪getCurrentPageId()
494  {
495  return (int)$this->parameters['pid'];
496  }
497 
505  public function ‪getConfiguration()
506  {
507  return ‪$this->buttonConfig;
508  }
509 
515  protected function ‪getBodyTagAttributes()
516  {
517  ‪$parameters = parent::getBodyTagAttributes();
518  ‪$parameters['data-site-url'] = ‪$this->siteUrl;
519  ‪$parameters['data-default-link-target'] = ‪$this->defaultLinkTarget;
520  return ‪$parameters;
521  }
522 
528  public function ‪getUrlParameters(array $overrides = null)
529  {
530  return [
531  'act' => $overrides['act'] ?? ‪$this->displayedLinkHandlerId,
532  'P' => $overrides['P'] ?? ‪$this->parameters,
533  'editorId' => ‪$this->editorId,
534  'contentsLanguage' => ‪$this->contentsLanguage
535  ];
536  }
537 
538  protected function ‪isReadonlyTitle(): bool
539  {
540  if (isset($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['title']['readOnly'])) {
541  return (bool)$this->buttonConfig[‪$this->displayedLinkHandlerId]['properties']['title']['readOnly'];
542  }
543 
544  return (bool)($this->buttonConfig['properties']['title']['readOnly'] ?? false);
545  }
546 }
‪TYPO3\CMS\Core\Localization\LanguageService\sL
‪string sL($input)
Definition: LanguageService.php:194
‪TYPO3\CMS\Core\Page\PageRenderer
Definition: PageRenderer.php:42
‪TYPO3\CMS\Core\Utility\GeneralUtility\trimExplode
‪static string[] trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
Definition: GeneralUtility.php:1059
‪TYPO3\CMS\RteCKEditor\Controller
Definition: BrowseLinksController.php:18
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:42
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Configuration\Richtext
Definition: Richtext.php:33
‪TYPO3\CMS\Core\Localization\LanguageService\create
‪static static create(string $locale)
Definition: LanguageService.php:430