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