‪TYPO3CMS  ‪main
LinkBrowserController.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\ResponseInterface;
21 use Psr\Http\Message\ServerRequestInterface;
23 use TYPO3\CMS\Backend\Utility\BackendUtility;
28 use TYPO3\CMS\Core\LinkHandling\TypoLinkCodecService;
33 
37 #[AsController]
38 class ‪LinkBrowserController extends AbstractLinkBrowserController
39 {
40  public function ‪__construct(
41  protected readonly ‪LinkService $linkService,
42  protected readonly TypoLinkCodecService $typoLinkCodecService,
43  protected readonly ‪FlashMessageService $flashMessageService,
44  private readonly ‪HashService $hashService,
45  ) {}
46 
47  public function ‪getConfiguration(): array
48  {
49  $tsConfig = BackendUtility::getPagesTSconfig($this->‪getCurrentPageId());
50  return $tsConfig['TCEMAIN.']['linkHandler.']['page.']['configuration.'] ?? [];
51  }
52 
57  public function ‪encodeTypoLink(ServerRequestInterface $request): ResponseInterface
58  {
59  $typoLinkParts = $request->getQueryParams();
60  if (isset($typoLinkParts['params'])) {
61  $typoLinkParts['additionalParams'] = $typoLinkParts['params'];
62  unset($typoLinkParts['params']);
63  }
64  $typoLink = $this->typoLinkCodecService->encode($typoLinkParts);
65  return new ‪JsonResponse(['typoLink' => $typoLink]);
66  }
67 
68  protected function ‪initDocumentTemplate(): void
69  {
71  $this->parameters['fieldChangeFunc'] = [];
72  }
73  $this->pageRenderer->getJavaScriptRenderer()->addJavaScriptModuleInstruction(
74  ‪JavaScriptModuleInstruction::create('@typo3/backend/form-engine-link-browser-adapter.js')
75  // @todo use a proper constructor when migrating to TypeScript
76  ->invoke('setOnFieldChangeItems', $this->parameters['fieldChangeFunc'])
77  );
78  }
79 
80  protected function ‪getCurrentPageId(): int
81  {
82  $pageId = 0;
83  $browserParameters = $this->parameters;
84  if (isset($browserParameters['pid'])) {
85  $pageId = $browserParameters['pid'];
86  } elseif (isset($browserParameters['itemName'])) {
87  // parse data[<table>][<uid>]
88  if (preg_match('~data\[([^]]*)\]\[([^]]*)\]~', $browserParameters['itemName'], $matches)) {
89  $recordArray = BackendUtility::getRecord($matches['1'], $matches['2']);
90  if (is_array($recordArray)) {
91  $pageId = $recordArray['pid'];
92  }
93  }
94  }
95  return (int)BackendUtility::getTSCpidCached($browserParameters['table'], $browserParameters['uid'], $pageId)[0];
96  }
97 
98  protected function ‪initCurrentUrl(): void
99  {
100  $currentLink = isset($this->parameters['currentValue']) ? trim($this->parameters['currentValue']) : '';
102  $currentLinkParts = $this->typoLinkCodecService->decode($currentLink);
103  $currentLinkParts['params'] = $currentLinkParts['additionalParams'];
104  unset($currentLinkParts['additionalParams']);
105 
106  if (!empty($currentLinkParts['url'])) {
107  try {
108  $data = $this->linkService->resolve($currentLinkParts['url']);
109  $currentLinkParts['type'] = $data['type'];
110  unset($data['type']);
111  $currentLinkParts['url'] = $data;
112  } catch (‪UnknownLinkHandlerException $e) {
113  $this->flashMessageService->getMessageQueueByIdentifier()->enqueue(
114  new ‪FlashMessage(message: $e->getMessage(), severity: ContextualFeedbackSeverity::ERROR)
115  );
116  }
117  }
118 
119  $this->currentLinkParts = $currentLinkParts;
120 
121  parent::initCurrentUrl();
122  }
123 
131  protected function ‪areFieldChangeFunctionsValid(bool $handleFlexformSections = false): bool
132  {
133  $result = false;
134  if (isset($this->parameters['fieldChangeFunc']) && is_array($this->parameters['fieldChangeFunc']) && isset($this->parameters['fieldChangeFuncHash'])) {
135  $matches = [];
136  $pattern = '#\\[el\\]\\[(([^]-]+-[^]-]+-)(idx\\d+-)([^]]+))\\]#i';
137  $fieldChangeFunctions = $this->parameters['fieldChangeFunc'];
138  // Special handling of flexform sections:
139  // Field change functions are modified in JavaScript, thus the hash is always invalid
140  if ($handleFlexformSections && preg_match($pattern, $this->parameters['itemName'], $matches)) {
141  $originalName = $matches[1];
142  $cleanedName = $matches[2] . $matches[4];
143  $fieldChangeFunctions = $this->‪strReplaceRecursively(
144  $originalName,
145  $cleanedName,
146  $fieldChangeFunctions
147  );
148  }
149  $result = hash_equals($this->hashService->hmac(serialize($fieldChangeFunctions), 'backend-link-browser'), $this->parameters['fieldChangeFuncHash']);
150  }
151  return $result;
152  }
153 
154  protected function ‪strReplaceRecursively(string $search, string $replace, array $array): array
155  {
156  foreach ($array as &$item) {
157  if (is_array($item)) {
158  $item = $this->‪strReplaceRecursively($search, $replace, $item);
159  } else {
160  $item = str_replace($search, $replace, $item);
161  }
162  }
163  return $array;
164  }
165 }
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction\create
‪static create(string $name, string $exportName=null)
Definition: JavaScriptModuleInstruction.php:47
‪TYPO3\CMS\Core\Page\JavaScriptModuleInstruction
Definition: JavaScriptModuleInstruction.php:23
‪TYPO3\CMS\Core\Type\ContextualFeedbackSeverity
‪ContextualFeedbackSeverity
Definition: ContextualFeedbackSeverity.php:25
‪TYPO3\CMS\Core\Messaging\FlashMessage
Definition: FlashMessage.php:27
‪TYPO3\CMS\Core\Http\JsonResponse
Definition: JsonResponse.php:28
‪TYPO3\CMS\Backend\Attribute\AsController
Definition: AsController.php:25
‪TYPO3\CMS\Core\Crypto\HashService
Definition: HashService.php:27
‪TYPO3\CMS\Backend\Controller
Definition: AboutController.php:18
‪TYPO3\CMS\Core\Messaging\FlashMessageService
Definition: FlashMessageService.php:27