‪TYPO3CMS  9.5
RteLinkSyntaxUpdater.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\Log\LoggerInterface;
25 
31 {
37  protected ‪$tableFieldListToConsider = [];
38 
42  protected ‪$blackListedTables = [
43  'sys_log',
44  'sys_history',
45  'sys_template',
46  ];
47 
52  protected ‪$regularExpressions = [
53  'default' => '#
54  (?\'tag\'<link\\s++(?\'typolink\'[^>]+)>)
55  (?\'content\'(?:[^<]++|<(?!/link>))*+)
56  </link>
57  #xumsi',
58  'flex' => '#
59  (?\'tag\'&lt;link\\s++(?\'typolink\'(?:[^&]++|&(?!gt;))++)&gt;)
60  (?\'content\'(?:[^&]++|&(?!lt;/link&gt;))*+)
61  &lt;/link&gt;
62  #xumsi'
63  ];
64 
68  protected ‪$logger;
69 
75  public function ‪getTitle(): string
76  {
77  return 'Scan for old "<link>" syntax in richtext and text fields and update to "<a href>"';
78  }
79 
86  public function ‪hasPotentialUpdateForTable(string $tableName): bool
87  {
88  if (!is_array(‪$GLOBALS['TCA'][$tableName])) {
89  throw new \RuntimeException(
90  'Globals TCA of ' . $tableName . ' must be an array',
91  1484173035
92  );
93  }
94  $result = false;
95  if (in_array($tableName, $this->blackListedTables, true)) {
96  return $result;
97  }
98  $tcaOfTable = ‪$GLOBALS['TCA'][$tableName];
99  if (!is_array($tcaOfTable['columns'])) {
100  return $result;
101  }
102  foreach ($tcaOfTable['columns'] as $fieldName => $fieldConfiguration) {
103  if (isset($fieldConfiguration['config']['type'])
104  && in_array($fieldConfiguration['config']['type'], ['input', 'text', 'flex'], true)
105  ) {
106  $result = true;
107  if (!is_array($this->tableFieldListToConsider[$tableName])) {
108  $this->tableFieldListToConsider[$tableName] = [];
109  }
110  $this->tableFieldListToConsider[$tableName][] = $fieldName;
111  }
112  }
113  return $result;
114  }
115 
123  public function ‪updateTableRow(string $tableName, array $row): array
124  {
125  if (!is_array($this->tableFieldListToConsider)) {
126  throw new \RuntimeException(
127  'Parent should not call me with a table name I do not consider relevant for update',
128  1484173650
129  );
130  }
131  $this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
132  $fieldsToScan = $this->tableFieldListToConsider[$tableName];
133  foreach ($fieldsToScan as $fieldName) {
134  $row[$fieldName] = $this->‪transformLinkTagsIfFound(
135  $tableName,
136  $fieldName,
137  $row,
138  ‪$GLOBALS['TCA'][$tableName]['columns'][$fieldName]['config']['type'] === 'flex'
139  );
140  }
141  return $row;
142  }
143 
154  protected function ‪transformLinkTagsIfFound(string $tableName, string $fieldName, array $row, bool $isFlexformField)
155  {
156  $content = $row[$fieldName];
157  if (is_string($content)
158  && !empty($content)
159  && (stripos($content, '<link') !== false || stripos($content, '&lt;link') !== false)
160  ) {
161  $result = preg_replace_callback(
162  $this->regularExpressions[$isFlexformField ? 'flex' : 'default'],
163  function ($matches) use ($isFlexformField) {
164  $typoLink = $isFlexformField ? htmlspecialchars_decode($matches['typolink']) : $matches['typolink'];
165  $typoLinkParts = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typoLink);
166  $anchorTagAttributes = [
167  'target' => $typoLinkParts['target'],
168  'class' => $typoLinkParts['class'],
169  'title' => $typoLinkParts['title'],
170  ];
171 
172  $link = $typoLinkParts['url'];
173  if (!empty($typoLinkParts['additionalParams'])) {
174  $link .= (strpos($link, '?') === false ? '?' : '&') . ltrim($typoLinkParts['additionalParams'], '&');
175  }
176 
177  try {
178  $linkService = GeneralUtility::makeInstance(LinkService::class);
179  // Ensure the old syntax is converted to the new t3:// syntax, if necessary
180  $linkParts = $linkService->resolve($link);
181  $anchorTagAttributes['href'] = $linkService->asString($linkParts);
182  $newLink = '<a ' . GeneralUtility::implodeAttributes($anchorTagAttributes, true) . '>' .
183  ($isFlexformField ? htmlspecialchars_decode($matches['content']) : $matches['content']) .
184  '</a>';
185  if ($isFlexformField) {
186  $newLink = htmlspecialchars($newLink);
187  }
188  } catch (UnknownLinkHandlerException $e) {
189  $newLink = $matches[0];
190  } catch (UnknownUrnException $e) {
191  $newLink = $matches[0];
192  }
193 
194  return $newLink;
195  },
196  $content
197  );
198  if ($result !== null) {
199  $content = $result;
200  } else {
201  $this->logger->error('Converting links failed due to PCRE error', [
202  'table' => $tableName,
203  'field' => $fieldName,
204  'uid' => $row['uid'] ?? null,
205  'errorCode' => preg_last_error()
206  ]);
207  }
208  }
209  return $content;
210  }
211 }
‪TYPO3\CMS\Install\Updates\RowUpdater\RowUpdaterInterface
Definition: RowUpdaterInterface.php:22
‪TYPO3\CMS\Install\Updates\RowUpdater
Definition: ImageCropUpdater.php:3
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:5
‪TYPO3\CMS\Core\Log\LogManager
Definition: LogManager.php:25
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:45