From 8a8f141337c75b626eddbdef75493e6385cdf88f Mon Sep 17 00:00:00 2001 From: Oliver Hader <oliver@typo3.org> Date: Fri, 19 Nov 2021 09:53:45 +0100 Subject: [PATCH] [TASK] Avoid inline JavaScript in backend update signals BackendUtility::getUpdateSignalCode() returned plain inline JavaScript code which got replaced by new BackendUtility::getUpdateSignalDetails(). The new function is capable of handling inline JavaScript code (for backward compatibility reasons) and HTML markup using corresponding `<typo3-immediate-action>` custom element. OpendocsToolbarItem::updateNumberOfOpenDocsHook has been adjusted as well to use the script-less implementation. Resolves: #96002 Resolves: #96012 Releases: master, 11.5 Change-Id: I248db207600ae5c7452471a093f84f3802050c59 Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/72206 Tested-by: core-ci <typo3@b13.com> Tested-by: Jochen <rothjochen@gmail.com> Tested-by: Oliver Bartsch <bo@cedev.de> Reviewed-by: Jochen <rothjochen@gmail.com> Reviewed-by: Oliver Bartsch <bo@cedev.de> --- .../Element/ImmediateActionElement.ts | 3 + .../TypeScript/Event/EventDispatcher.ts | 26 ++++++ .../Public/TypeScript/Toolbar/OpendocsMenu.ts | 4 + .../Model/Element/ImmediateActionElement.php | 13 +++ .../Classes/Template/ModuleTemplate.php | 15 +++- .../Classes/Utility/BackendUtility.php | 80 ++++++++++++++++++- .../Element/ImmediateActionElement.js | 2 +- .../JavaScript/Event/EventDispatcher.js | 13 +++ .../ToolbarItems/OpendocsToolbarItem.php | 13 +-- .../Public/JavaScript/Toolbar/OpendocsMenu.js | 2 +- .../Hooks/DispatchNotificationHook.php | 1 + 11 files changed, 159 insertions(+), 13 deletions(-) create mode 100644 Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Event/EventDispatcher.ts create mode 100644 typo3/sysext/backend/Resources/Public/JavaScript/Event/EventDispatcher.js diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Element/ImmediateActionElement.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Element/ImmediateActionElement.ts index e5bdafbd6c00..72be32e20f84 100644 --- a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Element/ImmediateActionElement.ts +++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Element/ImmediateActionElement.ts @@ -12,6 +12,7 @@ */ import Utility = require('TYPO3/CMS/Backend/Utility'); +import {EventDispatcher} from 'TYPO3/CMS/Backend/Event/EventDispatcher'; /** * Module: TYPO3/CMS/Backend/Element/ImmediateActionElement @@ -41,6 +42,8 @@ export class ImmediateActionElement extends HTMLElement { return (await import('TYPO3/CMS/Backend/Storage/ModuleStateStorage')).ModuleStateStorage.update; case 'TYPO3.Backend.Storage.ModuleStateStorage.updateWithCurrentMount': return (await import('TYPO3/CMS/Backend/Storage/ModuleStateStorage')).ModuleStateStorage.updateWithCurrentMount; + case 'TYPO3.Backend.Event.EventDispatcher.dispatchCustomEvent': + return EventDispatcher.dispatchCustomEvent; default: throw Error('Unknown action "' + action + '"'); } diff --git a/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Event/EventDispatcher.ts b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Event/EventDispatcher.ts new file mode 100644 index 000000000000..4eb69a5635f9 --- /dev/null +++ b/Build/Sources/TypeScript/backend/Resources/Public/TypeScript/Event/EventDispatcher.ts @@ -0,0 +1,26 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Backend/Event/EventDispatcher + */ +export class EventDispatcher { + static dispatchCustomEvent(name: string, detail: any = null, useTop: boolean = false): void { + const event = new CustomEvent(name, {detail: detail}); + if (!useTop) { + document.dispatchEvent(event); + } else if (typeof top !== 'undefined') { + top.document.dispatchEvent(event); + } + } +} diff --git a/Build/Sources/TypeScript/opendocs/Resources/Public/TypeScript/Toolbar/OpendocsMenu.ts b/Build/Sources/TypeScript/opendocs/Resources/Public/TypeScript/Toolbar/OpendocsMenu.ts index 2b5708379f87..f9625c88c3fc 100644 --- a/Build/Sources/TypeScript/opendocs/Resources/Public/TypeScript/Toolbar/OpendocsMenu.ts +++ b/Build/Sources/TypeScript/opendocs/Resources/Public/TypeScript/Toolbar/OpendocsMenu.ts @@ -47,6 +47,10 @@ class OpendocsMenu { } constructor() { + document.addEventListener( + 'typo3:opendocs:updateRequested', + (evt: CustomEvent) => this.updateMenu(), + ); Viewport.Topbar.Toolbar.registerEvent((): void => { this.initializeEvents(); this.updateMenu(); diff --git a/typo3/sysext/backend/Classes/Domain/Model/Element/ImmediateActionElement.php b/typo3/sysext/backend/Classes/Domain/Model/Element/ImmediateActionElement.php index 16f2a4c7f246..ffa7727ab806 100644 --- a/typo3/sysext/backend/Classes/Domain/Model/Element/ImmediateActionElement.php +++ b/typo3/sysext/backend/Classes/Domain/Model/Element/ImmediateActionElement.php @@ -31,6 +31,11 @@ class ImmediateActionElement protected string $action; protected ?array $args = null; + public static function forAction(string $action): self + { + return new self($action, null); + } + public static function moduleStateUpdate(string $module, $identifier, bool $select = null): self { return new self( @@ -47,6 +52,14 @@ class ImmediateActionElement ); } + public static function dispatchCustomEvent(string $name, array $details = null, bool $useTop = false): self + { + return new self( + 'TYPO3.Backend.Event.EventDispatcher.dispatchCustomEvent', + [$name, $details, $useTop] + ); + } + private function __construct(string $action, ?array $args) { $this->action = $action; diff --git a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php index 02dac7b752ca..809ebf494b24 100644 --- a/typo3/sysext/backend/Classes/Template/ModuleTemplate.php +++ b/typo3/sysext/backend/Classes/Template/ModuleTemplate.php @@ -410,7 +410,20 @@ class ModuleTemplate $this->view->assign('uiBlock', $this->uiBlock); $this->view->assign('flashMessageQueueIdentifier', $this->flashMessageQueue->getIdentifier()); $this->pageRenderer->addBodyContent($this->bodyTag . $this->view->render()); - $this->pageRenderer->addJsFooterInlineCode('updateSignals', BackendUtility::getUpdateSignalCode()); + + $updateSignalDetails = BackendUtility::getUpdateSignalDetails(); + if (!empty($updateSignalDetails['html'])) { + $this->pageRenderer->addHeaderData( + implode("\n", $updateSignalDetails['html']) + ); + } + // @todo deprecate inline JavaScript in TYPO3 v12.0 + if (!empty($updateSignalDetails['script'])) { + $this->pageRenderer->addJsFooterInlineCode( + 'updateSignals', + implode("\n", $updateSignalDetails['script']) + ); + } return $this->pageRenderer->render(); } diff --git a/typo3/sysext/backend/Classes/Utility/BackendUtility.php b/typo3/sysext/backend/Classes/Utility/BackendUtility.php index 259f383d41f3..cb557e230f97 100644 --- a/typo3/sysext/backend/Classes/Utility/BackendUtility.php +++ b/typo3/sysext/backend/Classes/Utility/BackendUtility.php @@ -19,6 +19,7 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Log\LoggerInterface; use TYPO3\CMS\Backend\Configuration\TranslationConfigurationProvider; use TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher; +use TYPO3\CMS\Backend\Domain\Model\Element\ImmediateActionElement; use TYPO3\CMS\Backend\Routing\Route; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; @@ -2488,13 +2489,13 @@ class BackendUtility * * @param string $set Key to set the update signal. When setting, this value contains strings telling WHAT to set. At this point it seems that the value "updatePageTree" is the only one it makes sense to set. If empty, all update signals will be removed. * @param mixed $params Additional information for the update signal, used to only refresh a branch of the tree - * @see BackendUtility::getUpdateSignalCode() + * @see BackendUtility::getUpdateSignalDetails() */ public static function setUpdateSignal($set = '', $params = '') { $beUser = static::getBackendUserAuthentication(); $modData = $beUser->getModuleData( - \TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', + BackendUtility::class . '::getUpdateSignal', 'ses' ); if ($set) { @@ -2506,7 +2507,7 @@ class BackendUtility // clear the module data $modData = []; } - $beUser->pushModuleData(\TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', $modData); + $beUser->pushModuleData(BackendUtility::class . '::getUpdateSignal', $modData); } /** @@ -2515,12 +2516,13 @@ class BackendUtility * * @return string HTML javascript code * @see BackendUtility::setUpdateSignal() + * @internal use getUpdateSignalDetails() instead, will be deprecated in TYPO3 v12.0 */ public static function getUpdateSignalCode() { $signals = []; $modData = static::getBackendUserAuthentication()->getModuleData( - \TYPO3\CMS\Backend\Utility\BackendUtility::class . '::getUpdateSignal', + BackendUtility::class . '::getUpdateSignal', 'ses' ); if (empty($modData)) { @@ -2570,6 +2572,76 @@ class BackendUtility return $content; } + /** + * Gets instructions for update signals (e.g. page tree shall be refreshed, + * since some page title has been modified during the current HTTP request). + * + * @return array{html: list<string>, script: list<string>} + * @see BackendUtility::setUpdateSignal() + */ + public static function getUpdateSignalDetails(): array + { + $details = [ + 'html' => [], + // @todo deprecate inline JavaScript in TYPO3 v12.0 + 'script' => [], + ]; + $modData = static::getBackendUserAuthentication()->getModuleData( + BackendUtility::class . '::getUpdateSignal', + 'ses' + ); + if (empty($modData)) { + return $details; + } + // Hook: Allows to let TYPO3 execute your JS code + $updateSignals = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_befunc.php']['updateSignalHook'] ?? []; + // Loop through all setUpdateSignals and get the JS code + foreach ($modData as $set => $val) { + if (isset($updateSignals[$set])) { + $params = ['set' => $set, 'parameter' => $val['parameter'], 'JScode' => '', 'html' => '']; + $ref = null; + GeneralUtility::callUserFunction($updateSignals[$set], $params, $ref); + // @todo verify and adjust documentation + if (!empty($params['html'])) { + $details['html'][] = $params['html']; + } elseif (!empty($params['JScode'])) { + // @todo deprecate in TYPO3 v12.0, avoid inline JavaScript + $details['script'][] = $params['JScode']; + } + } else { + switch ($set) { + case 'updatePageTree': + $details['html'][] = ImmediateActionElement::dispatchCustomEvent( + 'typo3:pagetree:refresh', + null, + true + ); + break; + case 'updateFolderTree': + $details['html'][] = ImmediateActionElement::dispatchCustomEvent( + 'typo3:filestoragetree:refresh', + null, + true + ); + break; + case 'updateModuleMenu': + $details['html'][] = ImmediateActionElement::forAction( + 'TYPO3.ModuleMenu.App.refreshMenu', + ); + break; + case 'updateTopbar': + $details['html'][] = ImmediateActionElement::forAction( + 'TYPO3.Backend.Topbar.refresh' + ); + break; + } + } + } + // reset update signals + self::setUpdateSignal(); + return $details; + } + /** * Returns an array which is most backend modules becomes MOD_SETTINGS containing values from function menus etc. determining the function of the module. * This is kind of session variable management framework for the backend users. diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Element/ImmediateActionElement.js b/typo3/sysext/backend/Resources/Public/JavaScript/Element/ImmediateActionElement.js index 07f6c190b952..fe3ea96fd8a9 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/Element/ImmediateActionElement.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Element/ImmediateActionElement.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[a]}})}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)"default"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&__createBinding(t,e,a);return __setModuleDefault(t,e),t};define(["require","exports","TYPO3/CMS/Backend/Utility"],(function(e,t,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ImmediateActionElement=void 0;class r extends HTMLElement{constructor(){super(...arguments),this.args=[]}static async getDelegate(t){switch(t){case"TYPO3.ModuleMenu.App.refreshMenu":const a=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,a)}).then(__importStar);return a.App.refreshMenu.bind(a.App);case"TYPO3.Backend.Topbar.refresh":const r=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Viewport"],t,a)}).then(__importStar);return r.Topbar.refresh.bind(r.Topbar);case"TYPO3.WindowManager.localOpen":const n=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/WindowManager"],t,a)}).then(__importStar);return n.localOpen.bind(n);case"TYPO3.Backend.Storage.ModuleStateStorage.update":return(await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Storage/ModuleStateStorage"],t,a)}).then(__importStar)).ModuleStateStorage.update;case"TYPO3.Backend.Storage.ModuleStateStorage.updateWithCurrentMount":return(await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Storage/ModuleStateStorage"],t,a)}).then(__importStar)).ModuleStateStorage.updateWithCurrentMount;default:throw Error('Unknown action "'+t+'"')}}static get observedAttributes(){return["action","args","args-list"]}attributeChangedCallback(e,t,r){if("action"===e)this.action=r;else if("args"===e){const e=r.replace(/"/g,'"'),t=JSON.parse(e);this.args=t instanceof Array?a.trimItems(t):[]}else if("args-list"===e){const e=r.split(",");this.args=a.trimItems(e)}}connectedCallback(){if(!this.action)throw new Error("Missing mandatory action attribute");r.getDelegate(this.action).then(e=>e.apply(null,this.args))}}t.ImmediateActionElement=r,window.customElements.define("typo3-immediate-action",r)})); \ No newline at end of file +var __createBinding=this&&this.__createBinding||(Object.create?function(e,t,a,r){void 0===r&&(r=a),Object.defineProperty(e,r,{enumerable:!0,get:function(){return t[a]}})}:function(e,t,a,r){void 0===r&&(r=a),e[r]=t[a]}),__setModuleDefault=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),__importStar=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var a in e)"default"!==a&&Object.prototype.hasOwnProperty.call(e,a)&&__createBinding(t,e,a);return __setModuleDefault(t,e),t};define(["require","exports","TYPO3/CMS/Backend/Utility","TYPO3/CMS/Backend/Event/EventDispatcher"],(function(e,t,a,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ImmediateActionElement=void 0;class n extends HTMLElement{constructor(){super(...arguments),this.args=[]}static async getDelegate(t){switch(t){case"TYPO3.ModuleMenu.App.refreshMenu":const a=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/ModuleMenu"],t,a)}).then(__importStar);return a.App.refreshMenu.bind(a.App);case"TYPO3.Backend.Topbar.refresh":const n=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Viewport"],t,a)}).then(__importStar);return n.Topbar.refresh.bind(n.Topbar);case"TYPO3.WindowManager.localOpen":const i=await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/WindowManager"],t,a)}).then(__importStar);return i.localOpen.bind(i);case"TYPO3.Backend.Storage.ModuleStateStorage.update":return(await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Storage/ModuleStateStorage"],t,a)}).then(__importStar)).ModuleStateStorage.update;case"TYPO3.Backend.Storage.ModuleStateStorage.updateWithCurrentMount":return(await new Promise((t,a)=>{e(["TYPO3/CMS/Backend/Storage/ModuleStateStorage"],t,a)}).then(__importStar)).ModuleStateStorage.updateWithCurrentMount;case"TYPO3.Backend.Event.EventDispatcher.dispatchCustomEvent":return r.EventDispatcher.dispatchCustomEvent;default:throw Error('Unknown action "'+t+'"')}}static get observedAttributes(){return["action","args","args-list"]}attributeChangedCallback(e,t,r){if("action"===e)this.action=r;else if("args"===e){const e=r.replace(/"/g,'"'),t=JSON.parse(e);this.args=t instanceof Array?a.trimItems(t):[]}else if("args-list"===e){const e=r.split(",");this.args=a.trimItems(e)}}connectedCallback(){if(!this.action)throw new Error("Missing mandatory action attribute");n.getDelegate(this.action).then(e=>e.apply(null,this.args))}}t.ImmediateActionElement=n,window.customElements.define("typo3-immediate-action",n)})); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/Event/EventDispatcher.js b/typo3/sysext/backend/Resources/Public/JavaScript/Event/EventDispatcher.js new file mode 100644 index 000000000000..e65dd56134b2 --- /dev/null +++ b/typo3/sysext/backend/Resources/Public/JavaScript/Event/EventDispatcher.js @@ -0,0 +1,13 @@ +/* + * 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! + */ +define(["require","exports"],(function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.EventDispatcher=void 0;t.EventDispatcher=class{static dispatchCustomEvent(e,t=null,n=!1){const s=new CustomEvent(e,{detail:t});n?"undefined"!=typeof top&&top.document.dispatchEvent(s):document.dispatchEvent(s)}}})); \ No newline at end of file diff --git a/typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php b/typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php index 66d76bfceb7f..c0cd8e06c61e 100644 --- a/typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php +++ b/typo3/sysext/opendocs/Classes/Backend/ToolbarItems/OpendocsToolbarItem.php @@ -17,6 +17,7 @@ declare(strict_types=1); namespace TYPO3\CMS\Opendocs\Backend\ToolbarItems; +use TYPO3\CMS\Backend\Domain\Model\Element\ImmediateActionElement; use TYPO3\CMS\Backend\Routing\UriBuilder; use TYPO3\CMS\Backend\Toolbar\ToolbarItemInterface; use TYPO3\CMS\Backend\Utility\BackendUtility; @@ -121,13 +122,13 @@ class OpendocsToolbarItem implements ToolbarItemInterface * * @param array $params */ - public function updateNumberOfOpenDocsHook(&$params) + public function updateNumberOfOpenDocsHook(array &$params) { - $params['JScode'] = ' - if (top && top.TYPO3.OpendocsMenu) { - top.TYPO3.OpendocsMenu.updateMenu(); - } - '; + $params['html'] = ImmediateActionElement::dispatchCustomEvent( + 'typo3:opendocs:updateRequested', + null, + true + ); } /** diff --git a/typo3/sysext/opendocs/Resources/Public/JavaScript/Toolbar/OpendocsMenu.js b/typo3/sysext/opendocs/Resources/Public/JavaScript/Toolbar/OpendocsMenu.js index 7c2ba3695618..9c8b18a22f33 100644 --- a/typo3/sysext/opendocs/Resources/Public/JavaScript/Toolbar/OpendocsMenu.js +++ b/typo3/sysext/opendocs/Resources/Public/JavaScript/Toolbar/OpendocsMenu.js @@ -10,4 +10,4 @@ * * 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","TYPO3/CMS/Backend/Icons","TYPO3/CMS/Backend/Viewport","TYPO3/CMS/Backend/Storage/ModuleStateStorage"],(function(e,t,o,n,r,c,a){"use strict";var l;o=__importDefault(o),function(e){e.containerSelector="#typo3-cms-opendocs-backend-toolbaritems-opendocstoolbaritem",e.closeSelector=".t3js-topbar-opendocs-close",e.menuContainerSelector=".dropdown-menu",e.toolbarIconSelector=".toolbar-item-icon .t3js-icon",e.openDocumentsItemsSelector=".t3js-topbar-opendocs-item",e.counterSelector="#tx-opendocs-counter",e.entrySelector=".t3js-open-doc"}(l||(l={}));class s{constructor(){this.hashDataAttributeName="opendocsidentifier",this.toggleMenu=()=>{o.default(".scaffold").removeClass("scaffold-toolbar-expanded"),o.default(l.containerSelector).toggleClass("open")},c.Topbar.Toolbar.registerEvent(()=>{this.initializeEvents(),this.updateMenu()})}static updateNumberOfDocs(){const e=o.default(l.containerSelector).find(l.openDocumentsItemsSelector).length;o.default(l.counterSelector).text(e).toggle(e>0)}updateMenu(){let e=o.default(l.toolbarIconSelector,l.containerSelector),t=e.clone();r.getIcon("spinner-circle-light",r.sizes.small).done(t=>{e.replaceWith(t)}),new n(TYPO3.settings.ajaxUrls.opendocs_menu).get().then(async e=>{o.default(l.containerSelector).find(l.menuContainerSelector).html(await e.resolve()),s.updateNumberOfDocs()}).finally(()=>{o.default(l.toolbarIconSelector,l.containerSelector).replaceWith(t)})}initializeEvents(){o.default(l.containerSelector).on("click",l.closeSelector,e=>{e.preventDefault();const t=o.default(e.currentTarget).data(this.hashDataAttributeName);this.closeDocument(t)}).on("click",l.entrySelector,e=>{e.preventDefault();const t=o.default(e.currentTarget);this.toggleMenu(),a.ModuleStateStorage.updateWithCurrentMount("web",t.data("pid"),!0);document.querySelector("typo3-backend-module-router").setAttribute("endpoint",t.attr("href"))})}closeDocument(e){const t={};e&&(t.md5sum=e),new n(TYPO3.settings.ajaxUrls.opendocs_closedoc).post(t).then(async e=>{o.default(l.menuContainerSelector,l.containerSelector).html(await e.resolve()),s.updateNumberOfDocs(),o.default(l.containerSelector).toggleClass("open")})}}let i;return i=new s,"undefined"!=typeof TYPO3&&(TYPO3.OpendocsMenu=i),i})); \ No newline at end of file +var __importDefault=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","TYPO3/CMS/Backend/Icons","TYPO3/CMS/Backend/Viewport","TYPO3/CMS/Backend/Storage/ModuleStateStorage"],(function(e,t,o,n,r,a,c){"use strict";var l;o=__importDefault(o),function(e){e.containerSelector="#typo3-cms-opendocs-backend-toolbaritems-opendocstoolbaritem",e.closeSelector=".t3js-topbar-opendocs-close",e.menuContainerSelector=".dropdown-menu",e.toolbarIconSelector=".toolbar-item-icon .t3js-icon",e.openDocumentsItemsSelector=".t3js-topbar-opendocs-item",e.counterSelector="#tx-opendocs-counter",e.entrySelector=".t3js-open-doc"}(l||(l={}));class s{constructor(){this.hashDataAttributeName="opendocsidentifier",this.toggleMenu=()=>{o.default(".scaffold").removeClass("scaffold-toolbar-expanded"),o.default(l.containerSelector).toggleClass("open")},document.addEventListener("typo3:opendocs:updateRequested",e=>this.updateMenu()),a.Topbar.Toolbar.registerEvent(()=>{this.initializeEvents(),this.updateMenu()})}static updateNumberOfDocs(){const e=o.default(l.containerSelector).find(l.openDocumentsItemsSelector).length;o.default(l.counterSelector).text(e).toggle(e>0)}updateMenu(){let e=o.default(l.toolbarIconSelector,l.containerSelector),t=e.clone();r.getIcon("spinner-circle-light",r.sizes.small).done(t=>{e.replaceWith(t)}),new n(TYPO3.settings.ajaxUrls.opendocs_menu).get().then(async e=>{o.default(l.containerSelector).find(l.menuContainerSelector).html(await e.resolve()),s.updateNumberOfDocs()}).finally(()=>{o.default(l.toolbarIconSelector,l.containerSelector).replaceWith(t)})}initializeEvents(){o.default(l.containerSelector).on("click",l.closeSelector,e=>{e.preventDefault();const t=o.default(e.currentTarget).data(this.hashDataAttributeName);this.closeDocument(t)}).on("click",l.entrySelector,e=>{e.preventDefault();const t=o.default(e.currentTarget);this.toggleMenu(),c.ModuleStateStorage.updateWithCurrentMount("web",t.data("pid"),!0);document.querySelector("typo3-backend-module-router").setAttribute("endpoint",t.attr("href"))})}closeDocument(e){const t={};e&&(t.md5sum=e),new n(TYPO3.settings.ajaxUrls.opendocs_closedoc).post(t).then(async e=>{o.default(l.menuContainerSelector,l.containerSelector).html(await e.resolve()),s.updateNumberOfDocs(),o.default(l.containerSelector).toggleClass("open")})}}let u;return u=new s,"undefined"!=typeof TYPO3&&(TYPO3.OpendocsMenu=u),u})); \ No newline at end of file diff --git a/typo3/sysext/redirects/Classes/Hooks/DispatchNotificationHook.php b/typo3/sysext/redirects/Classes/Hooks/DispatchNotificationHook.php index 0b02c44fc834..9c32c73db60e 100644 --- a/typo3/sysext/redirects/Classes/Hooks/DispatchNotificationHook.php +++ b/typo3/sysext/redirects/Classes/Hooks/DispatchNotificationHook.php @@ -30,6 +30,7 @@ final class DispatchNotificationHook */ public function dispatchNotification(&$params) { + // @todo https://forge.typo3.org/issues/96003 $code = ' // Ensure the event handler is ready and listening to events top.window.require(["TYPO3/CMS/Redirects/EventHandler"], function() { -- GitLab