TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
RteHtmlParser.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Html;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
19 use TYPO3\CMS\Core\Resource;
23 
28 {
33  public $blockElementList = 'DIV,TABLE,BLOCKQUOTE,PRE,UL,OL,H1,H2,H3,H4,H5,H6,ADDRESS,DL,DD,HEADER,SECTION,FOOTER,NAV,ARTICLE,ASIDE';
34 
39  protected $defaultAllowedTagsList = 'b,i,u,a,img,br,div,center,pre,font,hr,sub,sup,p,strong,em,li,ul,ol,blockquote,strike,span';
40 
46  public $recPid = 0;
47 
53  public $elRef = '';
54 
60  public $tsConfig = [];
61 
67  public $procOptions = [];
68 
75 
81  public $getKeepTags_cache = [];
82 
88  public $allowedClasses = [];
89 
97  public function init($elRef = '', $recPid = 0)
98  {
99  $this->recPid = $recPid;
100  $this->elRef = $elRef;
101  }
102 
103  /**********************************************
104  *
105  * Main function
106  *
107  **********************************************/
118  public function RTE_transform($value, $specConf, $direction = 'rte', $thisConfig = [])
119  {
120  // Init:
121  $this->tsConfig = $thisConfig;
122  $this->procOptions = (array)$thisConfig['proc.'];
123  // dynamic configuration of blockElementList
124  if ($this->procOptions['blockElementList']) {
125  $this->blockElementList = $this->procOptions['blockElementList'];
126  }
127  // Setting modes:
128  if ((string)$this->procOptions['overruleMode'] !== '') {
129  $modes = array_unique(GeneralUtility::trimExplode(',', $this->procOptions['overruleMode']));
130  } else {
131  // Get parameters for rte_transformation:
132  $specialFieldConfiguration = BackendUtility::getSpecConfParametersFromArray($specConf['rte_transform']['parameters']);
133  $modes = array_unique(GeneralUtility::trimExplode('-', $specialFieldConfiguration['mode']));
134  }
135  $revmodes = array_flip($modes);
136  // Find special modes and extract them:
137  if (isset($revmodes['ts_css'])) {
138  $modes[$revmodes['ts_css']] = 'css_transform,ts_images,ts_links';
139  }
140  // Make list unique
141  $modes = array_unique(GeneralUtility::trimExplode(',', implode(',', $modes), true));
142  // Reverse order if direction is "rte"
143  if ($direction === 'rte') {
144  $modes = array_reverse($modes);
145  }
146  // Getting additional HTML cleaner configuration. These are applied either before or after the main transformation is done and is thus totally independent processing options you can set up:
147  $entry_HTMLparser = $this->procOptions['entryHTMLparser_' . $direction] ? $this->HTMLparserConfig($this->procOptions['entryHTMLparser_' . $direction . '.']) : '';
148  $exit_HTMLparser = $this->procOptions['exitHTMLparser_' . $direction] ? $this->HTMLparserConfig($this->procOptions['exitHTMLparser_' . $direction . '.']) : '';
149  // Line breaks of content is unified into char-10 only (removing char 13)
150  if (!$this->procOptions['disableUnifyLineBreaks']) {
151  $value = str_replace(CRLF, LF, $value);
152  }
153  // In an entry-cleaner was configured, pass value through the HTMLcleaner with that:
154  if (is_array($entry_HTMLparser)) {
155  $value = $this->HTMLcleaner($value, $entry_HTMLparser[0], $entry_HTMLparser[1], $entry_HTMLparser[2], $entry_HTMLparser[3]);
156  }
157  // Traverse modes:
158  foreach ($modes as $cmd) {
159  // ->DB
160  if ($direction == 'db') {
161  // Checking for user defined transformation:
162  if ($_classRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['transformation'][$cmd]) {
163  $_procObj = GeneralUtility::getUserObj($_classRef);
164  $_procObj->pObj = $this;
165  $_procObj->transformationKey = $cmd;
166  $value = $_procObj->transform_db($value, $this);
167  } else {
168  // ... else use defaults:
169  switch ($cmd) {
170  case 'ts_images':
171  $value = $this->TS_images_db($value);
172  break;
173  case 'ts_links':
174  $value = $this->TS_links_db($value);
175  break;
176  case 'css_transform':
177  $this->allowedClasses = GeneralUtility::trimExplode(',', $this->procOptions['allowedClasses'], true);
178  // CR has a very disturbing effect, so just remove all CR and rely on LF
179  $value = str_replace(CR, '', $value);
180  // Transform empty paragraphs into spacing paragraphs
181  $value = str_replace('<p></p>', '<p>&nbsp;</p>', $value);
182  // Double any trailing spacing paragraph so that it does not get removed by divideIntoLines()
183  $value = preg_replace('/<p>&nbsp;<\/p>$/', '<p>&nbsp;</p>' . '<p>&nbsp;</p>', $value);
184  $value = $this->TS_transform_db($value);
185  break;
186  default:
187  // Do nothing
188  }
189  }
190  }
191  // ->RTE
192  if ($direction == 'rte') {
193  // Checking for user defined transformation:
194  if ($_classRef = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['transformation'][$cmd]) {
195  $_procObj = GeneralUtility::getUserObj($_classRef);
196  $_procObj->pObj = $this;
197  $value = $_procObj->transform_rte($value, $this);
198  } else {
199  // ... else use defaults:
200  switch ($cmd) {
201  case 'ts_images':
202  $value = $this->TS_images_rte($value);
203  break;
204  case 'ts_links':
205  $value = $this->TS_links_rte($value);
206  break;
207  case 'css_transform':
208  // Has a very disturbing effect, so just remove all '13' - depend on '10'
209  $value = str_replace(CR, '', $value);
210  $value = $this->TS_transform_rte($value);
211  break;
212  default:
213  // Do nothing
214  }
215  }
216  }
217  }
218  // In an exit-cleaner was configured, pass value through the HTMLcleaner with that:
219  if (is_array($exit_HTMLparser)) {
220  $value = $this->HTMLcleaner($value, $exit_HTMLparser[0], $exit_HTMLparser[1], $exit_HTMLparser[2], $exit_HTMLparser[3]);
221  }
222  // Final clean up of linebreaks:
223  if (!$this->procOptions['disableUnifyLineBreaks']) {
224  // Make sure no \r\n sequences has entered in the meantime...
225  $value = str_replace(CRLF, LF, $value);
226  // ... and then change all \n into \r\n
227  $value = str_replace(LF, CRLF, $value);
228  }
229  // Return value:
230  return $value;
231  }
232 
233  /************************************
234  *
235  * Specific RTE TRANSFORMATION functions
236  *
237  *************************************/
249  public function TS_images_db($value)
250  {
251  // Split content by <img> tags and traverse the resulting array for processing:
252  $imgSplit = $this->splitTags('img', $value);
253  if (count($imgSplit) > 1) {
254  $siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
255  $sitePath = str_replace(GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST'), '', $siteUrl);
257  $resourceFactory = Resource\ResourceFactory::getInstance();
259  $magicImageService = GeneralUtility::makeInstance(Resource\Service\MagicImageService::class);
260  $magicImageService->setMagicImageMaximumDimensions($this->tsConfig);
261  foreach ($imgSplit as $k => $v) {
262  // Image found, do processing:
263  if ($k % 2) {
264  // Get attributes
265  list($attribArray) = $this->get_tag_attributes($v, true);
266  // It's always an absolute URL coming from the RTE into the Database.
267  $absoluteUrl = trim($attribArray['src']);
268  // Make path absolute if it is relative and we have a site path which is not '/'
269  $pI = pathinfo($absoluteUrl);
270  if ($sitePath && !$pI['scheme'] && GeneralUtility::isFirstPartOfStr($absoluteUrl, $sitePath)) {
271  // If site is in a subpath (eg. /~user_jim/) this path needs to be removed because it will be added with $siteUrl
272  $absoluteUrl = substr($absoluteUrl, strlen($sitePath));
273  $absoluteUrl = $siteUrl . $absoluteUrl;
274  }
275  // Image dimensions set in the img tag, if any
276  $imgTagDimensions = $this->getWHFromAttribs($attribArray);
277  if ($imgTagDimensions[0]) {
278  $attribArray['width'] = $imgTagDimensions[0];
279  }
280  if ($imgTagDimensions[1]) {
281  $attribArray['height'] = $imgTagDimensions[1];
282  }
283  $originalImageFile = null;
284  if ($attribArray['data-htmlarea-file-uid']) {
285  // An original image file uid is available
286  try {
288  $originalImageFile = $resourceFactory->getFileObject((int)$attribArray['data-htmlarea-file-uid']);
289  } catch (Resource\Exception\FileDoesNotExistException $fileDoesNotExistException) {
290  // Log the fact the file could not be retrieved.
291  $message = sprintf('Could not find file with uid "%s"', $attribArray['data-htmlarea-file-uid']);
292  $this->getLogger()->error($message);
293  }
294  }
295  if ($originalImageFile instanceof Resource\File) {
296  // Public url of local file is relative to the site url, absolute otherwise
297  if ($absoluteUrl == $originalImageFile->getPublicUrl() || $absoluteUrl == $siteUrl . $originalImageFile->getPublicUrl()) {
298  // This is a plain image, i.e. reference to the original image
299  if ($this->procOptions['plainImageMode']) {
300  // "plain image mode" is configured
301  // Find the dimensions of the original image
302  $imageInfo = [
303  $originalImageFile->getProperty('width'),
304  $originalImageFile->getProperty('height')
305  ];
306  if (!$imageInfo[0] || !$imageInfo[1]) {
307  $filePath = $originalImageFile->getForLocalProcessing(false);
308  $imageInfo = @getimagesize($filePath);
309  }
310  $attribArray = $this->applyPlainImageModeSettings($imageInfo, $attribArray);
311  }
312  } else {
313  // Magic image case: get a processed file with the requested configuration
314  $imageConfiguration = [
315  'width' => $imgTagDimensions[0],
316  'height' => $imgTagDimensions[1]
317  ];
318  $magicImage = $magicImageService->createMagicImage($originalImageFile, $imageConfiguration);
319  $attribArray['width'] = $magicImage->getProperty('width');
320  $attribArray['height'] = $magicImage->getProperty('height');
321  $attribArray['src'] = $magicImage->getPublicUrl();
322  }
323  } elseif (!GeneralUtility::isFirstPartOfStr($absoluteUrl, $siteUrl) && !$this->procOptions['dontFetchExtPictures'] && TYPO3_MODE === 'BE') {
324  // External image from another URL: in that case, fetch image, unless the feature is disabled or we are not in backend mode
325  // Fetch the external image
326  $externalFile = GeneralUtility::getUrl($absoluteUrl);
327  if ($externalFile) {
328  $pU = parse_url($absoluteUrl);
329  $pI = pathinfo($pU['path']);
330  $extension = strtolower($pI['extension']);
331  if ($extension === 'jpg' || $extension === 'jpeg' || $extension === 'gif' || $extension === 'png') {
332  $fileName = GeneralUtility::shortMD5($absoluteUrl) . '.' . $pI['extension'];
333  // We insert this image into the user default upload folder
334  list($table, $field) = explode(':', $this->elRef);
335  $folder = $GLOBALS['BE_USER']->getDefaultUploadFolder($this->recPid, $table, $field);
336  $fileObject = $folder->createFile($fileName)->setContents($externalFile);
337  $imageConfiguration = [
338  'width' => $attribArray['width'],
339  'height' => $attribArray['height']
340  ];
341  $magicImage = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
342  $attribArray['width'] = $magicImage->getProperty('width');
343  $attribArray['height'] = $magicImage->getProperty('height');
344  $attribArray['data-htmlarea-file-uid'] = $fileObject->getUid();
345  $attribArray['src'] = $magicImage->getPublicUrl();
346  }
347  }
348  } elseif (GeneralUtility::isFirstPartOfStr($absoluteUrl, $siteUrl)) {
349  // Finally, check image as local file (siteURL equals the one of the image)
350  // Image has no data-htmlarea-file-uid attribute
351  // Relative path, rawurldecoded for special characters.
352  $path = rawurldecode(substr($absoluteUrl, strlen($siteUrl)));
353  // Absolute filepath, locked to relative path of this project
354  $filepath = GeneralUtility::getFileAbsFileName($path);
355  // Check file existence (in relative directory to this installation!)
356  if ($filepath && @is_file($filepath)) {
357  // Treat it as a plain image
358  if ($this->procOptions['plainImageMode']) {
359  // If "plain image mode" has been configured
360  // Find the original dimensions of the image
361  $imageInfo = @getimagesize($filepath);
362  $attribArray = $this->applyPlainImageModeSettings($imageInfo, $attribArray);
363  }
364  // Let's try to find a file uid for this image
365  try {
366  $fileOrFolderObject = $resourceFactory->retrieveFileOrFolderObject($path);
367  if ($fileOrFolderObject instanceof Resource\FileInterface) {
368  $fileIdentifier = $fileOrFolderObject->getIdentifier();
369  $fileObject = $fileOrFolderObject->getStorage()->getFile($fileIdentifier);
370  // @todo if the retrieved file is a processed file, get the original file...
371  $attribArray['data-htmlarea-file-uid'] = $fileObject->getUid();
372  }
373  } catch (Resource\Exception\ResourceDoesNotExistException $resourceDoesNotExistException) {
374  // Nothing to be done if file/folder not found
375  }
376  }
377  }
378  // Remove width and height from style attribute
379  $attribArray['style'] = preg_replace('/(?:^|[^-])(\\s*(?:width|height)\\s*:[^;]*(?:$|;))/si', '', $attribArray['style']);
380  // Must have alt attribute
381  if (!isset($attribArray['alt'])) {
382  $attribArray['alt'] = '';
383  }
384  // Convert absolute to relative url
385  if (GeneralUtility::isFirstPartOfStr($attribArray['src'], $siteUrl)) {
386  $attribArray['src'] = substr($attribArray['src'], strlen($siteUrl));
387  }
388  $imgSplit[$k] = '<img ' . GeneralUtility::implodeAttributes($attribArray, 1, 1) . ' />';
389  }
390  }
391  }
392  return implode('', $imgSplit);
393  }
394 
403  public function TS_images_rte($value)
404  {
405  // Split content by <img> tags and traverse the resulting array for processing:
406  $imgSplit = $this->splitTags('img', $value);
407  if (count($imgSplit) > 1) {
408  $siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
409  $sitePath = str_replace(GeneralUtility::getIndpEnv('TYPO3_REQUEST_HOST'), '', $siteUrl);
410  foreach ($imgSplit as $k => $v) {
411  // Image found
412  if ($k % 2) {
413  // Get the attributes of the img tag
414  list($attribArray) = $this->get_tag_attributes($v, true);
415  $absoluteUrl = trim($attribArray['src']);
416  // Transform the src attribute into an absolute url, if it not already
417  if (strtolower(substr($absoluteUrl, 0, 4)) !== 'http') {
418  // If site is in a subpath (eg. /~user_jim/) this path needs to be removed because it will be added with $siteUrl
419  $attribArray['src'] = preg_replace('#^' . preg_quote($sitePath, '#') . '#', '', $attribArray['src']);
420  $attribArray['src'] = $siteUrl . $attribArray['src'];
421  }
422  // Must have alt attribute
423  if (!isset($attribArray['alt'])) {
424  $attribArray['alt'] = '';
425  }
426  $imgSplit[$k] = '<img ' . GeneralUtility::implodeAttributes($attribArray, 1, 1) . ' />';
427  }
428  }
429  }
430  // Return processed content:
431  return implode('', $imgSplit);
432  }
433 
442  public function TS_links_db($value)
443  {
444  $conf = [];
445  // Split content into <a> tag blocks and process:
446  $blockSplit = $this->splitIntoBlock('A', $value);
447  foreach ($blockSplit as $k => $v) {
448  // If an A-tag was found:
449  if ($k % 2) {
450  list($attribArray) = $this->get_tag_attributes($this->getFirstTag($v), true);
451  $info = $this->urlInfoForLinkTags($attribArray['href']);
452  // Check options:
453  $attribArray_copy = $attribArray;
454  unset($attribArray_copy['href']);
455  unset($attribArray_copy['target']);
456  unset($attribArray_copy['class']);
457  unset($attribArray_copy['title']);
458  unset($attribArray_copy['data-htmlarea-external']);
459  // Unset "rteerror" and "style" attributes if "rteerror" is set!
460  if ($attribArray_copy['rteerror']) {
461  unset($attribArray_copy['style']);
462  unset($attribArray_copy['rteerror']);
463  }
464  // Remove additional parameters
465  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['removeParams_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['removeParams_PostProc'])) {
466  $parameters = [
467  'conf' => &$conf,
468  'aTagParams' => &$attribArray_copy
469  ];
470  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['removeParams_PostProc'] as $objRef) {
471  $processor = GeneralUtility::getUserObj($objRef);
472  $attribArray_copy = $processor->removeParams($parameters, $this);
473  }
474  }
475  // Only if href, target, class and tile are the only attributes, we can alter the link!
476  if (empty($attribArray_copy)) {
477  // Quoting class and title attributes if they contain spaces
478  $attribArray['class'] = preg_match('/ /', $attribArray['class']) ? '"' . $attribArray['class'] . '"' : $attribArray['class'];
479  $attribArray['title'] = preg_match('/ /', $attribArray['title']) ? '"' . $attribArray['title'] . '"' : $attribArray['title'];
480  // Creating the TYPO3 pseudo-tag "<LINK>" for the link (includes href/url, target and class attributes):
481  // If data-htmlarea-external attribute is set, keep the href unchanged
482  if ($attribArray['data-htmlarea-external']) {
483  $href = $attribArray['href'];
484  } else {
485  $href = $info['url'] . ($info['query'] ? ',0,' . $info['query'] : '');
486  }
487  $typoLink = GeneralUtility::makeInstance(TypoLinkCodecService::class)->encode(['url' => $href, 'target' => $attribArray['target'], 'class' => trim($attribArray['class'], '"'), 'title' => trim($attribArray['title'], '"'), 'additionalParams' => '']);
488  $bTag = '<link ' . $typoLink . '>';
489  $eTag = '</link>';
490  // Modify parameters
491  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc'])) {
492  $parameters = [
493  'conf' => &$conf,
494  'currentBlock' => $v,
495  'url' => $href,
496  'attributes' => $attribArray
497  ];
498  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksDb_PostProc'] as $objRef) {
499  $processor = GeneralUtility::getUserObj($objRef);
500  $blockSplit[$k] = $processor->modifyParamsLinksDb($parameters, $this);
501  }
502  } else {
503  $blockSplit[$k] = $bTag . $this->TS_links_db($this->removeFirstAndLastTag($blockSplit[$k])) . $eTag;
504  }
505  } else {
506  // ... otherwise store the link as a-tag.
507  // Unsetting 'rtekeep' attribute if that had been set.
508  unset($attribArray['rtekeep']);
509  if (!$attribArray['data-htmlarea-external']) {
510  $siteURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
511  // If the url is local, remove url-prefix
512  if ($siteURL && substr($attribArray['href'], 0, strlen($siteURL)) == $siteURL) {
513  $attribArray['href'] = substr($attribArray['href'], strlen($siteURL));
514  }
515  // Check for FAL link-handler keyword
516  list($linkHandlerKeyword, $linkHandlerValue) = explode(':', $attribArray['href'], 2);
517  if ($linkHandlerKeyword === '?file') {
518  try {
519  $fileOrFolderObject = Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject(rawurldecode($linkHandlerValue));
520  if ($fileOrFolderObject instanceof Resource\FileInterface || $fileOrFolderObject instanceof Resource\Folder) {
521  $attribArray['href'] = $fileOrFolderObject->getPublicUrl();
522  }
523  } catch (Resource\Exception\ResourceDoesNotExistException $resourceDoesNotExistException) {
524  // The identifier inserted in the RTE is already gone...
525  }
526  }
527  }
528  unset($attribArray['data-htmlarea-external']);
529  $bTag = '<a ' . GeneralUtility::implodeAttributes($attribArray, 1) . '>';
530  $eTag = '</a>';
531  $blockSplit[$k] = $bTag . $this->TS_links_db($this->removeFirstAndLastTag($blockSplit[$k])) . $eTag;
532  }
533  }
534  }
535  return implode('', $blockSplit);
536  }
537 
546  public function TS_links_rte($value)
547  {
548  $conf = [];
549  $value = $this->TS_AtagToAbs($value);
550  // Split content by the TYPO3 pseudo tag "<link>":
551  $blockSplit = $this->splitIntoBlock('link', $value, 1);
552  $siteUrl = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
553  foreach ($blockSplit as $k => $v) {
554  $error = '';
555  $external = false;
556  // Block
557  if ($k % 2) {
558  // split away the first "<link" part
559  $typolink = explode(' ', substr($this->getFirstTag($v), 0, -1), 2)[1];
560  $tagCode = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typolink);
561 
562  $link_param = $tagCode['url'];
563  // Parsing the typolink data. This parsing is roughly done like in \TYPO3\CMS\Frontend\ContentObject->typoLink()
564  // Parse URL:
565  $pU = parse_url($link_param);
566  if (strstr($link_param, '@') && (!$pU['scheme'] || $pU['scheme'] == 'mailto')) {
567  // mailadr
568  $href = 'mailto:' . preg_replace('/^mailto:/i', '', $link_param);
569  } elseif ($link_param[0] === '#') {
570  // check if anchor
571  $href = $siteUrl . $link_param;
572  } else {
573  // Check for FAL link-handler keyword:
574  list($linkHandlerKeyword, $linkHandlerValue) = explode(':', trim($link_param), 2);
575  if ($linkHandlerKeyword === 'file' && strpos($link_param, 'file://') !== 0) {
576  $href = $siteUrl . '?' . $linkHandlerKeyword . ':' . rawurlencode($linkHandlerValue);
577  } else {
578  $fileChar = (int)strpos($link_param, '/');
579  $urlChar = (int)strpos($link_param, '.');
580  // Detects if a file is found in site-root.
581  list($rootFileDat) = explode('?', $link_param);
582  $rFD_fI = pathinfo($rootFileDat);
583  $fileExtension = strtolower($rFD_fI['extension']);
584  if (strpos($link_param, '/') === false && trim($rootFileDat) && (@is_file(PATH_site . $rootFileDat) || $fileExtension === 'php' || $fileExtension === 'html' || $fileExtension === 'htm')) {
585  $href = $siteUrl . $link_param;
586  } elseif (
587  (
588  $pU['scheme']
589  && !isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][$pU['scheme']])
590  )
591  || $urlChar && (!$fileChar || $urlChar < $fileChar)
592  ) {
593  // url (external): if has scheme or if a '.' comes before a '/'.
594  $href = $link_param;
595  if (!$pU['scheme']) {
596  $href = 'http://' . $href;
597  }
598  $external = true;
599  } elseif ($fileChar) {
600  // It is an internal file or folder
601  // Try to transform the href into a FAL reference
602  try {
603  $fileOrFolderObject = Resource\ResourceFactory::getInstance()->retrieveFileOrFolderObject($link_param);
604  } catch (Resource\Exception $exception) {
605  // Nothing to be done if file/folder not found or path invalid
606  $fileOrFolderObject = null;
607  }
608  if ($fileOrFolderObject instanceof Resource\Folder) {
609  // It's a folder
610  $folderIdentifier = $fileOrFolderObject->getIdentifier();
611  $href = $siteUrl . '?file:' . rawurlencode($folderIdentifier);
612  } elseif ($fileOrFolderObject instanceof Resource\FileInterface) {
613  // It's a file
614  $fileIdentifier = $fileOrFolderObject->getIdentifier();
615  $fileObject = $fileOrFolderObject->getStorage()->getFile($fileIdentifier);
616  $href = $siteUrl . '?file:' . $fileObject->getUid();
617  } else {
618  $href = $siteUrl . $link_param;
619  }
620  } else {
621  // integer or alias (alias is without slashes or periods or commas, that is 'nospace,alphanum_x,lower,unique' according to tables.php!!)
622  // Splitting the parameter by ',' and if the array counts more than 1 element it's an id/type/parameters triplet
623  $pairParts = GeneralUtility::trimExplode(',', $link_param, true);
624  $idPart = $pairParts[0];
625  $link_params_parts = explode('#', $idPart);
626  $idPart = trim($link_params_parts[0]);
627  $sectionMark = trim($link_params_parts[1]);
628  if ((string)$idPart === '') {
629  $idPart = $this->recPid;
630  }
631  // If no id or alias is given, set it to class record pid
632  // Checking if the id-parameter is an alias.
634  list($idPartR) = BackendUtility::getRecordsByField('pages', 'alias', $idPart);
635  $idPart = (int)$idPartR['uid'];
636  }
637  $page = BackendUtility::getRecord('pages', $idPart);
638  if (is_array($page)) {
639  // Page must exist...
640  $href = $siteUrl . '?id=' . $idPart . ($pairParts[2] ? $pairParts[2] : '') . ($sectionMark ? '#' . $sectionMark : '');
641  } elseif (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['tslib/class.tslib_content.php']['typolinkLinkHandler'][array_shift(explode(':', $link_param))])) {
642  $href = $link_param;
643  } else {
644  $href = $siteUrl . '?id=' . $link_param;
645  $error = 'No page found: ' . $idPart;
646  }
647  }
648  }
649  }
650  // Setting the A-tag:
651  $bTag = '<a href="' . htmlspecialchars($href) . '"'
652  . ($tagCode['target'] ? ' target="' . htmlspecialchars($tagCode['target']) . '"' : '')
653  . ($tagCode['class'] ? ' class="' . htmlspecialchars($tagCode['class']) . '"' : '')
654  . ($tagCode['title'] ? ' title="' . htmlspecialchars($tagCode['title']) . '"' : '')
655  . ($external ? ' data-htmlarea-external="1"' : '')
656  . ($error ? ' rteerror="' . htmlspecialchars($error) . '" style="background-color: yellow; border:2px red solid; color: black;"' : '') . '>';
657  $eTag = '</a>';
658  // Modify parameters
659  if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksRte_PostProc']) && is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksRte_PostProc'])) {
660  $parameters = [
661  'conf' => &$conf,
662  'currentBlock' => $v,
663  'url' => $href,
664  'tagCode' => $tagCode,
665  'external' => $external,
666  'error' => $error
667  ];
668  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_parsehtml_proc.php']['modifyParams_LinksRte_PostProc'] as $objRef) {
669  $processor = GeneralUtility::getUserObj($objRef);
670  $blockSplit[$k] = $processor->modifyParamsLinksRte($parameters, $this);
671  }
672  } else {
673  $blockSplit[$k] = $bTag . $this->TS_links_rte($this->removeFirstAndLastTag($blockSplit[$k])) . $eTag;
674  }
675  }
676  }
677  // Return content:
678  return implode('', $blockSplit);
679  }
680 
689  public function TS_transform_db($value)
690  {
691  // Safety... so forever loops are avoided (they should not occur, but an error would potentially do this...)
692  $this->TS_transform_db_safecounter--;
693  if ($this->TS_transform_db_safecounter < 0) {
694  return $value;
695  }
696  // Split the content from RTE by the occurrence of these blocks:
697  $blockSplit = $this->splitIntoBlock($this->blockElementList, $value);
698 
699  // Avoid superfluous linebreaks by transform_db after ending headListTag
700  while (count($blockSplit) > 0 && trim(end($blockSplit)) === '') {
701  array_pop($blockSplit);
702  }
703 
704  // Traverse the blocks
705  foreach ($blockSplit as $k => $v) {
706  if ($k % 2) {
707  // Inside block:
708  // Init:
709  $tag = $this->getFirstTag($v);
710  $tagName = strtolower($this->getFirstTagName($v));
711  // Process based on the tag:
712  switch ($tagName) {
713  case 'blockquote':
714  case 'dd':
715  case 'div':
716  case 'header':
717  case 'section':
718  case 'footer':
719  case 'nav':
720  case 'article':
721  case 'aside':
722  $blockSplit[$k] = $tag . $this->TS_transform_db($this->removeFirstAndLastTag($blockSplit[$k])) . '</' . $tagName . '>';
723  break;
724  case 'pre':
725  break;
726  default:
727  // usually <hx> tags and <table> tags where no other block elements are within the tags
728  // Eliminate true linebreaks inside block element tags
729  $blockSplit[$k] = preg_replace(('/[' . LF . CR . ']+/'), ' ', $this->transformStyledATags($blockSplit[$k]));
730  }
731  } else {
732  // NON-block:
733  if (trim($blockSplit[$k]) !== '') {
734  $blockSplit[$k] = str_replace('<hr/>', '<hr />', $blockSplit[$k]);
735  // Remove linebreaks preceding hr tags
736  $blockSplit[$k] = preg_replace('/[' . LF . CR . ']+<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/', '<$1$2/>', $blockSplit[$k]);
737  // Remove linebreaks following hr tags
738  $blockSplit[$k] = preg_replace('/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>[' . LF . CR . ']+/', '<$1$2/>', $blockSplit[$k]);
739  // Replace other linebreaks with space
740  $blockSplit[$k] = preg_replace('/[' . LF . CR . ']+/', ' ', $blockSplit[$k]);
741  $blockSplit[$k] = $this->divideIntoLines($blockSplit[$k]);
742  $blockSplit[$k] = $this->transformStyledATags($blockSplit[$k]);
743  } else {
744  unset($blockSplit[$k]);
745  }
746  }
747  }
748  $this->TS_transform_db_safecounter++;
749  return implode(LF, $blockSplit);
750  }
751 
758  public function transformStyledATags($value)
759  {
760  $blockSplit = $this->splitIntoBlock('A', $value);
761  foreach ($blockSplit as $k => $v) {
762  // If an A-tag was found
763  if ($k % 2) {
764  list($attribArray) = $this->get_tag_attributes($this->getFirstTag($v), true);
765  // If "style" attribute is set and rteerror is not set!
766  if ($attribArray['style'] && !$attribArray['rteerror']) {
767  $attribArray_copy['style'] = $attribArray['style'];
768  unset($attribArray['style']);
769  $bTag = '<span ' . GeneralUtility::implodeAttributes($attribArray_copy, 1) . '><a ' . GeneralUtility::implodeAttributes($attribArray, 1) . '>';
770  $eTag = '</a></span>';
771  $blockSplit[$k] = $bTag . $this->removeFirstAndLastTag($blockSplit[$k]) . $eTag;
772  }
773  }
774  }
775  return implode('', $blockSplit);
776  }
777 
786  public function TS_transform_rte($value)
787  {
788  // Split the content from database by the occurrence of the block elements
789  $blockSplit = $this->splitIntoBlock($this->blockElementList, $value);
790  // Traverse the blocks
791  foreach ($blockSplit as $k => $v) {
792  if ($k % 2) {
793  // Inside one of the blocks:
794  // Init:
795  $tag = $this->getFirstTag($v);
796  $tagName = strtolower($this->getFirstTagName($v));
797  // Based on tagname, we do transformations:
798  switch ($tagName) {
799  case 'blockquote':
800  case 'dd':
801  case 'div':
802  case 'header':
803  case 'section':
804  case 'footer':
805  case 'nav':
806  case 'article':
807  case 'aside':
808  $blockSplit[$k] = $tag . $this->TS_transform_rte($this->removeFirstAndLastTag($blockSplit[$k])) . '</' . $tagName . '>';
809  break;
810  }
811  $blockSplit[$k + 1] = preg_replace('/^[ ]*' . LF . '/', '', $blockSplit[$k + 1]);
812  } else {
813  // NON-block:
814  $nextFTN = $this->getFirstTagName($blockSplit[$k + 1]);
815  $onlyLineBreaks = (preg_match('/^[ ]*' . LF . '+[ ]*$/', $blockSplit[$k]) == 1);
816  // If the line is followed by a block or is the last line:
817  if (GeneralUtility::inList($this->blockElementList, $nextFTN) || !isset($blockSplit[$k + 1])) {
818  // If the line contains more than just linebreaks, reduce the number of trailing linebreaks by 1
819  if (!$onlyLineBreaks) {
820  $blockSplit[$k] = preg_replace('/(' . LF . '*)' . LF . '[ ]*$/', '$1', $blockSplit[$k]);
821  } else {
822  // If the line contains only linebreaks, remove the leading linebreak
823  $blockSplit[$k] = preg_replace('/^[ ]*' . LF . '/', '', $blockSplit[$k]);
824  }
825  }
826  // If $blockSplit[$k] is blank then unset the line, unless the line only contained linebreaks
827  if ((string)$blockSplit[$k] === '' && !$onlyLineBreaks) {
828  unset($blockSplit[$k]);
829  } else {
830  $blockSplit[$k] = $this->setDivTags($blockSplit[$k]);
831  }
832  }
833  }
834  return implode(LF, $blockSplit);
835  }
836 
837  /***************************************************************
838  *
839  * Generic RTE transformation, analysis and helper functions
840  *
841  **************************************************************/
842 
852  public function HTMLcleaner_db($content)
853  {
854  $keepTags = $this->getKeepTags('db');
855  // Default: remove unknown tags.
856  $keepUnknownTags = (bool)$this->procOptions['dontRemoveUnknownTags_db'];
857  return $this->HTMLcleaner($content, $keepTags, $keepUnknownTags);
858  }
859 
868  public function getKeepTags($direction = 'rte')
869  {
870  if (!is_array($this->getKeepTags_cache[$direction])) {
871  // Setting up allowed tags:
872  // Default is to get allowed/denied tags from internal array of processing options:
873  // Construct default list of tags to keep:
874  $keepTags = array_flip(GeneralUtility::trimExplode(',', $this->defaultAllowedTagsList . ',' . strtolower($this->procOptions['allowTags']), true));
875  // For tags to deny, remove them from $keepTags array:
876  $denyTags = GeneralUtility::trimExplode(',', $this->procOptions['denyTags'], true);
877  foreach ($denyTags as $dKe) {
878  unset($keepTags[$dKe]);
879  }
880  // Based on the direction of content, set further options:
881  switch ($direction) {
882  case 'rte':
883  if (!isset($this->procOptions['transformBoldAndItalicTags']) || $this->procOptions['transformBoldAndItalicTags']) {
884  // Transform bold/italics tags to strong/em
885  if (isset($keepTags['b'])) {
886  $keepTags['b'] = ['remap' => 'STRONG'];
887  }
888  if (isset($keepTags['i'])) {
889  $keepTags['i'] = ['remap' => 'EM'];
890  }
891  }
892  // Transforming keepTags array so it can be understood by the HTMLcleaner function. This basically converts the format of the array from TypoScript (having .'s) to plain multi-dimensional array.
893  list($keepTags) = $this->HTMLparserConfig($this->procOptions['HTMLparser_rte.'], $keepTags);
894  break;
895  case 'db':
896  if (!isset($this->procOptions['transformBoldAndItalicTags']) || $this->procOptions['transformBoldAndItalicTags']) {
897  // Transform strong/em back to bold/italics:
898  if (isset($keepTags['strong'])) {
899  $keepTags['strong'] = ['remap' => 'b'];
900  }
901  if (isset($keepTags['em'])) {
902  $keepTags['em'] = ['remap' => 'i'];
903  }
904  }
905  // Setting up span tags if they are allowed:
906  if (isset($keepTags['span'])) {
907  $keepTags['span'] = [
908  'allowedAttribs' => 'id,class,style,title,lang,xml:lang,dir,itemscope,itemtype,itemprop',
909  'fixAttrib' => [
910  'class' => [
911  'removeIfFalse' => 1
912  ]
913  ],
914  'rmTagIfNoAttrib' => 1
915  ];
916  if (!empty($this->allowedClasses)) {
917  $keepTags['span']['fixAttrib']['class']['list'] = $this->allowedClasses;
918  }
919  }
920  // Setting further options, getting them from the processiong options:
921  $TSc = $this->procOptions['HTMLparser_db.'];
922  if (!$TSc['globalNesting']) {
923  $TSc['globalNesting'] = 'b,i,u,a,center,font,sub,sup,strong,em,strike,span';
924  }
925  if (!$TSc['noAttrib']) {
926  $TSc['noAttrib'] = 'b,i,u,br,center,hr,sub,sup,strong,em,li,ul,ol,blockquote,strike';
927  }
928  // Transforming the array from TypoScript to regular array:
929  list($keepTags) = $this->HTMLparserConfig($TSc, $keepTags);
930  break;
931  }
932  // Caching (internally, in object memory) the result unless tagList is set:
933  $this->getKeepTags_cache[$direction] = $keepTags;
934  }
935  // Return result:
936  return $this->getKeepTags_cache[$direction];
937  }
938 
951  public function divideIntoLines($value, $count = 5, $returnArray = false)
952  {
953  // Setting configuration for processing:
954  $allowTagsOutside = GeneralUtility::trimExplode(',', strtolower($this->procOptions['allowTagsOutside'] ? 'hr,' . $this->procOptions['allowTagsOutside'] : 'hr,img'), true);
955  // Setting the third param will eliminate false end-tags. Maybe this is a good thing to do...?
956  $divSplit = $this->splitIntoBlock('p', $value, true);
957  // Returns plainly the value if there was no div/p sections in it
958  if (count($divSplit) <= 1 || $count <= 0) {
959  // Wrap hr tags with LF's
960  $newValue = preg_replace('/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', LF . '<$1$2/>' . LF, $value);
961  $newValue = str_replace(LF . LF, LF, $newValue);
962  $newValue = preg_replace('/(^' . LF . ')|(' . LF . '$)/i', '', $newValue);
963  return $newValue;
964  }
965 
966  // define which attributes are allowed on <p> tags
967  if ($this->procOptions['keepPDIVattribs']) {
968  $allowedAttributesForParagraphTags = GeneralUtility::trimExplode(',', strtolower($this->procOptions['keepPDIVattribs']), true);
969  } else {
970  $allowedAttributesForParagraphTags = [];
971  }
972 
973  // Traverse the splitted sections:
974  foreach ($divSplit as $k => $v) {
975  if ($k % 2) {
976  // Inside
977  $v = $this->removeFirstAndLastTag($v);
978  // Fetching 'sub-lines' - which will explode any further p nesting...
979  $subLines = $this->divideIntoLines($v, $count - 1, true);
980  // So, if there happened to be sub-nesting of p, this is written directly as the new content of THIS section. (This would be considered 'an error')
981  if (!is_array($subLines)) {
982  //... but if NO subsection was found, we process it as a TRUE line without erronous content:
983  $subLines = [$subLines];
984  // process break-tags, if configured for. Simply, the breaktags will here be treated like if each was a line of content...
985  if (!$this->procOptions['dontConvBRtoParagraph']) {
986  $subLines = preg_split('/<br[[:space:]]*[\\/]?>/i', $v);
987  }
988  // Traverse sublines (there is typically one, except if <br/> has been converted to lines as well!)
989  foreach ($subLines as $sk => $value) {
990  // Clear up the subline for DB.
991  $subLines[$sk] = $this->HTMLcleaner_db($subLines[$sk]);
992  // Get first tag, attributes etc:
993  $fTag = $this->getFirstTag($divSplit[$k]);
994  list($tagAttributes) = $this->get_tag_attributes($fTag);
995  // Keep attributes (lowercase)
996  $newAttribs = [];
997  if (!empty($allowedAttributesForParagraphTags)) {
998  foreach ($allowedAttributesForParagraphTags as $keepA) {
999  if (isset($tagAttributes[$keepA])) {
1000  $newAttribs[$keepA] = $tagAttributes[$keepA];
1001  }
1002  }
1003  // CLASS attribute - sort out the allowed tags
1004  if (trim($newAttribs['class']) !== '' && !empty($this->allowedClasses) && !in_array($newAttribs['class'], $this->allowedClasses)) {
1005  $classes = GeneralUtility::trimExplode(' ', $newAttribs['class'], true);
1006  $newClasses = [];
1007  foreach ($classes as $class) {
1008  if (in_array($class, $this->allowedClasses)) {
1009  $newClasses[] = $class;
1010  }
1011  }
1012  if (!empty($newClasses)) {
1013  $newAttribs['class'] = implode(' ', $newClasses);
1014  } else {
1015  unset($newAttribs['class']);
1016  }
1017  }
1018  }
1019  // Remove any line break char (10 or 13)
1020  $subLines[$sk] = str_replace([LF, CR], '', $subLines[$sk]);
1021  $subLines[$sk] = '<' . rtrim('p ' . $this->compileTagAttribs($newAttribs)) . '>' . $subLines[$sk] . '</p>';
1022  }
1023  }
1024  // Add the processed line(s)
1025  $divSplit[$k] = implode(LF, $subLines);
1026  // If it turns out the line is just blank (containing a &nbsp; possibly) then just make it pure blank.
1027  // But, prevent filtering of lines that are blank in sense above, but whose tags contain attributes.
1028  // Those attributes should have been filtered before; if they are still there they must be considered as possible content.
1029  if (trim(strip_tags($divSplit[$k])) == '&nbsp;' && !preg_match('/\\<(img)(\\s[^>]*)?\\/?>/si', $divSplit[$k]) && !preg_match('/\\<([^>]*)?( align| class| style| id| title| dir| lang| xml:lang)([^>]*)?>/si', trim($divSplit[$k]))) {
1030  $divSplit[$k] = '';
1031  }
1032  } else {
1033  // outside div:
1034  // Remove positions which are outside p tags and without content
1035  $divSplit[$k] = trim(strip_tags($divSplit[$k], '<' . implode('><', $allowTagsOutside) . '>'));
1036  // Wrap hr tags with LF's
1037  $divSplit[$k] = preg_replace('/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', LF . '<$1$2/>' . LF, $divSplit[$k]);
1038  $divSplit[$k] = str_replace(LF . LF, LF, $divSplit[$k]);
1039  $divSplit[$k] = preg_replace('/(^' . LF . ')|(' . LF . '$)/i', '', $divSplit[$k]);
1040  if ((string)$divSplit[$k] === '') {
1041  unset($divSplit[$k]);
1042  } else {
1043  // add <p> tags around the content
1044  $divSplit[$k] = str_replace(strip_tags($divSplit[$k]), '<p>' . strip_tags($divSplit[$k]) . '</p>', $divSplit[$k]);
1045  }
1046  }
1047  }
1048  // Return value:
1049  return $returnArray ? $divSplit : implode(LF, $divSplit);
1050  }
1051 
1060  public function setDivTags($value)
1061  {
1062  // First, setting configuration for the HTMLcleaner function. This will process each line between the <div>/<p> section on their way to the RTE
1063  $keepTags = $this->getKeepTags('rte');
1064  // Default: remove unknown tags.
1065  $kUknown = $this->procOptions['dontProtectUnknownTags_rte'] ? 0 : 'protect';
1066  // Divide the content into lines, based on LF:
1067  $parts = explode(LF, $value);
1068  foreach ($parts as $k => $v) {
1069  // Processing of line content:
1070  // If the line is blank, set it to &nbsp;
1071  if (trim($parts[$k]) === '') {
1072  $parts[$k] = '&nbsp;';
1073  } else {
1074  // Clean the line content:
1075  $parts[$k] = $this->HTMLcleaner($parts[$k], $keepTags, $kUknown);
1076  if (!$this->procOptions['dontConvAmpInNBSP_rte']) {
1077  $parts[$k] = str_replace('&amp;nbsp;', '&nbsp;', $parts[$k]);
1078  }
1079  }
1080  // Wrapping the line in <$dT> if not already wrapped and does not contain an hr tag
1081  if (!preg_match('/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', $parts[$k])) {
1082  $testStr = strtolower(trim($parts[$k]));
1083  if (substr($testStr, 0, 4) != '<div' || substr($testStr, -6) != '</div>') {
1084  if (substr($testStr, 0, 2) != '<p' || substr($testStr, -4) != '</p>') {
1085  // Only set p-tags if there is not already div or p tags:
1086  $parts[$k] = '<p>' . $parts[$k] . '</p>';
1087  }
1088  }
1089  }
1090  }
1091  // Implode result:
1092  return implode(LF, $parts);
1093  }
1094 
1102  public function getWHFromAttribs($attribArray)
1103  {
1104  $style = trim($attribArray['style']);
1105  $w = 0;
1106  $h = 0;
1107  if ($style) {
1108  $regex = '[[:space:]]*:[[:space:]]*([0-9]*)[[:space:]]*px';
1109  // Width
1110  $reg = [];
1111  preg_match('/width' . $regex . '/i', $style, $reg);
1112  $w = (int)$reg[1];
1113  // Height
1114  preg_match('/height' . $regex . '/i', $style, $reg);
1115  $h = (int)$reg[1];
1116  }
1117  if (!$w) {
1118  $w = $attribArray['width'];
1119  }
1120  if (!$h) {
1121  $h = $attribArray['height'];
1122  }
1123  return [(int)$w, (int)$h];
1124  }
1125 
1132  public function urlInfoForLinkTags($url)
1133  {
1134  $info = [];
1135  $url = trim($url);
1136  if (substr(strtolower($url), 0, 7) == 'mailto:') {
1137  $info['url'] = trim(substr($url, 7));
1138  $info['type'] = 'email';
1139  } elseif (strpos($url, '?file:') !== false) {
1140  $info['type'] = 'file';
1141  $info['url'] = rawurldecode(substr($url, strpos($url, '?file:') + 1));
1142  } else {
1143  $curURL = GeneralUtility::getIndpEnv('TYPO3_SITE_URL');
1144  $urlLength = strlen($url);
1145  for ($a = 0; $a < $urlLength; $a++) {
1146  if ($url[$a] != $curURL[$a]) {
1147  break;
1148  }
1149  }
1150  $info['relScriptPath'] = substr($curURL, $a);
1151  $info['relUrl'] = substr($url, $a);
1152  $info['url'] = $url;
1153  $info['type'] = 'ext';
1154  $siteUrl_parts = parse_url($url);
1155  $curUrl_parts = parse_url($curURL);
1156  // Hosts should match
1157  if ($siteUrl_parts['host'] == $curUrl_parts['host'] && (!$info['relScriptPath'] || defined('TYPO3_mainDir') && substr($info['relScriptPath'], 0, strlen(TYPO3_mainDir)) == TYPO3_mainDir)) {
1158  // If the script path seems to match or is empty (FE-EDIT)
1159  // New processing order 100502
1160  $uP = parse_url($info['relUrl']);
1161  if ($info['relUrl'] === '#' . $siteUrl_parts['fragment']) {
1162  $info['url'] = $info['relUrl'];
1163  $info['type'] = 'anchor';
1164  } elseif (!trim($uP['path']) || $uP['path'] === 'index.php') {
1165  // URL is a page (id parameter)
1166  $pp = preg_split('/^id=/', $uP['query']);
1167  $pp[1] = preg_replace('/&id=[^&]*/', '', $pp[1]);
1168  $parameters = explode('&', $pp[1]);
1169  $id = array_shift($parameters);
1170  if ($id) {
1171  $info['pageid'] = $id;
1172  $info['cElement'] = $uP['fragment'];
1173  $info['url'] = $id . ($info['cElement'] ? '#' . $info['cElement'] : '');
1174  $info['type'] = 'page';
1175  $info['query'] = $parameters[0] ? '&' . implode('&', $parameters) : '';
1176  }
1177  } else {
1178  $info['url'] = $info['relUrl'];
1179  $info['type'] = 'file';
1180  }
1181  } else {
1182  unset($info['relScriptPath']);
1183  unset($info['relUrl']);
1184  }
1185  }
1186  return $info;
1187  }
1188 
1196  public function TS_AtagToAbs($value, $dontSetRTEKEEP = false)
1197  {
1198  $blockSplit = $this->splitIntoBlock('A', $value);
1199  foreach ($blockSplit as $k => $v) {
1200  // Block
1201  if ($k % 2) {
1202  list($attribArray) = $this->get_tag_attributes($this->getFirstTag($v), true);
1203  // Checking if there is a scheme, and if not, prepend the current url.
1204  // ONLY do this if href has content - the <a> tag COULD be an anchor and if so, it should be preserved...
1205  if ($attribArray['href'] !== '') {
1206  $uP = parse_url(strtolower($attribArray['href']));
1207  if (!$uP['scheme']) {
1208  $attribArray['href'] = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . $attribArray['href'];
1209  } elseif ($uP['scheme'] != 'mailto') {
1210  $attribArray['data-htmlarea-external'] = 1;
1211  }
1212  } else {
1213  $attribArray['rtekeep'] = 1;
1214  }
1215  if (!$dontSetRTEKEEP) {
1216  $attribArray['rtekeep'] = 1;
1217  }
1218  $bTag = '<a ' . GeneralUtility::implodeAttributes($attribArray, true) . '>';
1219  $eTag = '</a>';
1220  $blockSplit[$k] = $bTag . $this->TS_AtagToAbs($this->removeFirstAndLastTag($blockSplit[$k])) . $eTag;
1221  }
1222  }
1223  return implode('', $blockSplit);
1224  }
1225 
1234  protected function applyPlainImageModeSettings($imageInfo, $attribArray)
1235  {
1236  if ($this->procOptions['plainImageMode']) {
1237  // Perform corrections to aspect ratio based on configuration
1238  switch ((string)$this->procOptions['plainImageMode']) {
1239  case 'lockDimensions':
1240  $attribArray['width'] = $imageInfo[0];
1241  $attribArray['height'] = $imageInfo[1];
1242  break;
1243  case 'lockRatioWhenSmaller':
1244  if ($attribArray['width'] > $imageInfo[0]) {
1245  $attribArray['width'] = $imageInfo[0];
1246  }
1247  case 'lockRatio':
1248  if ($imageInfo[0] > 0) {
1249  $attribArray['height'] = round($attribArray['width'] * ($imageInfo[1] / $imageInfo[0]));
1250  }
1251  break;
1252  }
1253  }
1254  return $attribArray;
1255  }
1256 
1262  protected function getLogger()
1263  {
1265  $logManager = GeneralUtility::makeInstance(LogManager::class);
1266  return $logManager->getLogger(get_class($this));
1267  }
1268 }
HTMLparserConfig($TSconfig, $keepTags=[])
Definition: HtmlParser.php:861
RTE_transform($value, $specConf, $direction= 'rte', $thisConfig=[])
static isFirstPartOfStr($str, $partStr)
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
divideIntoLines($value, $count=5, $returnArray=false)
TS_AtagToAbs($value, $dontSetRTEKEEP=false)
splitIntoBlock($tag, $content, $eliminateExtraEndTags=false)
Definition: HtmlParser.php:51
static getRecord($table, $uid, $fields= '*', $where= '', $useDeleteClause=true)
compileTagAttribs($tagAttrib, $meta=[])
Definition: HtmlParser.php:839
static getRecordsByField($theTable, $theField, $theValue, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '', $useDeleteClause=true, $queryBuilder=null)
static implodeAttributes(array $arr, $xhtmlSafe=false, $dontOmitBlankAttribs=false)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
get_tag_attributes($tag, $deHSC=false)
Definition: HtmlParser.php:248
static makeInstance($className,...$constructorArguments)
getFirstTagName($str, $preserveCase=false)
Definition: HtmlParser.php:224
static getFileAbsFileName($filename, $_=null, $_2=null)
init($elRef= '', $recPid=0)
applyPlainImageModeSettings($imageInfo, $attribArray)