Commit af49bb96 authored by Oliver Hader's avatar Oliver Hader Committed by Andreas Fernandez
Browse files

[TASK] Remove window.open inline JavaScript in backend

To reduce the amount of inline JavaScript corresponding `window.open`
occurrences are removed from TYPO3 backend user interface:

* Backend Login refresh handling
  + tests checking for arbitrary inline JavaScript were removed
* ViewPage page preview
* FormEngine
  + "open in new window" button in top right bar
  + close behavior when editing form in new window

Resolves: #91815
Releases: master
Change-Id: I39c9c0350a0bdcf22b12bb6dba8fee8e9a03e315
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/65050


Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Andreas Fernandez's avatarAndreas Fernandez <a.fernandez@scripting-base.de>
parent c5290640
...@@ -51,6 +51,7 @@ class BackendLogin { ...@@ -51,6 +51,7 @@ class BackendLogin {
useridentField: '.t3js-login-userident-field', useridentField: '.t3js-login-userident-field',
}; };
this.checkLoginRefresh();
this.checkCookieSupport(); this.checkCookieSupport();
this.checkForInterfaceCookie(); this.checkForInterfaceCookie();
this.checkDocumentReferrerSupport(); this.checkDocumentReferrerSupport();
...@@ -160,6 +161,16 @@ class BackendLogin { ...@@ -160,6 +161,16 @@ class BackendLogin {
$(this.options.errorNoCookies).addClass('hidden'); $(this.options.errorNoCookies).addClass('hidden');
} }
private checkLoginRefresh(): void {
const loginRefresh = document.querySelector(this.options.loginForm + ' input[name="loginRefresh"]');
if (loginRefresh instanceof HTMLInputElement && loginRefresh.value) {
if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.LoginRefresh) {
window.opener.TYPO3.LoginRefresh.startTask();
window.close();
}
}
}
/** /**
* Checks browser's cookie support * Checks browser's cookie support
* see http://stackoverflow.com/questions/8112634/jquery-detecting-cookies-enabled * see http://stackoverflow.com/questions/8112634/jquery-detecting-cookies-enabled
......
...@@ -47,6 +47,7 @@ use TYPO3\CMS\Core\Http\HtmlResponse; ...@@ -47,6 +47,7 @@ use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Http\RedirectResponse; use TYPO3\CMS\Core\Http\RedirectResponse;
use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory; use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Messaging\FlashMessage; use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageService; use TYPO3\CMS\Core\Messaging\FlashMessageService;
use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Core\Page\PageRenderer;
...@@ -1743,18 +1744,20 @@ class EditDocumentController ...@@ -1743,18 +1744,20 @@ class EditDocumentController
$requestUri = GeneralUtility::linkThisScript([ $requestUri = GeneralUtility::linkThisScript([
'returnUrl' => $closeUrl, 'returnUrl' => $closeUrl,
]); ]);
$aOnClick = 'vHWin=window.open('
. GeneralUtility::quoteJSvalue($requestUri) . ','
. GeneralUtility::quoteJSvalue(md5($this->R_URI))
. ',\'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1\');vHWin.focus();return false;';
$openInNewWindowButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar() $openInNewWindowButton = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar()
->makeLinkButton() ->makeLinkButton()
->setHref('#') ->setHref('#')
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.openInNewWindow')) ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.openInNewWindow'))
->setIcon($this->iconFactory->getIcon('actions-window-open', Icon::SIZE_SMALL)) ->setIcon($this->iconFactory->getIcon('actions-window-open', Icon::SIZE_SMALL))
->setOnClick($aOnClick); ->setDataAttributes([
'dispatch-action' => 'TYPO3.WindowManager.localOpen',
'dispatch-args' => GeneralUtility::jsonEncodeForHtmlAttribute([
$requestUri,
true, // switchFocus
md5($this->R_URI), // windowName,
'width=670,height=500,status=0,menubar=0,scrollbars=1,resizable=1', // windowFeatures
]),
]);
$buttonBar->addButton($openInNewWindowButton, $position, $group); $buttonBar->addButton($openInNewWindowButton, $position, $group);
} }
} }
...@@ -2555,12 +2558,7 @@ class EditDocumentController ...@@ -2555,12 +2558,7 @@ class EditDocumentController
return $GLOBALS['BE_USER']; return $GLOBALS['BE_USER'];
} }
/** protected function getLanguageService(): LanguageService
* Returns LanguageService
*
* @return LanguageService
*/
protected function getLanguageService()
{ {
return $GLOBALS['LANG']; return $GLOBALS['LANG'];
} }
......
...@@ -405,12 +405,7 @@ class LoginController ...@@ -405,12 +405,7 @@ class LoginController
if ($this->loginRefresh) { if ($this->loginRefresh) {
$formProtection->setSessionTokenFromRegistry(); $formProtection->setSessionTokenFromRegistry();
$formProtection->persistSessionToken(); $formProtection->persistSessionToken();
$this->pageRenderer->addJsInlineCode('loginRefresh', ' // triggering `TYPO3/CMS/Backend/LoginRefresh` module happens in `TYPO3/CMS/Backend/Login`
if (window.opener && window.opener.TYPO3 && window.opener.TYPO3.LoginRefresh) {
window.opener.TYPO3.LoginRefresh.startTask();
window.close();
}
');
} else { } else {
$formProtection->storeSessionTokenInRegistry(); $formProtection->storeSessionTokenInRegistry();
$this->redirectToUrl(); $this->redirectToUrl();
......
...@@ -5,10 +5,7 @@ ...@@ -5,10 +5,7 @@
<!-- TYPO3 Script ID: typo3/sysext/backend/Resources/Public/Html/Close.html --> <!-- TYPO3 Script ID: typo3/sysext/backend/Resources/Public/Html/Close.html -->
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Close</title> <title>Close</title>
<script> <script src="Close.js"></script>
self.close();
window.opener.location.reload(true);
</script>
</head> </head>
<body> <body>
</body> </body>
......
self.close();
window.opener.location.reload(true);
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
* *
* The TYPO3 project - inspiring people to share! * The TYPO3 project - inspiring people to share!
*/ */
var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","bootstrap","TYPO3/CMS/Backend/Input/Clearable"],(function(e,t,o,i){"use strict";o=__importDefault(o);return new class{constructor(){this.ready=!0,this.options={error:".t3js-login-error",errorNoCookies:".t3js-login-error-nocookies",errorNoReferrer:".t3js-login-error-noreferrer",formFields:".t3js-login-formfields",interfaceField:".t3js-login-interface-field",loginForm:"#typo3-login-form",loginUrlLink:"t3js-login-url",submitButton:".t3js-login-submit",submitHandler:null,useridentField:".t3js-login-userident-field"},this.checkCookieSupport(),this.checkForInterfaceCookie(),this.checkDocumentReferrerSupport(),this.initializeEvents(),top.location.href!==location.href&&(this.ready=!1,top.location.href=location.href),this.ready&&document.body.setAttribute("data-typo3-login-ready","true")}showLoginProcess(){this.showLoadingIndicator(),o.default(this.options.error).addClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}showLoadingIndicator(){const e=o.default(this.options.submitButton);e.html(e.data("loading-text"))}handleSubmit(e){this.showLoginProcess(),"function"==typeof this.options.submitHandler&&this.options.submitHandler(e)}interfaceSelectorChanged(){const e=new Date,t=new Date(e.getTime()+31536e6);document.cookie="typo3-login-interface="+o.default(this.options.interfaceField).val()+"; expires="+t.toUTCString()+";"}checkForInterfaceCookie(){if(o.default(this.options.interfaceField).length){const e=document.cookie.indexOf("typo3-login-interface=");if(-1!==e){let t=document.cookie.substr(e+22);t=t.substr(0,t.indexOf(";")),o.default(this.options.interfaceField).val(t)}}}checkDocumentReferrerSupport(){const e=document.getElementById(this.options.loginUrlLink);null!==e&&void 0===e.dataset.referrerCheckEnabled&&"1"!==e.dataset.referrerCheckEnabled||void 0!==TYPO3.settings&&void 0!==TYPO3.settings.ajaxUrls&&new i(TYPO3.settings.ajaxUrls.login_preflight).get().then(async e=>{!0!==(await e.resolve("application/json")).capabilities.referrer&&document.querySelectorAll(this.options.errorNoReferrer).forEach(e=>e.classList.remove("hidden"))})}showCookieWarning(){o.default(this.options.formFields).addClass("hidden"),o.default(this.options.errorNoCookies).removeClass("hidden")}hideCookieWarning(){o.default(this.options.formFields).removeClass("hidden"),o.default(this.options.errorNoCookies).addClass("hidden")}checkCookieSupport(){const e=navigator.cookieEnabled;!1===e?this.showCookieWarning():document.cookie||null!==e||(document.cookie="typo3-login-cookiecheck=1",document.cookie?document.cookie="typo3-login-cookiecheck=; expires="+new Date(0).toUTCString():this.showCookieWarning())}initializeEvents(){o.default(document).ajaxStart(this.showLoadingIndicator.bind(this)),o.default(this.options.loginForm).on("submit",this.handleSubmit.bind(this)),o.default(this.options.interfaceField).length>0&&o.default(document).on("change blur",this.options.interfaceField,this.interfaceSelectorChanged.bind(this)),document.querySelectorAll(".t3js-clearable").forEach(e=>e.clearable()),o.default(".t3js-login-news-carousel").on("slide.bs.carousel",e=>{const t=o.default(e.relatedTarget).height();o.default(e.target).find("div.active").parent().animate({height:t},500)})}}})); var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","bootstrap","TYPO3/CMS/Backend/Input/Clearable"],(function(e,o,t,i){"use strict";t=__importDefault(t);return new class{constructor(){this.ready=!0,this.options={error:".t3js-login-error",errorNoCookies:".t3js-login-error-nocookies",errorNoReferrer:".t3js-login-error-noreferrer",formFields:".t3js-login-formfields",interfaceField:".t3js-login-interface-field",loginForm:"#typo3-login-form",loginUrlLink:"t3js-login-url",submitButton:".t3js-login-submit",submitHandler:null,useridentField:".t3js-login-userident-field"},this.checkLoginRefresh(),this.checkCookieSupport(),this.checkForInterfaceCookie(),this.checkDocumentReferrerSupport(),this.initializeEvents(),top.location.href!==location.href&&(this.ready=!1,top.location.href=location.href),this.ready&&document.body.setAttribute("data-typo3-login-ready","true")}showLoginProcess(){this.showLoadingIndicator(),t.default(this.options.error).addClass("hidden"),t.default(this.options.errorNoCookies).addClass("hidden")}showLoadingIndicator(){const e=t.default(this.options.submitButton);e.html(e.data("loading-text"))}handleSubmit(e){this.showLoginProcess(),"function"==typeof this.options.submitHandler&&this.options.submitHandler(e)}interfaceSelectorChanged(){const e=new Date,o=new Date(e.getTime()+31536e6);document.cookie="typo3-login-interface="+t.default(this.options.interfaceField).val()+"; expires="+o.toUTCString()+";"}checkForInterfaceCookie(){if(t.default(this.options.interfaceField).length){const e=document.cookie.indexOf("typo3-login-interface=");if(-1!==e){let o=document.cookie.substr(e+22);o=o.substr(0,o.indexOf(";")),t.default(this.options.interfaceField).val(o)}}}checkDocumentReferrerSupport(){const e=document.getElementById(this.options.loginUrlLink);null!==e&&void 0===e.dataset.referrerCheckEnabled&&"1"!==e.dataset.referrerCheckEnabled||void 0!==TYPO3.settings&&void 0!==TYPO3.settings.ajaxUrls&&new i(TYPO3.settings.ajaxUrls.login_preflight).get().then(async e=>{!0!==(await e.resolve("application/json")).capabilities.referrer&&document.querySelectorAll(this.options.errorNoReferrer).forEach(e=>e.classList.remove("hidden"))})}showCookieWarning(){t.default(this.options.formFields).addClass("hidden"),t.default(this.options.errorNoCookies).removeClass("hidden")}hideCookieWarning(){t.default(this.options.formFields).removeClass("hidden"),t.default(this.options.errorNoCookies).addClass("hidden")}checkLoginRefresh(){const e=document.querySelector(this.options.loginForm+' input[name="loginRefresh"]');e instanceof HTMLInputElement&&e.value&&window.opener&&window.opener.TYPO3&&window.opener.TYPO3.LoginRefresh&&(window.opener.TYPO3.LoginRefresh.startTask(),window.close())}checkCookieSupport(){const e=navigator.cookieEnabled;!1===e?this.showCookieWarning():document.cookie||null!==e||(document.cookie="typo3-login-cookiecheck=1",document.cookie?document.cookie="typo3-login-cookiecheck=; expires="+new Date(0).toUTCString():this.showCookieWarning())}initializeEvents(){t.default(document).ajaxStart(this.showLoadingIndicator.bind(this)),t.default(this.options.loginForm).on("submit",this.handleSubmit.bind(this)),t.default(this.options.interfaceField).length>0&&t.default(document).on("change blur",this.options.interfaceField,this.interfaceSelectorChanged.bind(this)),document.querySelectorAll(".t3js-clearable").forEach(e=>e.clearable()),t.default(".t3js-login-news-carousel").on("slide.bs.carousel",e=>{const o=t.default(e.relatedTarget).height();t.default(e.target).find("div.active").parent().animate({height:o},500)})}}}));
\ No newline at end of file \ No newline at end of file
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
namespace TYPO3\CMS\Backend\Tests\Unit\Controller; namespace TYPO3\CMS\Backend\Tests\Unit\Controller;
use Prophecy\Argument;
use Prophecy\Prophecy\MethodProphecy;
use Prophecy\Prophecy\ObjectProphecy;
use TYPO3\CMS\Backend\Controller\LoginController; use TYPO3\CMS\Backend\Controller\LoginController;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\FormProtection\BackendFormProtection; use TYPO3\CMS\Core\FormProtection\BackendFormProtection;
...@@ -95,92 +92,6 @@ class LoginControllerTest extends UnitTestCase ...@@ -95,92 +92,6 @@ class LoginControllerTest extends UnitTestCase
); );
} }
/**
* @test
*/
public function checkRedirectAddsJavaScriptForCaseLoginRefresh(): void
{
$GLOBALS['LANG'] = $this->prophesize(LanguageService::class)->reveal();
$authenticationProphecy = $this->prophesize(BackendUserAuthentication::class);
$authenticationProphecy->getTSConfig()->willReturn([
'auth.' => [
'BE.' => [
'redirectToURL' => 'http://example.com'
]
]
]);
$authenticationProphecy->writeUC()->willReturn();
$this->prophesizeFormProtection();
$GLOBALS['BE_USER'] = $authenticationProphecy->reveal();
$this->loginControllerMock = $this->getAccessibleMock(
LoginController::class,
['isLoginInProgress', 'redirectToUrl'],
[],
'',
false
);
$GLOBALS['BE_USER']->user['uid'] = 1;
$this->loginControllerMock->method('isLoginInProgress')->willReturn(false);
$this->loginControllerMock->_set('loginRefresh', true);
/** @var ObjectProphecy|PageRenderer $pageRendererProphecy */
$pageRendererProphecy = $this->prophesize(PageRenderer::class);
/** @var MethodProphecy $inlineCodeProphecy */
$inlineCodeProphecy = $pageRendererProphecy->addJsInlineCode('loginRefresh', Argument::cetera());
$this->loginControllerMock->_set('pageRenderer', $pageRendererProphecy->reveal());
$this->loginControllerMock->_call(
'checkRedirect',
$this->prophesize(ServerRequest::class)->reveal()
);
$inlineCodeProphecy->shouldHaveBeenCalledTimes(1);
}
/**
* @test
*/
public function checkRedirectAddsJavaScriptForCaseLoginRefreshWhileLoginIsInProgress(): void
{
$GLOBALS['LANG'] = $this->prophesize(LanguageService::class)->reveal();
$authenticationProphecy = $this->prophesize(BackendUserAuthentication::class);
$authenticationProphecy->getTSConfig()->willReturn([
'auth.' => [
'BE.' => [
'redirectToURL' => 'http://example.com'
]
]
]);
$authenticationProphecy->writeUC()->willReturn();
$GLOBALS['BE_USER'] = $authenticationProphecy->reveal();
$this->prophesizeFormProtection();
$this->loginControllerMock = $this->getAccessibleMock(
LoginController::class,
['isLoginInProgress', 'redirectToUrl'],
[],
'',
false
);
$GLOBALS['BE_USER']->user['uid'] = 1;
$this->loginControllerMock->method('isLoginInProgress')->willReturn(true);
$this->loginControllerMock->_set('loginRefresh', true);
/** @var ObjectProphecy|PageRenderer $pageRendererProphecy */
$pageRendererProphecy = $this->prophesize(PageRenderer::class);
/** @var MethodProphecy $inlineCodeProphecy */
$inlineCodeProphecy = $pageRendererProphecy->addJsInlineCode('loginRefresh', Argument::cetera());
$this->loginControllerMock->_set('pageRenderer', $pageRendererProphecy->reveal());
$this->loginControllerMock->_call(
'checkRedirect',
$this->prophesize(ServerRequest::class)->reveal()
);
$inlineCodeProphecy->shouldHaveBeenCalledTimes(1);
}
/** /**
* @test * @test
*/ */
......
...@@ -136,7 +136,14 @@ class ViewModuleController ...@@ -136,7 +136,14 @@ class ViewModuleController
$buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar(); $buttonBar = $this->moduleTemplate->getDocHeaderComponent()->getButtonBar();
$showButton = $buttonBar->makeLinkButton() $showButton = $buttonBar->makeLinkButton()
->setHref($targetUrl) ->setHref($targetUrl)
->setOnClick('window.open(this.href, \'newTYPO3frontendWindow\').focus();return false;') ->setDataAttributes([
'dispatch-action' => 'TYPO3.WindowManager.localOpen',
'dispatch-args' => GeneralUtility::jsonEncodeForHtmlAttribute([
$targetUrl,
true, // switchFocus
'newTYPO3frontendWindow', // windowName,
]),
])
->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage')) ->setTitle($this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.showPage'))
->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL)); ->setIcon($this->iconFactory->getIcon('actions-view-page', Icon::SIZE_SMALL));
$buttonBar->addButton($showButton); $buttonBar->addButton($showButton);
......
Supports Markdown
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