Commit 94d20cec authored by Benjamin Franzke's avatar Benjamin Franzke
Browse files

[TASK] Support FLAG_USE_TOP_WINDOW for ESM instructions

Implement top-frame-module loading for ES6 modules.
With RequireJS we could simply invoke top.require(), but
for native ES modules and dynamic import() statements
top.import() is not possible, as import is not a function
but a statement, and therefore not invokable on `top.`.

This has been marked as todo in #96510 as it is a
non trival change that is better implemented in a
separately testable patch.

An event is used that is dispatched on the top document.
It is filled by a global event handler with a promise
that reveals the result of the import() statement
performed in the context of the top frame.

Top frame module loading is used when modals
or notifications are shown in the outer-frame,
therefore EXT:redirect and EXT:install are adapted
to make use of this feture and avoid the prior
RequireJS indirection.

Resolves: #96610
Related: #96510
Related: #96323
Releases: main
Change-Id: I699a7a39beb788c843e3c8292e29bf3db97e8cd9
parent 4fb032c9
Pipeline #22356 passed with stages
in 13 minutes and 57 seconds
/*
* 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!
*/
/**
* @internal
*/
document.addEventListener('typo3:import-javascript-module', (e: CustomEvent<{ specifier: string; importPromise?: Promise<any>; }>) => {
e.detail.importPromise = import(e.detail.specifier);
});
/*
* 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!
*/
/**
* @internal
*/
export function topLevelModuleImport(specifier: string): Promise<any> {
const event: CustomEvent<{ specifier: string; importPromise?: Promise<any>; }> = new CustomEvent('typo3:import-javascript-module', {
detail: {
specifier,
importPromise: null
}
});
top.document.dispatchEvent(event);
if (event.detail.importPromise) {
return event.detail.importPromise;
}
return Promise.reject(new Error('Top level did not respond with a promise.'));
}
......@@ -69,8 +69,17 @@ function loadModule(payload: JavaScriptItemPayload): Promise<any> {
if (!(payload.flags & FLAG_USE_TOP_WINDOW)) {
return moduleImporter(payload.name);
} else {
// @todo implement
throw new Error('FLAG_USE_TOP_WINDOW is not yet supported for JavaScript modules');
const event = new CustomEvent<{ specifier: string; importPromise: null|Promise<any>; }>(
'typo3:import-javascript-module',
{
detail: {
specifier: payload.name,
importPromise: null
}
}
);
top.document.dispatchEvent(event);
return event.detail.importPromise || Promise.reject(new Error('Top-level import failed'));
}
}
......
......@@ -15,6 +15,7 @@ import 'bootstrap';
import $ from 'jquery';
import {AjaxResponse} from 'TYPO3/CMS/Core/Ajax/AjaxResponse';
import {AbstractInteractableModule} from '../AbstractInteractableModule';
import {topLevelModuleImport} from 'TYPO3/CMS/Backend/Utility/TopLevelModuleImport';
import Modal from 'TYPO3/CMS/Backend/Modal';
import Notification from 'TYPO3/CMS/Backend/Notification';
import AjaxRequest from 'TYPO3/CMS/Core/Ajax/AjaxRequest';
......@@ -32,7 +33,7 @@ class SystemMaintainer extends AbstractInteractableModule {
this.currentModal = currentModal;
const isInIframe = window.location !== window.parent.location;
if (isInIframe) {
top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
topLevelModuleImport('TYPO3/CMS/Install/chosen.jquery.min.js').then((): void => {
this.getList();
});
} else {
......
......@@ -18,6 +18,7 @@ import '../../Renderable/Clearable';
import {AbstractInteractableModule} from '../AbstractInteractableModule';
import Notification from 'TYPO3/CMS/Backend/Notification';
import AjaxRequest from 'TYPO3/CMS/Core/Ajax/AjaxRequest';
import {topLevelModuleImport} from 'TYPO3/CMS/Backend/Utility/TopLevelModuleImport';
import Router from '../../Router';
import DebounceEvent from 'TYPO3/CMS/Core/Event/DebounceEvent';
......@@ -52,7 +53,7 @@ class UpgradeDocs extends AbstractInteractableModule {
this.currentModal = currentModal;
const isInIframe = (window.location !== window.parent.location);
if (isInIframe) {
top.require(['TYPO3/CMS/Install/chosen.jquery.min'], (): void => {
topLevelModuleImport('TYPO3/CMS/Install/chosen.jquery.min.js').then((): void => {
this.getContent();
});
} else {
......
......@@ -112,6 +112,7 @@ class BackendController
);
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/Module/Router.js');
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/ModuleMenu.js');
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/JavaScriptModuleImportEventHandler.js');
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/Storage/ModuleStateStorage.js');
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/Toolbar.js');
$this->pageRenderer->loadJavaScriptModule('TYPO3/CMS/Backend/Notification.js');
......
/*
* 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!
*/
"use strict";document.addEventListener("typo3:import-javascript-module",t=>{t.detail.importPromise=import(t.detail.specifier)});
\ No newline at end of file
/*
* 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!
*/
export function topLevelModuleImport(e){const o=new CustomEvent("typo3:import-javascript-module",{detail:{specifier:e,importPromise:null}});return top.document.dispatchEvent(o),o.detail.importPromise?o.detail.importPromise:Promise.reject(new Error("Top level did not respond with a promise."))}
\ No newline at end of file
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
const FLAG_USE_REQUIRE_JS=1,FLAG_USE_IMPORTMAP=2,FLAG_USE_TOP_WINDOW=16,deniedProperties=["__proto__","prototype","constructor"],allowedJavaScriptItemTypes=["assign","invoke","instance"];let useShim=!1;const moduleImporter=e=>useShim?window.importShim(e):import(e).catch(()=>(useShim=!0,moduleImporter(e)));function loadModule(e){if(!e.name)throw new Error("JavaScript module name is required");if(2==(2&e.flags)){if(16&e.flags)throw new Error("FLAG_USE_TOP_WINDOW is not yet supported for JavaScript modules");return moduleImporter(e.name)}if(1==(1&e.flags))return new Promise((t,o)=>{(16==(16&e.flags)?top.window:window).require([e.name],e=>t(e),e=>o(e))});throw new Error("Unknown JavaScript module type")}function executeJavaScriptModuleInstruction(e){if(!e.name)throw new Error("JavaScript module name is required");if(!e.items)return void loadModule(e);const t=e.exportName,o=o=>"string"==typeof t?o[t]:1==(1&e.flags)?o:o.default,r=e.items.filter(e=>allowedJavaScriptItemTypes.includes(e.type)).map(e=>"assign"===e.type?t=>{mergeRecursive(o(t),e.assignments)}:"invoke"===e.type?t=>{const r=o(t);r[e.method].apply(r,e.args)}:"instance"===e.type?t=>{const r=[null].concat(e.args),n=o(t);new(n.bind.apply(n,r))}:e=>{});loadModule(e).then(e=>r.forEach(t=>t.call(null,e)))}function isObjectInstance(e){return e instanceof Object&&!(e instanceof Array)}function mergeRecursive(e,t){Object.keys(t).forEach(o=>{if(-1!==deniedProperties.indexOf(o))throw new Error("Property "+o+" is not allowed");isObjectInstance(t[o])&&void 0!==e[o]?mergeRecursive(e[o],t[o]):Object.assign(e,{[o]:t[o]})})}export class JavaScriptItemProcessor{constructor(){this.invokableNames=["globalAssignment","javaScriptModuleInstruction"]}processItems(e){e.forEach(e=>this.invoke(e.type,e.payload))}invoke(e,t){if(!this.invokableNames.includes(e)||"function"!=typeof this[e])throw new Error('Unknown handler name "'+e+'"');this[e].call(this,t)}globalAssignment(e){mergeRecursive(window,e)}javaScriptModuleInstruction(e){executeJavaScriptModuleInstruction(e)}}
\ No newline at end of file
const FLAG_USE_REQUIRE_JS=1,FLAG_USE_IMPORTMAP=2,FLAG_USE_TOP_WINDOW=16,deniedProperties=["__proto__","prototype","constructor"],allowedJavaScriptItemTypes=["assign","invoke","instance"];let useShim=!1;const moduleImporter=e=>useShim?window.importShim(e):import(e).catch(()=>(useShim=!0,moduleImporter(e)));function loadModule(e){if(!e.name)throw new Error("JavaScript module name is required");if(2==(2&e.flags)){if(16&e.flags){const t=new CustomEvent("typo3:import-javascript-module",{detail:{specifier:e.name,importPromise:null}});return top.document.dispatchEvent(t),t.detail.importPromise||Promise.reject(new Error("Top-level import failed"))}return moduleImporter(e.name)}if(1==(1&e.flags))return new Promise((t,o)=>{(16==(16&e.flags)?top.window:window).require([e.name],e=>t(e),e=>o(e))});throw new Error("Unknown JavaScript module type")}function executeJavaScriptModuleInstruction(e){if(!e.name)throw new Error("JavaScript module name is required");if(!e.items)return void loadModule(e);const t=e.exportName,o=o=>"string"==typeof t?o[t]:1==(1&e.flags)?o:o.default,r=e.items.filter(e=>allowedJavaScriptItemTypes.includes(e.type)).map(e=>"assign"===e.type?t=>{mergeRecursive(o(t),e.assignments)}:"invoke"===e.type?t=>{const r=o(t);r[e.method].apply(r,e.args)}:"instance"===e.type?t=>{const r=[null].concat(e.args),n=o(t);new(n.bind.apply(n,r))}:e=>{});loadModule(e).then(e=>r.forEach(t=>t.call(null,e)))}function isObjectInstance(e){return e instanceof Object&&!(e instanceof Array)}function mergeRecursive(e,t){Object.keys(t).forEach(o=>{if(-1!==deniedProperties.indexOf(o))throw new Error("Property "+o+" is not allowed");isObjectInstance(t[o])&&void 0!==e[o]?mergeRecursive(e[o],t[o]):Object.assign(e,{[o]:t[o]})})}export class JavaScriptItemProcessor{constructor(){this.invokableNames=["globalAssignment","javaScriptModuleInstruction"]}processItems(e){e.forEach(e=>this.invoke(e.type,e.payload))}invoke(e,t){if(!this.invokableNames.includes(e)||"function"!=typeof this[e])throw new Error('Unknown handler name "'+e+'"');this[e].call(this,t)}globalAssignment(e){mergeRecursive(window,e)}javaScriptModuleInstruction(e){executeJavaScriptModuleInstruction(e)}}
\ No newline at end of file
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"TYPO3/CMS/Install/Module/AbstractInteractableModule.js";import Modal from"TYPO3/CMS/Backend/Modal.js";import Notification from"TYPO3/CMS/Backend/Notification.js";import AjaxRequest from"TYPO3/CMS/Core/Ajax/AjaxRequest.js";import Router from"TYPO3/CMS/Install/Router.js";class SystemMaintainer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorWriteTrigger=".t3js-systemMaintainer-write",this.selectorChosenContainer=".t3js-systemMaintainer-chosen",this.selectorChosenField=".t3js-systemMaintainer-chosen-select"}initialize(t){this.currentModal=t;window.location!==window.parent.location?top.require(["TYPO3/CMS/Install/chosen.jquery.min"],()=>{this.getList()}):import("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getList()}),t.on("click",this.selectorWriteTrigger,t=>{t.preventDefault(),this.write()})}getList(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("systemMaintainerGetList")).get({cache:"no-cache"}).then(async e=>{const s=await e.resolve();if(!0===s.success){t.html(s.html),Modal.setButtons(s.buttons),Array.isArray(s.users)&&s.users.forEach(e=>{let s=e.username;e.disable&&(s="[DISABLED] "+s);const o=$("<option>",{value:e.uid}).text(s);e.isSystemMaintainer&&o.attr("selected","selected"),t.find(this.selectorChosenField).append(o)});const e={".t3js-systemMaintainer-chosen-select":{width:"100%",placeholder_text_multiple:"users"}};for(const s in e)e.hasOwnProperty(s)&&t.find(s).chosen(e[s]);t.find(this.selectorChosenContainer).show(),t.find(this.selectorChosenField).trigger("chosen:updated")}},e=>{Router.handleAjaxError(e,t)})}write(){this.setModalButtonsState(!1);const t=this.getModalBody(),e=this.getModuleContent().data("system-maintainer-write-token"),s=this.findInModal(this.selectorChosenField).val();new AjaxRequest(Router.getUrl()).post({install:{users:s,token:e,action:"systemMaintainerWrite"}}).then(async t=>{const e=await t.resolve();!0===e.success?Array.isArray(e.status)&&e.status.forEach(t=>{Notification.success(t.title,t.message)}):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")},e=>{Router.handleAjaxError(e,t)}).finally(()=>{this.setModalButtonsState(!0)})}}export default new SystemMaintainer;
\ No newline at end of file
import"bootstrap";import $ from"jquery";import{AbstractInteractableModule}from"TYPO3/CMS/Install/Module/AbstractInteractableModule.js";import{topLevelModuleImport}from"TYPO3/CMS/Backend/Utility/TopLevelModuleImport.js";import Modal from"TYPO3/CMS/Backend/Modal.js";import Notification from"TYPO3/CMS/Backend/Notification.js";import AjaxRequest from"TYPO3/CMS/Core/Ajax/AjaxRequest.js";import Router from"TYPO3/CMS/Install/Router.js";class SystemMaintainer extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorWriteTrigger=".t3js-systemMaintainer-write",this.selectorChosenContainer=".t3js-systemMaintainer-chosen",this.selectorChosenField=".t3js-systemMaintainer-chosen-select"}initialize(t){this.currentModal=t;window.location!==window.parent.location?topLevelModuleImport("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getList()}):import("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getList()}),t.on("click",this.selectorWriteTrigger,t=>{t.preventDefault(),this.write()})}getList(){const t=this.getModalBody();new AjaxRequest(Router.getUrl("systemMaintainerGetList")).get({cache:"no-cache"}).then(async e=>{const s=await e.resolve();if(!0===s.success){t.html(s.html),Modal.setButtons(s.buttons),Array.isArray(s.users)&&s.users.forEach(e=>{let s=e.username;e.disable&&(s="[DISABLED] "+s);const o=$("<option>",{value:e.uid}).text(s);e.isSystemMaintainer&&o.attr("selected","selected"),t.find(this.selectorChosenField).append(o)});const e={".t3js-systemMaintainer-chosen-select":{width:"100%",placeholder_text_multiple:"users"}};for(const s in e)e.hasOwnProperty(s)&&t.find(s).chosen(e[s]);t.find(this.selectorChosenContainer).show(),t.find(this.selectorChosenField).trigger("chosen:updated")}},e=>{Router.handleAjaxError(e,t)})}write(){this.setModalButtonsState(!1);const t=this.getModalBody(),e=this.getModuleContent().data("system-maintainer-write-token"),s=this.findInModal(this.selectorChosenField).val();new AjaxRequest(Router.getUrl()).post({install:{users:s,token:e,action:"systemMaintainerWrite"}}).then(async t=>{const e=await t.resolve();!0===e.success?Array.isArray(e.status)&&e.status.forEach(t=>{Notification.success(t.title,t.message)}):Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")},e=>{Router.handleAjaxError(e,t)}).finally(()=>{this.setModalButtonsState(!0)})}}export default new SystemMaintainer;
\ No newline at end of file
......@@ -10,4 +10,4 @@
*
* The TYPO3 project - inspiring people to share!
*/
import"bootstrap";import $ from"jquery";import"TYPO3/CMS/Install/Renderable/Clearable.js";import{AbstractInteractableModule}from"TYPO3/CMS/Install/Module/AbstractInteractableModule.js";import Notification from"TYPO3/CMS/Backend/Notification.js";import AjaxRequest from"TYPO3/CMS/Core/Ajax/AjaxRequest.js";import Router from"TYPO3/CMS/Install/Router.js";import DebounceEvent from"TYPO3/CMS/Core/Event/DebounceEvent.js";class UpgradeDocs extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFulltextSearch=".t3js-upgradeDocs-fulltext-search",this.selectorChosenField=".t3js-upgradeDocs-chosen-select",this.selectorChangeLogsForVersionContainer=".t3js-version-changes",this.selectorChangeLogsForVersion=".t3js-changelog-list",this.selectorUpgradeDoc=".t3js-upgrade-doc"}static trimExplodeAndUnique(e,t){const s=[],o=t.split(e);for(let e=0;e<o.length;e++){const t=o[e].trim();t.length>0&&-1===$.inArray(t,s)&&s.push(t)}return s}initialize(e){this.currentModal=e;window.location!==window.parent.location?top.require(["TYPO3/CMS/Install/chosen.jquery.min"],()=>{this.getContent()}):import("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getContent()}),e.on("click",".t3js-upgradeDocs-markRead",e=>{this.markRead(e.target)}),e.on("click",".t3js-upgradeDocs-unmarkRead",e=>{this.unmarkRead(e.target)}),$.expr[":"].contains=$.expr.createPseudo(e=>t=>$(t).text().toUpperCase().includes(e.toUpperCase()))}getContent(){const e=this.getModalBody();e.on("show.bs.collapse",this.selectorUpgradeDoc,e=>{this.renderTags($(e.currentTarget))}),new AjaxRequest(Router.getUrl("upgradeDocsGetContent")).get({cache:"no-cache"}).then(async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0&&(e.empty().append(s.html),this.initializeFullTextSearch(),this.initializeChosenSelector(),this.loadChangelogs())},t=>{Router.handleAjaxError(t,e)})}loadChangelogs(){const e=[],t=this.getModalBody();this.findInModal(this.selectorChangeLogsForVersionContainer).each((s,o)=>{const a=new AjaxRequest(Router.getUrl("upgradeDocsGetChangelogForVersion")).withQueryArguments({install:{version:o.dataset.version}}).get({cache:"no-cache"}).then(async e=>{const t=await e.resolve();if(!0===t.success){const e=$(o),s=e.find(this.selectorChangeLogsForVersion);s.html(t.html),this.moveNotRelevantDocuments(s),e.find(".t3js-panel-loading").remove()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")},e=>{Router.handleAjaxError(e,t)});e.push(a)}),Promise.all(e).then(()=>{this.fulltextSearchField.prop("disabled",!1),this.appendItemsToChosenSelector()})}initializeFullTextSearch(){this.fulltextSearchField=this.findInModal(this.selectorFulltextSearch);const e=this.fulltextSearchField.get(0);e.clearable({onClear:()=>{this.combinedFilterSearch()}}),e.focus(),this.initializeChosenSelector(),new DebounceEvent("keyup",()=>{this.combinedFilterSearch()}).bindTo(e)}initializeChosenSelector(){this.chosenField=this.getModalBody().find(this.selectorChosenField);const e={".chosen-select":{width:"100%",placeholder_text_multiple:"tags"},".chosen-select-deselect":{allow_single_deselect:!0},".chosen-select-no-single":{disable_search_threshold:10},".chosen-select-no-results":{no_results_text:"Oops, nothing found!"},".chosen-select-width":{width:"100%"}};for(const t in e)e.hasOwnProperty(t)&&this.findInModal(t).chosen(e[t]);this.chosenField.on("change",()=>{this.combinedFilterSearch()})}appendItemsToChosenSelector(){let e="";$(this.findInModal(this.selectorUpgradeDoc)).each((t,s)=>{e+=$(s).data("item-tags")+","});const t=UpgradeDocs.trimExplodeAndUnique(",",e).sort((e,t)=>e.toLowerCase().localeCompare(t.toLowerCase()));this.chosenField.prop("disabled",!1),$.each(t,(e,t)=>{this.chosenField.append($("<option>").text(t))}),this.chosenField.trigger("chosen:updated")}combinedFilterSearch(){const e=this.getModalBody(),t=e.find("div.item");if(this.chosenField.val().length<1&&this.fulltextSearchField.val().length<1)return this.currentModal.find(".panel-version .panel-collapse.show").collapse("hide"),t.removeClass("hidden searchhit filterhit"),!1;if(t.addClass("hidden").removeClass("searchhit filterhit"),this.chosenField.val().length>0){t.addClass("hidden").removeClass("filterhit");const s=[],o=[];$.each(this.chosenField.val(),(e,t)=>{const a='[data-item-tags*="'+t+'"]';t.includes(":",1)?s.push(a):o.push(a)});const a=o.join(""),n=[];if(s.length)for(let e of s)n.push(a+e);else n.push(a);const i=n.join(",");e.find(i).removeClass("hidden").addClass("searchhit filterhit")}else t.addClass("filterhit").removeClass("hidden");const s=this.fulltextSearchField.val();return e.find("div.item.filterhit").each((e,t)=>{const o=$(t);$(":contains("+s+")",o).length>0||$('input[value*="'+s+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")}),e.find(".searchhit").closest(".panel-collapse").collapse("show"),e.find(".panel-version").each((e,t)=>{const s=$(t);s.find(".searchhit",".filterhit").length<1&&s.find(" > .panel-collapse").collapse("hide")}),!0}renderTags(e){const t=e.find(".t3js-tags");if(0===t.children().length){e.data("item-tags").split(",").forEach(e=>{t.append($("<span />",{class:"label"}).text(e))})}}moveNotRelevantDocuments(e){e.find('[data-item-state="read"]').appendTo(this.findInModal(".panel-body-read")),e.find('[data-item-state="notAffected"]').appendTo(this.findInModal(".panel-body-not-affected"))}markRead(e){const t=this.getModalBody(),s=this.getModuleContent().data("upgrade-docs-mark-read-token"),o=$(e).closest("button");o.toggleClass("t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead"),o.find("i").toggleClass("fa-check fa-ban"),o.closest(".panel").appendTo(this.findInModal(".panel-body-read")),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:o.data("filepath"),token:s,action:"upgradeDocsMarkRead"}}).catch(e=>{Router.handleAjaxError(e,t)})}unmarkRead(e){const t=this.getModalBody(),s=this.getModuleContent().data("upgrade-docs-unmark-read-token"),o=$(e).closest("button"),a=o.closest(".panel").data("item-version");o.toggleClass("t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead"),o.find("i").toggleClass("fa-check fa-ban"),o.closest(".panel").appendTo(this.findInModal('*[data-group-version="'+a+'"] .panel-body')),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:o.data("filepath"),token:s,action:"upgradeDocsUnmarkRead"}}).catch(e=>{Router.handleAjaxError(e,t)})}}export default new UpgradeDocs;
\ No newline at end of file
import"bootstrap";import $ from"jquery";import"TYPO3/CMS/Install/Renderable/Clearable.js";import{AbstractInteractableModule}from"TYPO3/CMS/Install/Module/AbstractInteractableModule.js";import Notification from"TYPO3/CMS/Backend/Notification.js";import AjaxRequest from"TYPO3/CMS/Core/Ajax/AjaxRequest.js";import{topLevelModuleImport}from"TYPO3/CMS/Backend/Utility/TopLevelModuleImport.js";import Router from"TYPO3/CMS/Install/Router.js";import DebounceEvent from"TYPO3/CMS/Core/Event/DebounceEvent.js";class UpgradeDocs extends AbstractInteractableModule{constructor(){super(...arguments),this.selectorFulltextSearch=".t3js-upgradeDocs-fulltext-search",this.selectorChosenField=".t3js-upgradeDocs-chosen-select",this.selectorChangeLogsForVersionContainer=".t3js-version-changes",this.selectorChangeLogsForVersion=".t3js-changelog-list",this.selectorUpgradeDoc=".t3js-upgrade-doc"}static trimExplodeAndUnique(e,t){const s=[],o=t.split(e);for(let e=0;e<o.length;e++){const t=o[e].trim();t.length>0&&-1===$.inArray(t,s)&&s.push(t)}return s}initialize(e){this.currentModal=e;window.location!==window.parent.location?topLevelModuleImport("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getContent()}):import("TYPO3/CMS/Install/chosen.jquery.min.js").then(()=>{this.getContent()}),e.on("click",".t3js-upgradeDocs-markRead",e=>{this.markRead(e.target)}),e.on("click",".t3js-upgradeDocs-unmarkRead",e=>{this.unmarkRead(e.target)}),$.expr[":"].contains=$.expr.createPseudo(e=>t=>$(t).text().toUpperCase().includes(e.toUpperCase()))}getContent(){const e=this.getModalBody();e.on("show.bs.collapse",this.selectorUpgradeDoc,e=>{this.renderTags($(e.currentTarget))}),new AjaxRequest(Router.getUrl("upgradeDocsGetContent")).get({cache:"no-cache"}).then(async t=>{const s=await t.resolve();!0===s.success&&"undefined"!==s.html&&s.html.length>0&&(e.empty().append(s.html),this.initializeFullTextSearch(),this.initializeChosenSelector(),this.loadChangelogs())},t=>{Router.handleAjaxError(t,e)})}loadChangelogs(){const e=[],t=this.getModalBody();this.findInModal(this.selectorChangeLogsForVersionContainer).each((s,o)=>{const a=new AjaxRequest(Router.getUrl("upgradeDocsGetChangelogForVersion")).withQueryArguments({install:{version:o.dataset.version}}).get({cache:"no-cache"}).then(async e=>{const t=await e.resolve();if(!0===t.success){const e=$(o),s=e.find(this.selectorChangeLogsForVersion);s.html(t.html),this.moveNotRelevantDocuments(s),e.find(".t3js-panel-loading").remove()}else Notification.error("Something went wrong","The request was not processed successfully. Please check the browser's console and TYPO3's log.")},e=>{Router.handleAjaxError(e,t)});e.push(a)}),Promise.all(e).then(()=>{this.fulltextSearchField.prop("disabled",!1),this.appendItemsToChosenSelector()})}initializeFullTextSearch(){this.fulltextSearchField=this.findInModal(this.selectorFulltextSearch);const e=this.fulltextSearchField.get(0);e.clearable({onClear:()=>{this.combinedFilterSearch()}}),e.focus(),this.initializeChosenSelector(),new DebounceEvent("keyup",()=>{this.combinedFilterSearch()}).bindTo(e)}initializeChosenSelector(){this.chosenField=this.getModalBody().find(this.selectorChosenField);const e={".chosen-select":{width:"100%",placeholder_text_multiple:"tags"},".chosen-select-deselect":{allow_single_deselect:!0},".chosen-select-no-single":{disable_search_threshold:10},".chosen-select-no-results":{no_results_text:"Oops, nothing found!"},".chosen-select-width":{width:"100%"}};for(const t in e)e.hasOwnProperty(t)&&this.findInModal(t).chosen(e[t]);this.chosenField.on("change",()=>{this.combinedFilterSearch()})}appendItemsToChosenSelector(){let e="";$(this.findInModal(this.selectorUpgradeDoc)).each((t,s)=>{e+=$(s).data("item-tags")+","});const t=UpgradeDocs.trimExplodeAndUnique(",",e).sort((e,t)=>e.toLowerCase().localeCompare(t.toLowerCase()));this.chosenField.prop("disabled",!1),$.each(t,(e,t)=>{this.chosenField.append($("<option>").text(t))}),this.chosenField.trigger("chosen:updated")}combinedFilterSearch(){const e=this.getModalBody(),t=e.find("div.item");if(this.chosenField.val().length<1&&this.fulltextSearchField.val().length<1)return this.currentModal.find(".panel-version .panel-collapse.show").collapse("hide"),t.removeClass("hidden searchhit filterhit"),!1;if(t.addClass("hidden").removeClass("searchhit filterhit"),this.chosenField.val().length>0){t.addClass("hidden").removeClass("filterhit");const s=[],o=[];$.each(this.chosenField.val(),(e,t)=>{const a='[data-item-tags*="'+t+'"]';t.includes(":",1)?s.push(a):o.push(a)});const a=o.join(""),n=[];if(s.length)for(let e of s)n.push(a+e);else n.push(a);const l=n.join(",");e.find(l).removeClass("hidden").addClass("searchhit filterhit")}else t.addClass("filterhit").removeClass("hidden");const s=this.fulltextSearchField.val();return e.find("div.item.filterhit").each((e,t)=>{const o=$(t);$(":contains("+s+")",o).length>0||$('input[value*="'+s+'"]',o).length>0?o.removeClass("hidden").addClass("searchhit"):o.removeClass("searchhit").addClass("hidden")}),e.find(".searchhit").closest(".panel-collapse").collapse("show"),e.find(".panel-version").each((e,t)=>{const s=$(t);s.find(".searchhit",".filterhit").length<1&&s.find(" > .panel-collapse").collapse("hide")}),!0}renderTags(e){const t=e.find(".t3js-tags");if(0===t.children().length){e.data("item-tags").split(",").forEach(e=>{t.append($("<span />",{class:"label"}).text(e))})}}moveNotRelevantDocuments(e){e.find('[data-item-state="read"]').appendTo(this.findInModal(".panel-body-read")),e.find('[data-item-state="notAffected"]').appendTo(this.findInModal(".panel-body-not-affected"))}markRead(e){const t=this.getModalBody(),s=this.getModuleContent().data("upgrade-docs-mark-read-token"),o=$(e).closest("button");o.toggleClass("t3js-upgradeDocs-unmarkRead t3js-upgradeDocs-markRead"),o.find("i").toggleClass("fa-check fa-ban"),o.closest(".panel").appendTo(this.findInModal(".panel-body-read")),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:o.data("filepath"),token:s,action:"upgradeDocsMarkRead"}}).catch(e=>{Router.handleAjaxError(e,t)})}unmarkRead(e){const t=this.getModalBody(),s=this.getModuleContent().data("upgrade-docs-unmark-read-token"),o=$(e).closest("button"),a=o.closest(".panel").data("item-version");o.toggleClass("t3js-upgradeDocs-markRead t3js-upgradeDocs-unmarkRead"),o.find("i").toggleClass("fa-check fa-ban"),o.closest(".panel").appendTo(this.findInModal('*[data-group-version="'+a+'"] .panel-body')),new AjaxRequest(Router.getUrl()).post({install:{ignoreFile:o.data("filepath"),token:s,action:"upgradeDocsUnmarkRead"}}).catch(e=>{Router.handleAjaxError(e,t)})}}export default new UpgradeDocs;
\ No newline at end of file
......@@ -36,8 +36,9 @@ final class DispatchNotificationHook
{
$javaScriptRenderer = GeneralUtility::makeInstance(PageRenderer::class)->getJavaScriptRenderer();
$javaScriptRenderer->addJavaScriptModuleInstruction(
// Ensures event handler is ready and listening to events
JavaScriptModuleInstruction::forRequireJS('TYPO3/CMS/Redirects/EventHandler')
// @todo refactor to directly invoke the redirects slugChanged() method
// instead of dispatching an event that is only catched by the event dispatcher itself
JavaScriptModuleInstruction::create('TYPO3/CMS/Redirects/EventHandler.js')
->addFlags(JavaScriptModuleInstruction::FLAG_USE_TOP_WINDOW)
->invoke('dispatchCustomEvent', 'typo3:redirects:slugChanged', $params['parameter'])
);
......
Markdown is supported
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