buttonConfig; } /** * @return array{act: string, P: array, editorId: string, contentsLanguage: string} Array of parameters which have to be added to URLs */ public function getUrlParameters(?array $overrides = null): array { return [ 'act' => $overrides['act'] ?? $this->displayedLinkHandlerId, 'P' => $overrides['P'] ?? $this->parameters, 'editorId' => $this->editorId, 'contentsLanguage' => $this->contentsLanguage, ]; } protected function initDocumentTemplate(): void { $this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction( JavaScriptModuleInstruction::create('@typo3/rte-ckeditor/rte-link-browser.js') ->invoke('initialize', $this->editorId) ); } protected function getCurrentPageId(): int { return (int)$this->parameters['pid']; } protected function initVariables(ServerRequestInterface $request): void { parent::initVariables($request); $queryParameters = $request->getQueryParams(); $this->siteUrl = $request->getAttribute('normalizedParams')->getSiteUrl(); $this->currentLinkParts = $queryParameters['P']['curUrl'] ?? []; $this->editorId = $queryParameters['editorId'] ?? ''; $this->contentsLanguage = $queryParameters['contentsLanguage'] ?? ''; $this->contentLanguageService = $this->languageServiceFactory->create($this->contentsLanguage); $tcaFieldConf = [ 'enableRichtext' => true, 'richtextConfiguration' => $this->parameters['richtextConfigurationName'] ?: null, ]; $this->thisConfig = $this->richtext->getConfiguration( $this->parameters['table'], $this->parameters['fieldName'], (int)$this->parameters['pid'], $this->parameters['recordType'], $tcaFieldConf ); $this->buttonConfig = $this->thisConfig['buttons']['link'] ?? []; } protected function initCurrentUrl(): void { if (empty($this->currentLinkParts)) { return; } if (!empty($this->currentLinkParts['url'])) { try { $data = $this->linkService->resolve($this->currentLinkParts['url']); $this->currentLinkParts['type'] = $data['type']; unset($data['type']); $this->currentLinkParts['url'] = $data; if (!empty($this->currentLinkParts['url']['parameters'])) { $this->currentLinkParts['params'] = '&' . $this->currentLinkParts['url']['parameters']; } } catch (UnknownLinkHandlerException $e) { $this->flashMessageService->getMessageQueueByIdentifier()->enqueue( new FlashMessage(message: $e->getMessage(), severity: ContextualFeedbackSeverity::ERROR) ); } } parent::initCurrentUrl(); } protected function renderLinkAttributeFields(ViewInterface $view): string { // Processing the classes configuration if (!empty($this->buttonConfig['properties']['class']['allowedClasses'])) { $classesAnchorArray = is_array($this->buttonConfig['properties']['class']['allowedClasses']) ? $this->buttonConfig['properties']['class']['allowedClasses'] : GeneralUtility::trimExplode(',', $this->buttonConfig['properties']['class']['allowedClasses'], true); // Collecting allowed classes and configured default values $classesAnchor = [ 'all' => [], ]; if (is_array($this->thisConfig['classesAnchor'] ?? null)) { foreach ($this->thisConfig['classesAnchor'] as $conf) { if (in_array($conf['class'] ?? null, $classesAnchorArray, true)) { $classesAnchor['all'][] = $conf['class']; if ($conf['type'] === $this->displayedLinkHandlerId) { $classesAnchor[$conf['type']][] = $conf['class']; if (($this->buttonConfig[$conf['type']]['properties']['class']['default'] ?? null) === $conf['class']) { $this->classesAnchorDefault[$conf['type']] = $conf['class']; if (isset($conf['target'])) { $this->classesAnchorDefaultTarget[$conf['type']] = trim((string)$conf['target']); } } } } } } $linkClass = $this->linkAttributeValues['class'] ?? ''; if ($linkClass !== '') { $currentLinkClassIsAllowed = true; if (!in_array($linkClass, $classesAnchorArray, true)) { // Current class is not a globally allowed class $currentLinkClassIsAllowed = false; } if ( isset($classesAnchor[$this->displayedLinkHandlerId]) && in_array($linkClass, $classesAnchor['all'], true) && !in_array($linkClass, $classesAnchor[$this->displayedLinkHandlerId], true) ) { // Current class is limited to specific link types but not available in current link type $currentLinkClassIsAllowed = false; } if (!$currentLinkClassIsAllowed) { $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ??= ''; // Add a dummy option that preserved the current class value (despite being invalid) // in order to prevent unintentional modification of assigned classes. $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] .= sprintf( '', htmlspecialchars($linkClass), htmlspecialchars( @sprintf( '[ ' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.noMatchingValue') . ' ]', $linkClass ) ) ); } } // Constructing the class selector options foreach ($classesAnchorArray as $class) { if ( !in_array($class, $classesAnchor['all'], true) || ( in_array($class, $classesAnchor['all'], true) && is_array($classesAnchor[$this->displayedLinkHandlerId] ?? null) && in_array($class, $classesAnchor[$this->displayedLinkHandlerId]) ) ) { $selected = ''; if ( (($this->linkAttributeValues['class'] ?? false) === $class) || ($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false) === $class ) { $selected = 'selected="selected"'; } $classLabel = !empty($this->thisConfig['classes'][$class]['name']) ? $this->getPageConfigLabel($this->thisConfig['classes'][$class]['name'], false) : $class; $classStyle = !empty($this->thisConfig['classes'][$class]['value']) ? $this->thisConfig['classes'][$class]['value'] : ''; $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ??= ''; $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] .= ''; } } if ( ($this->classesAnchorJSOptions[$this->displayedLinkHandlerId] ?? false) && !( ($this->buttonConfig['properties']['class']['required'] ?? false) || ($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['class']['required'] ?? false) ) ) { $selected = ''; if (!($this->linkAttributeValues['class'] ?? false) && !($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false)) { $selected = 'selected="selected"'; } $this->classesAnchorJSOptions[$this->displayedLinkHandlerId] = '' . $this->classesAnchorJSOptions[$this->displayedLinkHandlerId]; } } // Default target $this->defaultLinkTarget = ($this->classesAnchorDefault[$this->displayedLinkHandlerId] ?? false) && ($this->classesAnchorDefaultTarget[$this->displayedLinkHandlerId] ?? false) ? $this->classesAnchorDefaultTarget[$this->displayedLinkHandlerId] : ($this->buttonConfig[$this->displayedLinkHandlerId]['properties']['target']['default'] ?? $this->buttonConfig['properties']['target']['default'] ?? ''); return parent::renderLinkAttributeFields($view); } /** * Localize a label obtained from Page TSConfig * * @param string $string The label to be localized * @param bool $JScharCode If it needs to be converted to an array of char numbers * @return string Localized string */ protected function getPageConfigLabel(string $string, bool $JScharCode = true): string { $label = $this->getLanguageService()->sL(trim($string)); $label = str_replace(['\\\'', '"'], ['\'', '\\"'], $label); return $JScharCode ? GeneralUtility::quoteJSvalue($label) : $label; } protected function renderCurrentUrl(ViewInterface $view): void { $view->assign('removeCurrentLink', true); parent::renderCurrentUrl($view); } /** * @return string[] */ protected function getAllowedItems(): array { $allowedItems = parent::getAllowedItems(); if (isset($this->thisConfig['allowedTypes'])) { $allowedItems = array_intersect($allowedItems, GeneralUtility::trimExplode(',', $this->thisConfig['allowedTypes'], true)); } elseif (isset($this->thisConfig['blindLinkOptions'])) { // @todo Deprecate this option $allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkOptions'], true)); } if (is_array($this->buttonConfig['options'] ?? null) && !empty($this->buttonConfig['options']['removeItems'])) { $allowedItems = array_diff($allowedItems, GeneralUtility::trimExplode(',', $this->buttonConfig['options']['removeItems'], true)); } return $allowedItems; } /** * @return string[] */ protected function getAllowedLinkAttributes(): array { $allowedLinkAttributes = parent::getAllowedLinkAttributes(); if (isset($this->thisConfig['allowedOptions'])) { $allowedLinkAttributes = array_intersect($allowedLinkAttributes, GeneralUtility::trimExplode(',', $this->thisConfig['allowedOptions'], true)); } elseif (isset($this->thisConfig['blindLinkFields'])) { // @todo Deprecate this option $allowedLinkAttributes = array_diff($allowedLinkAttributes, GeneralUtility::trimExplode(',', $this->thisConfig['blindLinkFields'], true)); } return $allowedLinkAttributes; } /** * Create an array of link attribute field rendering definitions * * @return string[] */ protected function getLinkAttributeFieldDefinitions(): array { $fieldRenderingDefinitions = parent::getLinkAttributeFieldDefinitions(); $fieldRenderingDefinitions['class'] = $this->getClassField(); $fieldRenderingDefinitions['target'] = $this->getTargetField(); $fieldRenderingDefinitions['rel'] = $this->getRelField(); if (empty($this->buttonConfig['queryParametersSelector']['enabled'])) { unset($fieldRenderingDefinitions['params']); } return $fieldRenderingDefinitions; } protected function getRelField(): string { if (empty($this->buttonConfig['relAttribute']['enabled'])) { return ''; } $currentRel = ''; if ($this->displayedLinkHandler === $this->currentLinkHandler && !empty($this->currentLinkParts) && is_string($this->linkAttributeValues['rel'] ?? null) ) { $currentRel = $this->linkAttributeValues['rel']; } return '