17 use Psr\Log\LoggerAwareInterface;
18 use Psr\Log\LoggerAwareTrait;
29 use TYPO3\HtmlSanitizer\Builder\BuilderInterface;
46 'blockElementList' =>
'Using $blockElementList of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
47 'recPid' =>
'Using $recPid of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
48 'elRef' =>
'Using $elRef of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
49 'tsConfig' =>
'Using $tsConfig of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
50 'procOptions' =>
'Using $procOptions of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
51 'TS_transform_db_safecounter' =>
'Using $TS_transform_db_safecounter of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
52 'getKeepTags_cache' =>
'Using $getKeepTags_cache of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
53 'allowedClasses' =>
'Using $allowedClasses of class RteHtmlParser from the outside is discouraged, as this property is only used for internal storage.',
57 'TS_images_db' =>
'Using TS_images_db() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
58 'TS_links_db' =>
'Using TS_links_db() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
59 'TS_transform_db' =>
'Using TS_transform_db() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
60 'TS_transform_rte' =>
'Using TS_transform_rte() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
61 'HTMLcleaner_db' =>
'Using HTMLcleaner_db() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
62 'getKeepTags' =>
'Using getKeepTags() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
63 'divideIntoLines' =>
'Using divideIntoLines() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
64 'setDivTags' =>
'Using setDivTags() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
65 'getWHFromAttribs' =>
'Using getWHFromAttribs() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
66 'urlInfoForLinkTags' =>
'Using urlInfoForLinkTags() of class RteHtmlParser from the outside is discouraged, as this method is not in use anymore and will be removed.',
67 'TS_AtagToAbs' =>
'Using TS_AtagToAbs() of class RteHtmlParser from the outside is discouraged, as this method is only available for internal purposes.',
74 protected $blockElementList =
'DIV,TABLE,BLOCKQUOTE,PRE,UL,OL,H1,H2,H3,H4,H5,H6,ADDRESS,DL,DD,HEADER,SECTION,FOOTER,NAV,ARTICLE,ASIDE';
80 protected $defaultAllowedTagsList =
'b,i,u,a,img,br,div,center,pre,font,hr,sub,sup,p,strong,em,li,ul,ol,blockquote,strike,span,abbr,acronym,dfn';
197 public function RTE_transform($value, $_ =
null, $direction =
'rte', $thisConfig = [])
199 $this->tsConfig = $thisConfig;
200 $this->procOptions = (array)$thisConfig[
'proc.'];
201 if (isset($this->procOptions[
'allowedClasses.'])) {
202 $this->allowedClasses = (array)$this->procOptions[
'allowedClasses.'];
204 $this->allowedClasses = GeneralUtility::trimExplode(
',', $this->procOptions[
'allowedClasses'] ??
'',
true);
208 if (!empty($this->procOptions[
'blockElementList'])) {
209 $this->blockElementList = $this->procOptions[
'blockElementList'];
213 if (isset($this->procOptions[
'allowAttributes.'])) {
214 $this->allowedAttributesForParagraphTags = $this->procOptions[
'allowAttributes.'];
215 } elseif (isset($this->procOptions[
'keepPDIVattribs'])) {
216 trigger_error(
'HTML parsing option "keepPDIVattribs" will not be evaluated anymore in TYPO3 v10.0. Use "allowedAttributes" instead.', E_USER_DEPRECATED);
217 $this->allowedAttributesForParagraphTags = GeneralUtility::trimExplode(
',', strtolower($this->procOptions[
'keepPDIVattribs']),
true);
220 if (isset($this->procOptions[
'allowTagsOutside'])) {
221 if (!isset($this->procOptions[
'allowTagsOutside.'])) {
222 $this->allowedTagsOutsideOfParagraphs = GeneralUtility::trimExplode(
',', strtolower($this->procOptions[
'allowTagsOutside']),
true);
224 $this->allowedTagsOutsideOfParagraphs = (array)$this->procOptions[
'allowTagsOutside.'];
229 if ((
string)$this->procOptions[
'overruleMode'] !==
'') {
230 $modes = GeneralUtility::trimExplode(
',', $this->procOptions[
'overruleMode']);
232 $modes = [$this->procOptions[
'mode']];
242 foreach ($modes as $cmd) {
243 if ($direction ===
'db') {
245 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'transformation'][$cmd])) {
246 $_procObj = GeneralUtility::makeInstance(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'transformation'][$cmd]);
247 $_procObj->pObj = $this;
248 $_procObj->transformationKey = $cmd;
249 $value = $_procObj->transform_db($value, $this);
253 case 'detectbrokenlinks':
262 case 'css_transform':
264 $value = str_replace(
'<p></p>',
'<p> </p>', $value);
266 $value = preg_replace(
'/<p> <\/p>$/',
'<p> </p>' .
'<p> </p>', $value);
273 } elseif ($direction ===
'rte') {
275 if (!empty(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'transformation'][$cmd])) {
276 $_procObj = GeneralUtility::makeInstance(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'transformation'][$cmd]);
277 $_procObj->pObj = $this;
278 $value = $_procObj->transform_rte($value, $this);
282 case 'detectbrokenlinks':
291 case 'css_transform':
301 if ($direction ===
'db') {
303 $value = $this->
htmlSanitize($value, $this->procOptions[
'HTMLparser_db.'] ?? []);
324 $modeList = implode(
',', $modes);
327 $modeList = str_replace(
'default',
'detectbrokenlinks,css_transform,ts_images,ts_links', $modeList);
330 $modes = array_unique(GeneralUtility::trimExplode(
',', $modeList,
true));
332 if ($direction ===
'rte') {
333 $modes = array_reverse($modes);
352 if (!empty($this->procOptions[$configurationDirective])) {
353 list($keepTags, $keepNonMatchedTags, $hscMode, $additionalConfiguration) = $this->
HTMLparserConfig($this->procOptions[$configurationDirective .
'.']);
354 $content = $this->
HTMLcleaner($content, $keepTags, $keepNonMatchedTags, $hscMode, $additionalConfiguration);
379 if (count($imgSplit) > 1) {
380 $siteUrl = GeneralUtility::getIndpEnv(
'TYPO3_SITE_URL');
381 $sitePath = str_replace(GeneralUtility::getIndpEnv(
'TYPO3_REQUEST_HOST'),
'', $siteUrl);
385 $magicImageService = GeneralUtility::makeInstance(Resource\Service\MagicImageService::class);
386 $magicImageService->setMagicImageMaximumDimensions($this->tsConfig);
387 foreach ($imgSplit as $k => $v) {
393 $absoluteUrl = trim($attribArray[
'src']);
395 $pI = pathinfo($absoluteUrl);
396 if ($sitePath && !$pI[
'scheme'] && GeneralUtility::isFirstPartOfStr($absoluteUrl, $sitePath)) {
398 $absoluteUrl = substr($absoluteUrl, strlen($sitePath));
399 $absoluteUrl = $siteUrl . $absoluteUrl;
403 if ($imgTagDimensions[0]) {
404 $attribArray[
'width'] = $imgTagDimensions[0];
406 if ($imgTagDimensions[1]) {
407 $attribArray[
'height'] = $imgTagDimensions[1];
409 $originalImageFile =
null;
410 if ($attribArray[
'data-htmlarea-file-uid']) {
414 $originalImageFile = $resourceFactory->getFileObject((
int)$attribArray[
'data-htmlarea-file-uid']);
415 }
catch (Resource\Exception\FileDoesNotExistException $fileDoesNotExistException) {
417 $message = sprintf(
'Could not find file with uid "%s"', $attribArray[
'data-htmlarea-file-uid']);
418 $this->logger->error($message);
421 if ($originalImageFile instanceof Resource\File) {
423 if ($absoluteUrl == $originalImageFile->getPublicUrl() || $absoluteUrl == $siteUrl . $originalImageFile->getPublicUrl()) {
425 if ($this->procOptions[
'plainImageMode']) {
429 $originalImageFile->getProperty(
'width'),
430 $originalImageFile->getProperty(
'height')
432 if (!$imageInfo[0] || !$imageInfo[1]) {
433 $filePath = $originalImageFile->getForLocalProcessing(
false);
434 $imageInfoObject = GeneralUtility::makeInstance(ImageInfo::class, $filePath);
436 $imageInfoObject->getWidth(),
437 $imageInfoObject->getHeight()
444 $imageConfiguration = [
445 'width' => $imgTagDimensions[0],
446 'height' => $imgTagDimensions[1]
448 $magicImage = $magicImageService->createMagicImage($originalImageFile, $imageConfiguration);
449 $attribArray[
'width'] = $magicImage->getProperty(
'width');
450 $attribArray[
'height'] = $magicImage->getProperty(
'height');
451 $attribArray[
'src'] = $magicImage->getPublicUrl();
453 } elseif (!GeneralUtility::isFirstPartOfStr($absoluteUrl, $siteUrl) && !$this->procOptions[
'dontFetchExtPictures'] && TYPO3_MODE ===
'BE') {
456 $externalFile = GeneralUtility::getUrl($absoluteUrl);
458 $pU = parse_url($absoluteUrl);
459 $pI = pathinfo($pU[
'path']);
460 $extension = strtolower($pI[
'extension']);
461 if ($extension ===
'jpg' || $extension ===
'jpeg' || $extension ===
'gif' || $extension ===
'png') {
462 $fileName = GeneralUtility::shortMD5($absoluteUrl) .
'.' . $pI[
'extension'];
464 list($table, $field) = explode(
':', $this->elRef);
466 $folder =
$GLOBALS[
'BE_USER']->getDefaultUploadFolder($this->recPid, $table, $field);
468 $fileObject = $folder->createFile($fileName)->setContents($externalFile);
469 $imageConfiguration = [
470 'width' => $attribArray[
'width'],
471 'height' => $attribArray[
'height']
473 $magicImage = $magicImageService->createMagicImage($fileObject, $imageConfiguration);
474 $attribArray[
'width'] = $magicImage->getProperty(
'width');
475 $attribArray[
'height'] = $magicImage->getProperty(
'height');
476 $attribArray[
'data-htmlarea-file-uid'] = $fileObject->getUid();
477 $attribArray[
'src'] = $magicImage->getPublicUrl();
480 } elseif (GeneralUtility::isFirstPartOfStr($absoluteUrl, $siteUrl)) {
484 $path = rawurldecode(substr($absoluteUrl, strlen($siteUrl)));
486 $filepath = GeneralUtility::getFileAbsFileName($path);
488 if ($filepath && @is_file($filepath)) {
490 if ($this->procOptions[
'plainImageMode']) {
493 $imageInfoObject = GeneralUtility::makeInstance(ImageInfo::class, $filepath);
495 $imageInfoObject->getWidth(),
496 $imageInfoObject->getHeight()
502 $fileOrFolderObject = $resourceFactory->retrieveFileOrFolderObject($path);
503 if ($fileOrFolderObject instanceof Resource\FileInterface) {
504 $fileIdentifier = $fileOrFolderObject->getIdentifier();
506 $fileObject = $fileOrFolderObject->getStorage()->getFile($fileIdentifier);
508 $attribArray[
'data-htmlarea-file-uid'] = $fileObject->getUid();
510 }
catch (Resource\Exception\ResourceDoesNotExistException $resourceDoesNotExistException) {
516 $attribArray[
'style'] = preg_replace(
'/(?:^|[^-])(\\s*(?:width|height)\\s*:[^;]*(?:$|;))/si',
'', $attribArray[
'style']);
518 if (!isset($attribArray[
'alt'])) {
519 $attribArray[
'alt'] =
'';
522 if (GeneralUtility::isFirstPartOfStr($attribArray[
'src'], $siteUrl)) {
523 $attribArray[
'src'] = substr($attribArray[
'src'], strlen($siteUrl));
525 $imgSplit[$k] =
'<img ' . GeneralUtility::implodeAttributes($attribArray,
true,
true) .
' />';
529 return implode(
'', $imgSplit);
544 if (count($imgSplit) > 1) {
545 $siteUrl = GeneralUtility::getIndpEnv(
'TYPO3_SITE_URL');
546 $sitePath = str_replace(GeneralUtility::getIndpEnv(
'TYPO3_REQUEST_HOST'),
'', $siteUrl);
547 foreach ($imgSplit as $k => $v) {
552 $absoluteUrl = trim($attribArray[
'src']);
554 if (stripos($absoluteUrl,
'http') !== 0) {
556 $attribArray[
'src'] = preg_replace(
'#^' . preg_quote($sitePath,
'#') .
'#',
'', $attribArray[
'src']);
557 $attribArray[
'src'] = $siteUrl . $attribArray[
'src'];
560 if (!isset($attribArray[
'alt'])) {
561 $attribArray[
'alt'] =
'';
563 $imgSplit[$k] =
'<img ' . GeneralUtility::implodeAttributes($attribArray,
true,
true) .
' />';
568 return implode(
'', $imgSplit);
585 foreach ($blockSplit as $k => $v) {
590 if (!isset($tagAttributes[
'href'])) {
593 $linkService = GeneralUtility::makeInstance(LinkService::class);
594 $linkInformation = $linkService->resolve($tagAttributes[
'href'] ??
'');
597 if (isset(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'modifyParams_LinksDb_PostProc'])) {
598 trigger_error(
'The hook "t3lib/class.t3lib_parsehtml_proc.php->modifyParams_LinksDb_PostProc" will be removed in TYPO3 v10.0, use LinkService syntax to modify links to be stored in the database.', E_USER_DEPRECATED);
600 'currentBlock' => $v,
601 'linkInformation' => $linkInformation,
602 'url' => $linkInformation[
'href'],
603 'attributes' => $tagAttributes
605 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'modifyParams_LinksDb_PostProc'] ?? [] as $className) {
606 $processor = GeneralUtility::makeInstance($className);
607 $blockSplit[$k] = $processor->modifyParamsLinksDb($parameters, $this);
612 $tagAttributes[
'href'] = $linkService->asString($linkInformation);
613 }
catch (UnknownLinkHandlerException $e) {
614 $tagAttributes[
'href'] = $linkInformation[
'href'] ?? $tagAttributes[
'href'];
617 $blockSplit[$k] =
'<a ' . GeneralUtility::implodeAttributes($tagAttributes,
true) .
'>'
622 return implode(
'', $blockSplit);
637 public function TS_links_rte($value, $internallyCalledFromCore =
null)
639 if ($internallyCalledFromCore ===
null) {
640 trigger_error(
'RteHtmlParser->TS_links_rte() will be removed in TYPO3 v10.0, use TS_AtagToAbs() directly and do not use <link> syntax anymore.', E_USER_DEPRECATED);
642 $hasLinkTags =
false;
646 foreach ($blockSplit as $k => $v) {
651 $typoLinkData = explode(
' ', substr($this->
getFirstTag($v), 0, -1), 2)[1];
652 $tagCode = GeneralUtility::makeInstance(TypoLinkCodecService::class)->decode($typoLinkData);
655 $linkService = GeneralUtility::makeInstance(LinkService::class);
656 $linkInformation = $linkService->resolve($tagCode[
'url']);
659 $href = $linkService->asString($linkInformation);
660 }
catch (UnknownLinkHandlerException $e) {
665 if (is_array(
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'modifyParams_LinksRte_PostProc'] ??
false)) {
666 trigger_error(
'The hook "t3lib/class.t3lib_parsehtml_proc.php->modifyParams_LinksRte_PostProc" will be removed in TYPO3 v10.0, use the link service to properly use .', E_USER_DEPRECATED);
672 if (!is_array($pageRecord)) {
673 $error =
'Page with ID ' . $linkInformation[
'pageuid'] .
' not found';
677 'currentBlock' => $v,
679 'tagCode' => $tagCode,
683 foreach (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SC_OPTIONS'][
't3lib/class.t3lib_parsehtml_proc.php'][
'modifyParams_LinksRte_PostProc'] as $className) {
684 $processor = GeneralUtility::makeInstance($className);
685 $blockSplit[$k] = $processor->modifyParamsLinksRte($parameters, $this);
688 $anchorAttributes = [
690 'target' => $tagCode[
'target'],
691 'class' => $tagCode[
'class'],
692 'title' => $tagCode[
'title']
696 $blockSplit[$k] =
'<a ' . GeneralUtility::implodeAttributes($anchorAttributes,
true) .
'>'
703 trigger_error(
'Content with <link> syntax was found, update your content to use the t3:// syntax, and migrate your content via the upgrade wizard in the install tool.', E_USER_DEPRECATED);
705 return implode(
'', $blockSplit);
719 $this->TS_transform_db_safecounter--;
720 if ($this->TS_transform_db_safecounter < 0) {
727 while (count($blockSplit) > 0 && trim(end($blockSplit)) ===
'') {
728 array_pop($blockSplit);
732 foreach ($blockSplit as $k => $v) {
756 $blockSplit[$k] = preg_replace(
'/[' . LF .
']+/',
' ', $blockSplit[$k]);
760 if (trim($blockSplit[$k]) !==
'') {
761 $blockSplit[$k] = str_replace(
'<hr/>',
'<hr />', $blockSplit[$k]);
763 $blockSplit[$k] = preg_replace(
'/[' . LF .
']+<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/',
'<$1$2/>', $blockSplit[$k]);
765 $blockSplit[$k] = preg_replace(
'/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>[' . LF .
']+/',
'<$1$2/>', $blockSplit[$k]);
767 $blockSplit[$k] = preg_replace(
'/[' . LF .
']+/',
' ', $blockSplit[$k]);
770 (
string)$blockSplit[$k],
772 $this->procOptions[
'HTMLparser_db.'][
'keepNonMatchedTags'] ??
'',
773 (
int)($this->procOptions[
'HTMLparser_db.'][
'htmlSpecialChars'] ?? 0)
777 unset($blockSplit[$k]);
781 $this->TS_transform_db_safecounter++;
782 return implode(LF, $blockSplit);
798 trigger_error(
'RteHtmlParser->transformStyledATags() will be removed in TYPO3 v10.0. TYPO3 can handle style attribute in anchor tags properly since TYPO3 v8 LTS.', E_USER_DEPRECATED);
800 foreach ($blockSplit as $k => $v) {
805 if ($attribArray[
'style'] && !$attribArray[
'rteerror']) {
806 $attribArray_copy[
'style'] = $attribArray[
'style'];
807 unset($attribArray[
'style']);
808 $bTag =
'<span ' . GeneralUtility::implodeAttributes($attribArray_copy,
true) .
'><a ' . GeneralUtility::implodeAttributes($attribArray,
true) .
'>';
809 $eTag =
'</a></span>';
814 return implode(
'', $blockSplit);
830 foreach ($blockSplit as $k => $v) {
850 $blockSplit[$k + 1] = preg_replace(
'/^[ ]*' . LF .
'/',
'', $blockSplit[$k + 1]);
854 $onlyLineBreaks = (preg_match(
'/^[ ]*' . LF .
'+[ ]*$/', $blockSplit[$k]) == 1);
856 if (GeneralUtility::inList($this->blockElementList, $nextFTN) || !isset($blockSplit[$k + 1])) {
858 if (!$onlyLineBreaks) {
859 $blockSplit[$k] = preg_replace(
'/(' . LF .
'*)' . LF .
'[ ]*$/',
'$1', $blockSplit[$k]);
862 $blockSplit[$k] = preg_replace(
'/^[ ]*' . LF .
'/',
'', $blockSplit[$k]);
866 if ((
string)$blockSplit[$k] ===
'' && !$onlyLineBreaks) {
867 unset($blockSplit[$k]);
869 $blockSplit[$k] = $this->
setDivTags($blockSplit[$k]);
873 return implode(LF, $blockSplit);
895 if (isset($this->procOptions[
'dontRemoveUnknownTags_db'])) {
896 trigger_error(
'HTMLParser option "dontRemoveUnknownTags_db" will not be evaluted anymore in TYPO3 v10.0. Remove its usages.', E_USER_DEPRECATED);
898 $keepUnknownTags = (bool)($this->procOptions[
'dontRemoveUnknownTags_db'] ??
false);
912 if (!isset($this->getKeepTags_cache[$direction]) || !is_array($this->getKeepTags_cache[$direction])) {
916 if (isset($this->procOptions[
'allowTags.']) && is_array($this->procOptions[
'allowTags.'])) {
917 $keepTags = implode(
',', $this->procOptions[
'allowTags.']);
919 $keepTags = $this->procOptions[
'allowTags'] ??
'';
921 $keepTags = array_flip(GeneralUtility::trimExplode(
',', $this->defaultAllowedTagsList .
',' . strtolower($keepTags),
true));
923 $denyTags = GeneralUtility::trimExplode(
',', $this->procOptions[
'denyTags'] ??
'',
true);
924 foreach ($denyTags as $dKe) {
925 unset($keepTags[$dKe]);
928 switch ($direction) {
932 list($keepTags) = $this->
HTMLparserConfig($this->procOptions[
'HTMLparser_rte.'] ?? [], $keepTags);
936 if (isset($keepTags[
'span'])) {
937 $keepTags[
'span'] = [
938 'allowedAttribs' =>
'id,class,style,title,lang,xml:lang,dir,itemscope,itemtype,itemprop',
944 'rmTagIfNoAttrib' => 1
946 if (!empty($this->allowedClasses)) {
951 $TSc = $this->procOptions[
'HTMLparser_db.'] ?? [];
952 if (empty($TSc[
'globalNesting'])) {
953 $TSc[
'globalNesting'] =
'b,i,u,a,center,font,sub,sup,strong,em,strike,span';
955 if (empty($TSc[
'noAttrib'])) {
956 $TSc[
'noAttrib'] =
'b,i,u,br,center,hr,sub,sup,strong,em,li,ul,ol,blockquote,strike';
963 $this->getKeepTags_cache[$direction] = $keepTags;
966 return $this->getKeepTags_cache[$direction];
981 protected function divideIntoLines($value, $count = 5, $returnArray =
false)
986 if (count($paragraphBlocks) <= 1 || $count <= 0) {
991 foreach ($paragraphBlocks as $k => $v) {
998 if (is_array($subLines)) {
999 $paragraphBlocks[$k] = implode(LF, $subLines);
1007 if (trim(strip_tags($paragraphBlocks[$k])) ===
' ' && !preg_match(
'/\\<(img)(\\s[^>]*)?\\/?>/si', $paragraphBlocks[$k]) && !preg_match(
'/\\<([^>]*)?( align| class| style| id| title| dir| lang| xml:lang)([^>]*)?>/si', trim($paragraphBlocks[$k]))) {
1008 $paragraphBlocks[$k] =
'';
1013 $paragraphBlocks[$k] = trim(strip_tags($paragraphBlocks[$k],
'<' . implode(
'><', $this->allowedTagsOutsideOfParagraphs) .
'>'));
1015 if ((
string)$paragraphBlocks[$k] ===
'') {
1016 unset($paragraphBlocks[$k]);
1019 $paragraphBlocks[$k] = str_replace(strip_tags($paragraphBlocks[$k]),
'<p>' . strip_tags($paragraphBlocks[$k]) .
'</p>', $paragraphBlocks[$k]);
1023 return $returnArray ? $paragraphBlocks : implode(LF, $paragraphBlocks);
1039 $parts = explode(LF, $value);
1040 foreach ($parts as $k => $v) {
1043 if (trim($parts[$k]) ===
'') {
1044 $parts[$k] =
' ';
1047 $parts[$k] = $this->
HTMLcleaner($parts[$k], $keepTags,
'protect');
1050 $parts[$k] = str_replace(
'&nbsp;',
' ', $parts[$k]);
1053 if (!preg_match(
'/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', $parts[$k])) {
1054 $testStr = strtolower(trim($parts[$k]));
1055 if (strpos($testStr,
'<div') !== 0 || substr($testStr, -6) !==
'</div>') {
1056 if (strpos($testStr,
'<p') !== 0 || substr($testStr, -4) !==
'</p>') {
1058 $parts[$k] =
'<p>' . $parts[$k] .
'</p>';
1064 return implode(LF, $parts);
1086 if (!empty($this->allowedAttributesForParagraphTags)) {
1089 $tagAttributes = array_intersect_key($tagAttributes, array_flip($this->allowedAttributesForParagraphTags));
1092 if (isset($tagAttributes[
'class']) && trim($tagAttributes[
'class']) !==
'' && !empty($this->allowedClasses) && !in_array($tagAttributes[
'class'], $this->allowedClasses,
true)) {
1093 $classes = GeneralUtility::trimExplode(
' ', $tagAttributes[
'class'],
true);
1094 $classes = array_intersect($classes, $this->allowedClasses);
1095 if (!empty($classes)) {
1096 $tagAttributes[
'class'] = implode(
' ', $classes);
1098 unset($tagAttributes[
'class']);
1102 $tagAttributes = [];
1105 $content = str_replace(LF,
'', $content);
1107 $content =
'<' . rtrim(
'p ' . $this->
compileTagAttribs($tagAttributes)) .
'>' . $content .
'</p>';
1119 $content = preg_replace(
'/<(hr)(\\s[^>\\/]*)?[[:space:]]*\\/?>/i', LF .
'<$1$2/>' . LF, $content);
1120 $content = str_replace(LF . LF, LF, $content);
1121 $content = preg_replace(
'/(^' . LF .
')|(' . LF .
'$)/i',
'', $content);
1134 $style = trim($attribArray[
'style']);
1138 $regex =
'[[:space:]]*:[[:space:]]*([0-9]*)[[:space:]]*px';
1141 preg_match(
'/width' . $regex .
'/i', $style, $reg);
1144 preg_match(
'/height' . $regex .
'/i', $style, $reg);
1148 $w = $attribArray[
'width'];
1151 $h = $attribArray[
'height'];
1153 return [(int)$w, (
int)$h];
1168 if (strpos(strtolower($url),
'mailto:') === 0) {
1169 $info[
'url'] = trim(substr($url, 7));
1170 $info[
'type'] =
'email';
1171 } elseif (strpos($url,
'?file:') !==
false) {
1172 $info[
'type'] =
'file';
1173 $info[
'url'] = rawurldecode(substr($url, strpos($url,
'?file:') + 1));
1175 $curURL = GeneralUtility::getIndpEnv(
'TYPO3_SITE_URL');
1176 $urlLength = strlen($url);
1178 for (; $a < $urlLength; $a++) {
1179 if ($url[$a] != $curURL[$a]) {
1183 $info[
'relScriptPath'] = substr($curURL, $a);
1184 $info[
'relUrl'] = substr($url, $a);
1185 $info[
'url'] = $url;
1186 $info[
'type'] =
'ext';
1187 $siteUrl_parts = parse_url($url);
1188 $curUrl_parts = parse_url($curURL);
1190 if ($siteUrl_parts[
'host'] == $curUrl_parts[
'host'] && (!$info[
'relScriptPath'] || defined(
'TYPO3_mainDir') && strpos($info[
'relScriptPath'], TYPO3_mainDir) === 0)) {
1193 $uP = parse_url($info[
'relUrl']);
1194 if ($info[
'relUrl'] ===
'#' . $siteUrl_parts[
'fragment']) {
1195 $info[
'url'] = $info[
'relUrl'];
1196 $info[
'type'] =
'anchor';
1197 } elseif (!trim($uP[
'path']) || $uP[
'path'] ===
'index.php') {
1199 $pp = preg_split(
'/^id=/', $uP[
'query']);
1200 $pp[1] = preg_replace(
'/&id=[^&]*/',
'', $pp[1]);
1201 $parameters = explode(
'&', $pp[1]);
1202 $id = array_shift($parameters);
1204 $info[
'pageid'] = $id;
1205 $info[
'cElement'] = $uP[
'fragment'];
1206 $info[
'url'] = $id . ($info[
'cElement'] ?
'#' . $info[
'cElement'] :
'');
1207 $info[
'type'] =
'page';
1208 $info[
'query'] = $parameters[0] ?
'&' . implode(
'&', $parameters) :
'';
1211 $info[
'url'] = $info[
'relUrl'];
1212 $info[
'type'] =
'file';
1215 unset($info[
'relScriptPath']);
1216 unset($info[
'relUrl']);
1230 if (func_num_args() > 1) {
1231 trigger_error(
'Second argument of RteHtmlParser->TS_AtagToAbs() is not in use and will be removed in TYPO3 v10.0, however the argument in the callers code can be removed without side-effects.', E_USER_DEPRECATED);
1234 foreach ($blockSplit as $k => $v) {
1240 if (($attribArray[
'href'] ??
'') !==
'') {
1241 $uP = parse_url(strtolower($attribArray[
'href']));
1242 if (!$uP[
'scheme']) {
1243 $attribArray[
'href'] = GeneralUtility::getIndpEnv(
'TYPO3_SITE_URL') . $attribArray[
'href'];
1246 $bTag =
'<a ' . GeneralUtility::implodeAttributes($attribArray,
true) .
'>';
1251 return implode(
'', $blockSplit);
1264 if ($this->procOptions[
'plainImageMode']) {
1266 switch ((
string)$this->procOptions[
'plainImageMode']) {
1267 case 'lockDimensions':
1268 $attribArray[
'width'] = $imageInfo[0];
1269 $attribArray[
'height'] = $imageInfo[1];
1271 case 'lockRatioWhenSmaller':
1272 if ($attribArray[
'width'] > $imageInfo[0]) {
1273 $attribArray[
'width'] = $imageInfo[0];
1275 if ($imageInfo[0] > 0) {
1276 $attribArray[
'height'] = round($attribArray[
'width'] * ($imageInfo[1] / $imageInfo[0]));
1280 if ($imageInfo[0] > 0) {
1281 $attribArray[
'height'] = round($attribArray[
'width'] * ($imageInfo[1] / $imageInfo[0]));
1286 return $attribArray;
1301 return str_replace(CR,
'', $content);
1319 return str_replace(LF, CRLF, $content);
1333 $linkService = GeneralUtility::makeInstance(LinkService::class);
1334 foreach ($blocks as $position => $value) {
1335 if ($position % 2 === 0) {
1339 if (empty($attributes[
'href'])) {
1342 $hrefInformation = $linkService->resolve($attributes[
'href']);
1345 if (!is_array($pageRecord)) {
1347 $attributes[
'data-rte-error'] =
'Page with ID ' . $hrefInformation[
'pageuid'] .
' not found';
1351 $blocks[$position] =
1352 '<a ' . GeneralUtility::implodeAttributes($attributes,
true,
true) .
'>'
1356 return implode(
'', $blocks);
1369 foreach ($blocks as $position => $value) {
1370 if ($position % 2 === 0) {
1374 if (empty($attributes[
'href'])) {
1379 unset($attributes[
'data-rte-error']);
1380 if (isset($attributes[
'style'])) {
1381 $attributes[
'style'] = trim(str_replace(
'background-color: yellow; border:2px red solid; color: black;',
'', $attributes[
'style']));
1382 if (empty($attributes[
'style'])) {
1383 unset($attributes[
'style']);
1386 $blocks[$position] =
1387 '<a ' . GeneralUtility::implodeAttributes($attributes,
true,
true) .
'>'
1391 return implode(
'', $blocks);
1394 protected function htmlSanitize(
string $content, array $configuration): string
1396 $features = GeneralUtility::makeInstance(Features::class);
1399 if (array_key_exists(
'htmlSanitize', $configuration) && empty($configuration[
'htmlSanitize'])
1400 || !$features->isFeatureEnabled(
'security.backend.htmlSanitizeRte')
1405 $build = $configuration[
'htmlSanitize.'][
'build'] ??
'default';
1406 if (class_exists($build) && is_a($build, BuilderInterface::class,
true)) {
1407 $builder = GeneralUtility::makeInstance($build);
1409 $factory = GeneralUtility::makeInstance(SanitizerBuilderFactory::class);
1410 $builder = $factory->build($build);
1412 $sanitizer = $builder->build();
1413 $initiator = GeneralUtility::makeInstance(SanitizerInitiator::class, get_class($this));
1414 return $sanitizer->sanitize($content, $initiator);