Skip to content
Snippets Groups Projects
Commit 72bbfb36 authored by Oliver Hader's avatar Oliver Hader Committed by Andreas Fernandez
Browse files

[TASK] Extract Indexed Search inline event handling

Indexed Search pagination still uses `onclick="..."` inline
event handlers. For being compatible with content security policy,
the corresponding handlers are added as inline JavaScript, embedded
with a CSP nonce value.

Resolves: #101271
Releases: main, 12.4
Change-Id: Ic1202faf8be2d1d9e84b599f62e8c9a53aeef2fa
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/79857


Tested-by: default avatarcore-ci <typo3@b13.com>
Tested-by: default avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: default avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent a40a4d21
Branches
Tags
No related merge requests found
......@@ -17,6 +17,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\IndexedSearch\ViewHelpers;
use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\MathUtility;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
......@@ -30,16 +31,18 @@ use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;
*/
final class PageBrowsingViewHelper extends AbstractTagBasedViewHelper
{
/**
* @var string
*/
protected static $prefixId = 'tx_indexedsearch';
protected static string $prefixId = 'tx_indexedsearch';
/**
* @var string
*/
protected $tagName = 'ul';
public function __construct(private readonly AssetCollector $assetCollector)
{
parent::__construct();
}
public function initializeArguments(): void
{
$this->registerArgument('maximumNumberOfResultPages', 'int', '', true);
......@@ -124,11 +127,41 @@ final class PageBrowsingViewHelper extends AbstractTagBasedViewHelper
*/
protected function makecurrentPageSelector_link($str, $p, $freeIndexUid)
{
$onclick = 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId . '_pointer') . ').value=' . GeneralUtility::quoteJSvalue((string)$p) . ';';
if ($freeIndexUid !== null) {
$onclick .= 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId . '_freeIndexUid') . ').value=' . GeneralUtility::quoteJSvalue($freeIndexUid) . ';';
$this->providePageSelectorJavaScript();
return sprintf(
'<a %s>%s</a>',
GeneralUtility::implodeAttributes([
'href' => '#',
'class' => 'tx-indexedsearch-page-selector',
'data-prefix' => self::$prefixId,
'data-pointer' => $p,
'data-freeIndexUid' => $freeIndexUid,
], true),
htmlspecialchars($str)
);
}
private function providePageSelectorJavaScript(): void
{
if ($this->assetCollector->hasInlineJavaScript(self::class)) {
return;
}
$onclick .= 'document.getElementById(' . GeneralUtility::quoteJSvalue(self::$prefixId) . ').submit();return false;';
return '<a href="#" onclick="' . htmlspecialchars($onclick) . '">' . htmlspecialchars($str) . '</a>';
$this->assetCollector->addInlineJavaScript(
self::class,
implode(' ', [
"document.addEventListener('click', (evt) => {",
'evt.preventDefault();',
"if (!evt.target.classList.contains('tx-indexedsearch-page-selector')) {",
'return;',
'}',
'var data = evt.target.dataset;',
"document.getElementById(data.prefix + '_pointer').value = data.pointer;",
"document.getElementById(data.prefix + '_freeIndexUid').value = data.freeIndexUid;",
'document.getElementById(data.prefix).submit();',
'});',
]),
[],
['useNonce' => true],
);
}
}
<?php
declare(strict_types=1);
namespace TYPO3\CMS\Backend;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Directive;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Mutation;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationCollection;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\MutationMode;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\Scope;
use TYPO3\CMS\Core\Security\ContentSecurityPolicy\SourceKeyword;
use TYPO3\CMS\Core\Type\Map;
/**
* \TYPO3\CMS\IndexedSearch\ViewHelpers\PageBrowsingViewHelper::providePageSelectorJavaScript
* adds inline JavaScript for the corresponding pagination feature - this configuration adds
* the required CSP `nonce-proxy` for the frontend scope.
*/
return Map::fromEntries([
Scope::frontend(),
new MutationCollection(
new Mutation(MutationMode::Extend, Directive::ScriptSrc, SourceKeyword::nonceProxy),
),
]);
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment