Commit 847ab12c authored by Oliver Hader's avatar Oliver Hader Committed by Benni Mack
Browse files

[TASK] Extract default inline frontend JavaScript

This change extracts dynamically added frontend JavaScript `openPic`
and `linkTo_UnCryptMailto` which are extracted to static file
`EXT:frontend/Resources/Public/JavaScript/default_frontend.js`.
Mentioned file is loaded automatically as async script element.

As a result, view-helper `<f:uri.email>` became obsolete, which
is deprecated and considered to be removed with TYPO3 v12.0.

Resolves: #95041
Releases: master
Change-Id: Ic1a17e11e5acfab4a1e3fe60d921a0ddc647f83e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/70827

Reviewed-by: Markus Klein's avatarMarkus Klein <markus.klein@typo3.org>
Reviewed-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: Markus Klein's avatarMarkus Klein <markus.klein@typo3.org>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Georg Ringer's avatarGeorg Ringer <georg.ringer@gmail.com>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent a49baa5c
......@@ -61,6 +61,10 @@
"../typo3/sysext/form/Resources/Public/JavaScript/*",
"form/Resources/Public/TypeScript/*"
],
"TYPO3/CMS/Frontend/*": [
"../typo3/sysext/frontend/Resources/Public/JavaScript/*",
"frontend/Resources/Public/TypeScript/*"
],
"TYPO3/CMS/Impexp/*": [
"../typo3/sysext/impexp/Resources/Public/JavaScript/*",
"impexp/Resources/Public/TypeScript/*"
......
......@@ -41,6 +41,7 @@ class DefaultSanitizerBuilder extends CommonBuilder
// + starting with `t3://`
$isTypo3Uri = new Behavior\RegExpAttrValue('#^t3://#');
// + TYPO3 spam protected email address using JavaScript
// @deprecated Only used in f:uri.email view-helper, which is deprecated and will be removed in TYPO3 v12.0
$isSpamProtectedEmailUri = new Behavior\RegExpAttrValue('#^javascript:linkTo_UnCryptMailto#');
// extends common attributes for TYPO3-specific URIs
......@@ -59,19 +60,6 @@ class DefaultSanitizerBuilder extends CommonBuilder
return GeneralUtility::makeInstance(Sanitizer::class, $visitor);
}
protected function createBasicTags(): array
{
/** @var Behavior\Tag[] $tags */
$tags = parent::createBasicTags();
// `... onclick="openPic(...)"` used in ContentObjectRenderer and AbstractMenuContentObject
// @todo get rid of `onclick` since it conflicts with Content-Security-Policy
$tags['a']->addAttrs(
(new Behavior\Attr('onclick'))
->addValues(new Behavior\RegExpAttrValue('#^openPic\(#'))
);
return $tags;
}
protected function createBehavior(): Behavior
{
return parent::createBehavior()
......
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Page;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
/**
* Trait used to add default JavaScript in frontend rendering context
* while considering TypoScript setting `config.removeDefaultJS` as well.
*
* @internal only to be used in EXT:frontend and TYPO3 Core, not part of TYPO3 Core API.
*/
trait DefaultJavaScriptAssetTrait
{
protected string $defaultFrontendJavaScriptFile = 'EXT:frontend/Resources/Public/JavaScript/default_frontend.js';
protected function addDefaultFrontendJavaScript(): void
{
// `config.removeDefaultJS = 1` - remove default JavaScript, no action required
if ($this->shallRemoveDefaultFrontendJavaScript()) {
return;
}
$filePath = $this->defaultFrontendJavaScriptFile;
/** @var AssetCollector $collector */
$collector = GeneralUtility::makeInstance(AssetCollector::class);
// `config.removeDefaultJS = external` - persist JavaScript to `typo3temp/assets/`
if ($this->shallExportDefaultFrontendJavaScript()) {
$source = file_get_contents(GeneralUtility::getFileAbsFileName($filePath));
$filePath = GeneralUtility::writeJavaScriptContentToTemporaryFile((string)$source);
}
$collector->addJavaScript('frontend-default', $filePath, ['async' => 'async']);
}
protected function shallRemoveDefaultFrontendJavaScript(): bool
{
/** @var ?TypoScriptFrontendController $frontendController */
$frontendController = $GLOBALS['TSFE'] ?? null;
return ($frontendController->config['config']['removeDefaultJS'] ?? '') === '1';
}
protected function shallExportDefaultFrontendJavaScript(): bool
{
/** @var ?TypoScriptFrontendController $frontendController */
$frontendController = $GLOBALS['TSFE'] ?? null;
return ($frontendController->config['config']['removeDefaultJS'] ?? '') === 'external';
}
}
.. include:: ../../Includes.txt
=========================================================
Deprecation: #95041 - Deprecate <f:uri.email> view-helper
=========================================================
See :issue:`95041`
Description
===========
Fluid view-helper :html:<f:uri.email email="{email}">` was used in combination
with :ts:`config.spamProtectEmailAddresses` settings during frontend rendering
and returned corresponding :js:`javascript:linkTo_UnCryptMailto(...)` inline
JavaScript URI. In case spam-protections is not configured, this view-helper
just passed through the given email address.
In favor of allowing more content security policy scenarios, `javascript:`
URI is not used anymore per default. As a result, :html:`<f:uri.email>`
view-helper became obsolete. The view-helper will be removed with TYPO3 v12.0.
Impact
======
Using :html:<f:uri.email>` view-helper triggers a deprecation level error.
Affected Installations
======================
All projects using :html:<f:uri.email email="{email}">` or
:html:`{email -> f:uri.email}` view-helper invocations in their Fluid templates.
Migration
=========
In case :ts:`config.spamProtectEmailAddresses` is used, make use of
:html:`<f.link.email email="{email}">` view-helper which returns the
complete :html:`<a>` tag like this:
... code-block:: html
<a href="#" data-mailto-token="ocknvq,hqqBdct0vnf"
data-mailto-vector="1">user(at)my.example(dot)com</a>
In case spam-protected is not used or not useful (e.g. in backend user
interface), view-helper invocation can be omitted completely.
.. index:: Fluid, Frontend, FullyScanned, ext:fluid
......@@ -113,6 +113,7 @@ class DefaultSanitizerBuilderTest extends FunctionalTestCase
],
'#056' => [
// config.spamProtectEmailAddresses = [n]
// @deprecated Only used in f:uri.email view-helper, which is deprecated and will be removed in TYPO3 v12.0
'<a href="javascript:linkTo_UnCryptMailto(%27ocknvq%2CkphqBrtczku%5C%2Fmkghgt0fg%27);">email(at)domain.tld</a>',
'<a href="javascript:linkTo_UnCryptMailto(%27ocknvq%2CkphqBrtczku%5C%2Fmkghgt0fg%27);">email(at)domain.tld</a>',
],
......@@ -123,16 +124,6 @@ class DefaultSanitizerBuilderTest extends FunctionalTestCase
// decodes those entities, which is good to have normalized attr values
'<a href="mailto:some.body@test.typo3.org">some.body(at)test.typo3(dot)org</a>',
],
'#058' => [
// `... onclick="openPic(...)"` used in ContentObjectRenderer and AbstractMenuContentObject
'<a href="/" target="FEopenLink" onclick="openPic(\'\/\',\'FEopenLink\',\'width=200,height=300\');return false;">Link</a>',
'<a href="/" target="FEopenLink" onclick="openPic(\'\/\',\'FEopenLink\',\'width=200,height=300\');return false;">Link</a>'
],
'#059' => [
// `... onclick="openPic(...)"` used in ContentObjectRenderer and AbstractMenuContentObject
'<a href="/index.php?eID=tx_cms_showpic" onclick="openPic(\'\/index.php?eID=tx_cms_showpic\u0026file=77\u0026md5=45a4b6287f68a61cf617a470e853d857461bc1d2\u0026parameters%5B0%5D=W10%3D\',\'thePicture\',\'width=1200,height=1799,status=0,menubar=0,=\'); return false;" target="thePicture"><img src="/logo.png"></a>',
'<a href="/index.php?eID=tx_cms_showpic" onclick="openPic(\'\/index.php?eID=tx_cms_showpic\u0026file=77\u0026md5=45a4b6287f68a61cf617a470e853d857461bc1d2\u0026parameters%5B0%5D=W10%3D\',\'thePicture\',\'width=1200,height=1799,status=0,menubar=0,=\'); return false;" target="thePicture"><img src="/logo.png"></a>'
],
'#090' => [
'<p data-bool><span data-bool><strong data-bool>value</strong></span></p>',
'<p data-bool><span data-bool><strong data-bool>value</strong></span></p>'
......
......@@ -16,6 +16,7 @@
namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
/**
......@@ -36,7 +37,7 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
*
* Output::
*
* <a href="javascript:linkTo_UnCryptMailto('ocknvq,hqqBdct0vnf');">foo(at)bar.tld</a>
* <a href="#" data-mailto-token="ocknvq,hqqBdct0vnf" data-mailto-vector="1">foo(at)bar.tld</a>
*
* Depending on `spamProtectEmailAddresses`_ setting.
*
......@@ -80,10 +81,11 @@ class EmailViewHelper extends AbstractTagBasedViewHelper
public function render()
{
$email = $this->arguments['email'];
if (ApplicationType::fromRequest($this->renderingContext->getRequest())->isFrontend()) {
[$linkHref, $linkText] = $GLOBALS['TSFE']->cObj->getMailTo($email, '');
$escapeSpecialCharacters = !isset($GLOBALS['TSFE']->spamProtectEmailAddresses) || $GLOBALS['TSFE']->spamProtectEmailAddresses !== 'ascii';
/** @var TypoScriptFrontendController $frontend */
$frontend = $GLOBALS['TSFE'];
[$linkHref, $linkText, $attributes] = $frontend->cObj->getMailTo($email, '');
$escapeSpecialCharacters = !isset($frontend->spamProtectEmailAddresses) || $frontend->spamProtectEmailAddresses !== 'ascii';
} else {
$linkHref = 'mailto:' . $email;
$linkText = htmlspecialchars($email);
......@@ -96,6 +98,7 @@ class EmailViewHelper extends AbstractTagBasedViewHelper
$this->tag->setContent($linkText);
$this->tag->addAttribute('href', $linkHref, $escapeSpecialCharacters);
$this->tag->forceClosingTag(true);
$this->tag->addAttributes($attributes ?? []);
return $this->tag->render();
}
}
......@@ -16,6 +16,8 @@
namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
......@@ -38,6 +40,8 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
* javascript:linkTo_UnCryptMailto('ocknvq,hqqBdct0vnf');
*
* Depending on `spamProtectEmailAddresses`_ setting.
*
* @deprecated Will be removed in TYPO3 v12.0
*/
class EmailViewHelper extends AbstractViewHelper
{
......@@ -60,10 +64,21 @@ class EmailViewHelper extends AbstractViewHelper
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
trigger_error('f:uri.email view-helper is deprecated an will be removed in TYPO3 v12.0', E_USER_DEPRECATED);
$email = $arguments['email'];
if (ApplicationType::fromRequest($renderingContext->getRequest())->isFrontend()) {
$emailParts = $GLOBALS['TSFE']->cObj->getMailTo($email, $email);
return reset($emailParts);
/** @var TypoScriptFrontendController $frontend */
$frontend = $GLOBALS['TSFE'];
[$linkHref, $linkText, $attributes] = $frontend->cObj->getMailTo($email, $email);
if (isset($attributes['data-mailto-token']) && isset($attributes['data-mailto-vector'])) {
$linkHref = sprintf(
'javascript:linkTo_UnCryptMailto(%s,%d);',
rawurlencode(GeneralUtility::quoteJSvalue($attributes['data-mailto-token'])),
-(int)$attributes['data-mailto-vector']
);
}
return $linkHref;
}
return 'mailto:' . $email;
}
......
......@@ -64,7 +64,7 @@ class EmailViewHelperTest extends FunctionalTestCase
'Plain email with spam protection' => [
'<f:link.email email="some@email.tld" />',
1,
'<a href="javascript:linkTo_UnCryptMailto(%27nbjmup%2BtpnfAfnbjm%5C%2Fume%27);">some(at)email.tld</a>',
'<a href="#" data-mailto-token="nbjmup+tpnfAfnbjm/ume" data-mailto-vector="1">some(at)email.tld</a>',
],
'Plain email with ascii spam protection' => [
'<f:link.email email="some@email.tld" />',
......@@ -79,7 +79,7 @@ class EmailViewHelperTest extends FunctionalTestCase
'Susceptible email with spam protection' => [
'<f:link.email email="\"><script>alert(\'email\')</script>" />',
1,
'<a href="javascript:linkTo_UnCryptMailto(%27nbjmup%2B%5Cu0022%5Cu003E%5Cu003Ctdsjqu%5Cu003Ebmfsu%28%5Cu0027fnbjm%5Cu0027%29%5Cu003C0tdsjqu%5Cu003E%27);">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>',
'<a href="#" data-mailto-token="nbjmup+&quot;&gt;&lt;tdsjqu&gt;bmfsu(\'fnbjm\')&lt;0tdsjqu&gt;" data-mailto-vector="1">&quot;&gt;&lt;script&gt;alert(\'email\')&lt;/script&gt;</a>',
],
'Susceptible email with ascii spam protection' => [
'<f:link.email email="\"><script>alert(\'email\')</script>" />',
......
......@@ -52,7 +52,7 @@ class EmailViewHelperTest extends FunctionalTestCase
'Plain email with spam protection' => [
'<f:uri.email email="some@email.tld" />',
1,
'javascript:linkTo_UnCryptMailto(%27nbjmup%2BtpnfAfnbjm%5C%2Fume%27);',
'javascript:linkTo_UnCryptMailto(%27nbjmup%2BtpnfAfnbjm%5C%2Fume%27,-1);',
],
'Susceptible email' => [
'<f:uri.email email="\"><script>alert(\'email\')</script>" />',
......@@ -62,7 +62,7 @@ class EmailViewHelperTest extends FunctionalTestCase
'Susceptible email with spam protection' => [
'<f:uri.email email="\"><script>alert(\'email\')</script>" />',
1,
'javascript:linkTo_UnCryptMailto(%27nbjmup%2B%5Cu0022%5Cu003E%5Cu003Ctdsjqu%5Cu003Ebmfsu%28%5Cu0027fnbjm%5Cu0027%29%5Cu003C0tdsjqu%5Cu003E%27);',
'javascript:linkTo_UnCryptMailto(%27nbjmup%2B%5Cu0022%5Cu003E%5Cu003Ctdsjqu%5Cu003Ebmfsu%28%5Cu0027fnbjm%5Cu0027%29%5Cu003C0tdsjqu%5Cu003E%27,-1);',
],
];
}
......
......@@ -44,7 +44,7 @@ use TYPO3\CMS\Core\Imaging\ImageManipulation\CropVariantCollection;
use TYPO3\CMS\Core\LinkHandling\Exception\UnknownLinkHandlerException;
use TYPO3\CMS\Core\LinkHandling\LinkService;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Core\Page\DefaultJavaScriptAssetTrait;
use TYPO3\CMS\Core\Resource\Exception;
use TYPO3\CMS\Core\Resource\Exception\InvalidPathException;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
......@@ -96,6 +96,7 @@ use TYPO3\HtmlSanitizer\Builder\BuilderInterface;
class ContentObjectRenderer implements LoggerAwareInterface
{
use LoggerAwareTrait;
use DefaultJavaScriptAssetTrait;
/**
* @var ContainerInterface|null
......@@ -1135,16 +1136,23 @@ class ContentObjectRenderer implements LoggerAwareInterface
$paramString .= htmlspecialchars((string)$paramKey) . '=' . htmlspecialchars((string)$paramValue) . ',';
}
$onClick = 'openPic('
. GeneralUtility::quoteJSvalue($this->getTypoScriptFrontendController()->baseUrlWrap($url)) . ','
. '\'' . ($newWindow ? md5((string)$url) : 'thePicture') . '\','
. GeneralUtility::quoteJSvalue(rtrim($paramString, ',')) . '); return false;';
$a1 = '<a href="' . htmlspecialchars((string)$url) . '"'
. ' onclick="' . htmlspecialchars($onClick) . '"'
. ($target !== '' ? ' target="' . htmlspecialchars($target) . '"' : '')
. $this->getTypoScriptFrontendController()->ATagParams . '>';
$attrs = [
'href' => (string)$url,
'data-window-url' => $this->getTypoScriptFrontendController()->baseUrlWrap($url),
'data-window-target' => $newWindow ? md5((string)$url) : 'thePicture',
'data-window-features' => rtrim($paramString, ','),
];
if ($target !== '') {
$attrs['target'] = $target;
}
$a1 = sprintf(
'<a %s%s>',
GeneralUtility::implodeAttributes($attrs, true),
$this->getTypoScriptFrontendController()->ATagParams
);
$a2 = '</a>';
GeneralUtility::makeInstance(AssetCollector::class)->addInlineJavaScript('openPic', 'function openPic(url, winName, winParams) { var theWindow = window.open(url, winName, winParams); if (theWindow) { theWindow.focus(); } }');
$this->addDefaultFrontendJavaScript();
} else {
$conf['linkParams.']['directImageLink'] = (bool)$conf['directImageLink'];
$conf['linkParams.']['parameter'] = $url;
......@@ -3967,8 +3975,10 @@ class ContentObjectRenderer implements LoggerAwareInterface
$parts[0] = substr($textpieces[$i], 0, $len);
$parts[1] = substr($textpieces[$i], $len);
$linktxt = (string)preg_replace('/\\?.*/', '', $parts[0]);
[$mailToUrl, $linktxt] = $this->getMailTo($parts[0], $linktxt);
[$mailToUrl, $linktxt, $attributes] = $this->getMailTo($parts[0], $linktxt);
$mailToUrl = $tsfe->spamProtectEmailAddresses === 'ascii' ? $mailToUrl : htmlspecialchars($mailToUrl);
$mailtoAttrs = GeneralUtility::implodeAttributes($attributes ?? [], true);
$aTagParams .= ($mailtoAttrs !== '' ? ' ' . $mailtoAttrs : '');
$res = '<a href="' . $mailToUrl . '"' . $aTagParams . '>';
$wrap = (string)$this->stdWrapValue('wrap', $conf);
if ((string)$conf['ATagBeforeWrap'] !== '') {
......@@ -4793,8 +4803,15 @@ class ContentObjectRenderer implements LoggerAwareInterface
// We need to backup the URL because ATagParams might call typolink again and change the last URL.
$url = $this->lastTypoLinkUrl;
$linkResultAttrs = array_filter(
$linkedResult->getAttributes(),
function (string $name): bool {
return !in_array($name, ['href', 'target']);
},
ARRAY_FILTER_USE_KEY
);
$finalTagParts = [
'aTagParams' => $this->getATagParams($conf),
'aTagParams' => rtrim($this->getATagParams($conf) . ' ' . GeneralUtility::implodeAttributes($linkResultAttrs, true)),
'url' => $url,
'TYPE' => $linkedResult->getType()
];
......@@ -4864,13 +4881,14 @@ class ContentObjectRenderer implements LoggerAwareInterface
}
if ($JSwindowParams) {
$onClick = 'openPic(' . GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($finalTagParts['url']))
. ',' . GeneralUtility::quoteJSvalue($this->lastTypoLinkResult->getTarget()) . ','
. GeneralUtility::quoteJSvalue($JSwindowParams)
. ');return false;';
$tagAttributes['onclick'] = htmlspecialchars($onClick);
GeneralUtility::makeInstance(AssetCollector::class)->addInlineJavaScript('openPic', 'function openPic(url, winName, winParams) { var theWindow = window.open(url, winName, winParams); if (theWindow) { theWindow.focus(); } }');
$this->lastTypoLinkResult = $this->lastTypoLinkResult->withAttribute('onclick', $onClick);
$JSwindowAttrs = [
'data-window-url' => $tsfe->baseUrlWrap($finalTagParts['url']),
'data-window-target' => $this->lastTypoLinkResult->getTarget(),
'data-window-features' => $JSwindowParams,
];
$tagAttributes = array_merge($tagAttributes, array_map('htmlspecialchars', $JSwindowAttrs));
$this->lastTypoLinkResult = $this->lastTypoLinkResult->withAttributes($JSwindowAttrs);
$this->addDefaultFrontendJavaScript();
}
if (!empty($resolvedLinkParameters['class'])) {
......@@ -5127,9 +5145,14 @@ class ContentObjectRenderer implements LoggerAwareInterface
* The function uses spamProtectEmailAddresses for encoding the mailto statement.
* If spamProtectEmailAddresses is disabled, it'll just return a string like "mailto:user@example.tld".
*
* Returns an array with three items (numeric index)
* #0: $mailToUrl (string), ready to be inserted into the href attribute of the <a> tag
* #1: $linktxt (string), content between starting and ending `<a>` tag
* #2: $attributes (array<string, string>), additional attributes for `<a>` tag
*
* @param string $mailAddress Email address
* @param string $linktxt Link text, default will be the email address.
* @return array A numerical array with two elements: 1) $mailToUrl, string ready to be inserted into the href attribute of the <a> tag, b) $linktxt: The string between starting and ending <a> tag.
* @return array{0: string, 1: string, 2: array<string, string>} A numerical array with three items
*/
public function getMailTo($mailAddress, $linktxt)
{
......@@ -5140,6 +5163,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
$originalMailToUrl = 'mailto:' . $mailAddress;
$mailToUrl = $this->processUrl(UrlProcessorInterface::CONTEXT_MAIL, $originalMailToUrl);
$attributes = [];
// no processing happened, therefore, the default processing kicks in
if ($mailToUrl === $originalMailToUrl) {
......@@ -5147,8 +5171,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
if ($tsfe->spamProtectEmailAddresses) {
$mailToUrl = $this->encryptEmail($mailToUrl, $tsfe->spamProtectEmailAddresses);
if ($tsfe->spamProtectEmailAddresses !== 'ascii') {
$encodedForJsAndHref = rawurlencode(GeneralUtility::quoteJSvalue($mailToUrl));
$mailToUrl = 'javascript:linkTo_UnCryptMailto(' . $encodedForJsAndHref . ');';
$attributes = [
'data-mailto-token' => $mailToUrl,
'data-mailto-vector' => (int)$tsfe->spamProtectEmailAddresses,
];
$mailToUrl = '#';
}
$atLabel = trim($tsfe->config['config']['spamProtectEmailAddresses_atSubst']) ?: '(at)';
$spamProtectedMailAddress = str_replace('@', $atLabel, htmlspecialchars($mailAddress));
......@@ -5162,10 +5189,11 @@ class ContentObjectRenderer implements LoggerAwareInterface
}
}
$linktxt = str_ireplace($mailAddress, $spamProtectedMailAddress, $linktxt);
$this->addDefaultFrontendJavaScript();
}
}
return [$mailToUrl, $linktxt];
return [$mailToUrl, $linktxt, $attributes];
}
/**
......
......@@ -22,7 +22,7 @@ use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\LanguageAspect;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Domain\Repository\PageRepository;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Core\Page\DefaultJavaScriptAssetTrait;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\TimeTracker\TimeTracker;
use TYPO3\CMS\Core\Type\Bitmask\PageTranslationVisibility;
......@@ -44,6 +44,8 @@ use TYPO3\CMS\Frontend\Typolink\PageLinkBuilder;
*/
abstract class AbstractMenuContentObject
{
use DefaultJavaScriptAssetTrait;
/**
* tells you which menu number this is. This is important when getting data from the setup
*
......@@ -1229,16 +1231,17 @@ abstract class AbstractMenuContentObject
}
/**
* Creates the URL, target and onclick values for the menu item link. Returns them in an array as key/value pairs for <A>-tag attributes
* Creates the URL, target and data-window-* attributes for the menu item link. Returns them in an array as key/value pairs for <A>-tag attributes
* This function doesn't care about the url, because if we let the url be redirected, it will be logged in the stat!!!
*
* @param int $key Pointer to a key in the $this->menuArr array where the value for that key represents the menu item we are linking to (page record)
* @param string $altTarget Alternative target
* @param string $typeOverride Alternative type
* @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and onClick)
* @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and data-window-* attrs)
*/
protected function link($key, $altTarget, $typeOverride)
{
$attrs = [];
$runtimeCache = $this->getRuntimeCache();
$MP_var = $this->getMPvar($key);
$cacheId = 'menu-generated-links-' . md5($key . $altTarget . $typeOverride . $MP_var . json_encode($this->menuArr[$key]));
......@@ -1339,17 +1342,17 @@ abstract class AbstractMenuContentObject
$LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
}
}
// OnClick open in windows.
$onClick = '';
// opens URL in new window
if ($this->mconf['JSWindow'] ?? false) {
$conf = $this->mconf['JSWindow.'];
$url = $LD['totalURL'];
$LD['totalURL'] = '#';
$onClick = 'openPic('
. GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($url)) . ','
. '\'' . ($conf['newWindow'] ? md5($url) : 'theNewPage') . '\','
. GeneralUtility::quoteJSvalue($conf['params']) . '); return false;';
GeneralUtility::makeInstance(AssetCollector::class)->addInlineJavaScript('openPic', 'function openPic(url, winName, winParams) { var theWindow = window.open(url, winName, winParams); if (theWindow) { theWindow.focus(); } }');
$attrs['data-window-url'] = $tsfe->baseUrlWrap($url);
$attrs['data-window-target'] = $conf['newWindow'] ? md5($url) : 'theNewPage';
if (!empty($conf['params'])) {
$attrs['data-window-features'] = $conf['params'];
}
$this->addDefaultFrontendJavaScript();
}
// look for type and popup
// following settings are valid in field target:
......@@ -1367,25 +1370,20 @@ abstract class AbstractMenuContentObject
}
// Open in popup window?
if (($matches[3] ?? false) && ($matches[4] ?? false)) {
$target = $LD['target'] ?? 'FEopenLink';
$JSparamWH = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : '');
$onClick = 'openPic('
. GeneralUtility::quoteJSvalue($tsfe->baseUrlWrap($LD['totalURL']))
. ',' . GeneralUtility::quoteJSvalue($target) . ',' . GeneralUtility::quoteJSvalue($JSparamWH) . ');vHWin.focus();return false;';
GeneralUtility::makeInstance(AssetCollector::class)->addInlineJavaScript('openPic', 'function openPic(url, winName, winParams) { var theWindow = window.open(url, winName, winParams); if (theWindow) { theWindow.focus(); } }');
$attrs['data-window-url'] = $tsfe->baseUrlWrap($LD['totalURL']);
$attrs['data-window-target'] = $LD['target'] ?? 'FEopenLink';
$attrs['data-window-features'] = 'width=' . $matches[3] . ',height=' . $matches[4] . ($matches[5] ? ',' . substr($matches[5], 1) : '');
$LD['target'] = '';
$this->addDefaultFrontendJavaScript();
}
}
// out:
$list = [];
// Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites)
// as URL if the calculated value is empty. The problem is that no link is generated with a blank URL
// and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated.
$list['HREF'] = (string)$LD['totalURL'] !== '' ? $LD['totalURL'] : $tsfe->baseUrl;
$list['TARGET'] = $LD['target'];
$list['onClick'] = $onClick;
$runtimeCache->set($cacheId, $list);
return $list;
$attrs['HREF'] = (string)$LD['totalURL'] !== '' ? $LD['totalURL'] : $tsfe->baseUrl;
$attrs['TARGET'] = $LD['target'];
$runtimeCache->set($cacheId, $attrs);
return $attrs;
}
/**
......
......@@ -696,44 +696,7 @@ class RequestHandler implements RequestHandlerInterface
);
$controller->INTincScript_loadJSCode();
$scriptJsCode = '';
if ($controller->spamProtectEmailAddresses && $controller->spamProtectEmailAddresses !== 'ascii') {
$scriptJsCode = '
/* decrypt helper function */
function decryptCharcode(n,start,end,offset) {
n = n + offset;
if (offset > 0 && n > end) {
n = start + (n - end - 1);
} else if (offset < 0 && n < start) {
n = end - (start - n - 1);
}
return String.fromCharCode(n);
}
/* decrypt string */
function decryptString(enc,offset) {
var dec = "";
var len = enc.length;
for(var i=0; i < len; i++) {
var n = enc.charCodeAt(i);
if (n >= 0x2B && n <= 0x3A) {
dec += decryptCharcode(n,0x2B,0x3A,offset); /* 0-9 . , - + / : */
} else if (n >= 0x40 && n <= 0x5A) {
dec += decryptCharcode(n,0x40,0x5A,offset); /* A-Z @ */
} else if (n >= 0x61 && n <= 0x7A) {
dec += decryptCharcode(n,0x61,0x7A,offset); /* a-z */
} else {
dec += enc.charAt(i);