diff --git a/api/js/etemplate/et2_core_xml.js b/api/js/etemplate/et2_core_xml.js index 7aff6bb3c3..94f02cee74 100644 --- a/api/js/etemplate/et2_core_xml.js +++ b/api/js/etemplate/et2_core_xml.js @@ -18,6 +18,7 @@ import { egw } from "../jsapi/egw_global"; * @param {function} _callback function(_xml) * @param {object} _context for _callback * @param {function} _fail_callback function(_xml) + * @return Promise */ export function et2_loadXMLFromURL(_url, _callback, _context, _fail_callback) { if (typeof _context == "undefined") { @@ -36,7 +37,7 @@ export function et2_loadXMLFromURL(_url, _callback, _context, _fail_callback) { if (typeof win == "undefined") { win = egw.top; } - win.jQuery.ajax({ + return win.jQuery.ajax({ // we add the full url (protocol and domain) as sometimes just the path // gives a CSP error interpreting it as file:///path // (if there are a enough 404 errors in html content ...) @@ -45,11 +46,13 @@ export function et2_loadXMLFromURL(_url, _callback, _context, _fail_callback) { type: 'GET', dataType: 'xml', success: function (_data, _status, _xmlhttp) { - _callback.call(_context, _data.documentElement); + if (typeof _callback === 'function') { + _callback.call(_context, _data.documentElement); + } }, error: function (_xmlhttp, _err) { egw().debug('error', 'Loading eTemplate from ' + _url + ' failed! ' + _xmlhttp.status + ' ' + _xmlhttp.statusText); - if (typeof _fail_callback !== 'undefined') { + if (typeof _fail_callback === 'function') { _fail_callback.call(_context, _err); } } diff --git a/api/js/etemplate/et2_core_xml.ts b/api/js/etemplate/et2_core_xml.ts index 9f0412c82f..2cdccd71b3 100644 --- a/api/js/etemplate/et2_core_xml.ts +++ b/api/js/etemplate/et2_core_xml.ts @@ -20,8 +20,9 @@ import {egw} from "../jsapi/egw_global"; * @param {function} _callback function(_xml) * @param {object} _context for _callback * @param {function} _fail_callback function(_xml) + * @return Promise */ -export function et2_loadXMLFromURL(_url : string, _callback : Function, _context : object, _fail_callback? : Function) +export function et2_loadXMLFromURL(_url : string, _callback? : Function, _context? : object, _fail_callback? : Function) { if (typeof _context == "undefined") { @@ -43,7 +44,7 @@ export function et2_loadXMLFromURL(_url : string, _callback : Function, _context { win = egw.top; } - win.jQuery.ajax({ + return win.jQuery.ajax({ // we add the full url (protocol and domain) as sometimes just the path // gives a CSP error interpreting it as file:///path // (if there are a enough 404 errors in html content ...) @@ -52,12 +53,13 @@ export function et2_loadXMLFromURL(_url : string, _callback : Function, _context type: 'GET', dataType: 'xml', success: function(_data, _status, _xmlhttp){ - _callback.call(_context, _data.documentElement); + if (typeof _callback === 'function') { + _callback.call(_context, _data.documentElement); + } }, error: function(_xmlhttp, _err) { egw().debug('error', 'Loading eTemplate from '+_url+' failed! '+_xmlhttp.status+' '+_xmlhttp.statusText); - if(typeof _fail_callback !== 'undefined') - { + if(typeof _fail_callback === 'function') { _fail_callback.call(_context, _err); } } diff --git a/api/js/etemplate/etemplate2.js b/api/js/etemplate/etemplate2.js index 500a6a7d50..5e5f8f856b 100644 --- a/api/js/etemplate/etemplate2.js +++ b/api/js/etemplate/etemplate2.js @@ -327,7 +327,8 @@ export class etemplate2 { * @param {function} _callback called after template is loaded * @param {object} _app local app object * @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object - * @param {string} _open_target flag of string to distinguishe between tab target and normal app object + * @param {string} _open_target flag of string to distinguish between tab target and normal app object + * @return Promise */ load(_name, _url, _data, _callback, _app, _no_et2_ready, _open_target) { let app = _app || window.app; @@ -383,7 +384,7 @@ export class etemplate2 { if (appname) { promisses.push(egw(currentapp, window).includeJS('/' + appname + '/js/app.js', undefined, undefined, egw.webserverUrl)); } - Promise.all(promisses).catch((err) => { + return Promise.all(promisses).catch((err) => { console.log("et2.load(): error loading lang-files and app.js: " + err.message); }).then(() => { this.clear(); @@ -538,8 +539,11 @@ export class etemplate2 { throw e; } } + // Split the given data into array manager objects and pass those to the + // widget container - do this here because file is loaded async + this._widgetContainer.setArrayMgrs(this._createArrayManagers(_data)); // Asynchronously load the XET file - et2_loadXMLFromURL(_url, function (_xmldoc) { + return et2_loadXMLFromURL(_url, function (_xmldoc) { // Scan for templates and store them for (let i = 0; i < _xmldoc.childNodes.length; i++) { const template = _xmldoc.childNodes[i]; @@ -551,9 +555,6 @@ export class etemplate2 { } _load.apply(this, []); }, this); - // Split the given data into array manager objects and pass those to the - // widget container - do this here because file is loaded async - this._widgetContainer.setArrayMgrs(this._createArrayManagers(_data)); }); } /** @@ -1015,8 +1016,7 @@ export class etemplate2 { // set id in case serverside returned a different template this._DOMContainer.id = this.uniqueId = data.DOMNodeID; // @ts-ignore - this.load(data.name, data.url, data.data); - return true; + return this.load(data.name, data.url, data.data); } else { // Not etemplate @@ -1034,8 +1034,7 @@ export class etemplate2 { uniqueId = data.DOMNodeID.replace('.', '-') + '-' + data['open_target']; } const et2 = new etemplate2(node, data.menuaction, uniqueId); - et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']); - return true; + return et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']); } else { egw.debug("error", "Could not find target node %s", data.DOMNodeId); diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index f0af49f404..1c67306dd5 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -501,7 +501,8 @@ export class etemplate2 * @param {function} _callback called after template is loaded * @param {object} _app local app object * @param {boolean} _no_et2_ready true: do not send et2_ready, used by et2_dialog to not overwrite app.js et2 object - * @param {string} _open_target flag of string to distinguishe between tab target and normal app object + * @param {string} _open_target flag of string to distinguish between tab target and normal app object + * @return Promise */ load(_name, _url, _data, _callback?, _app?, _no_et2_ready?, _open_target?) { @@ -572,7 +573,7 @@ export class etemplate2 if (appname) { promisses.push(egw(currentapp, window).includeJS('/'+appname+'/js/app.js', undefined, undefined, egw.webserverUrl)); } - Promise.all(promisses).catch((err) => { + return Promise.all(promisses).catch((err) => { console.log("et2.load(): error loading lang-files and app.js: "+err.message); }).then(() => { this.clear(); @@ -774,8 +775,12 @@ export class etemplate2 throw e; } } + // Split the given data into array manager objects and pass those to the + // widget container - do this here because file is loaded async + this._widgetContainer.setArrayMgrs(this._createArrayManagers(_data)); + // Asynchronously load the XET file - et2_loadXMLFromURL(_url, function (_xmldoc) + return et2_loadXMLFromURL(_url, function (_xmldoc) { // Scan for templates and store them @@ -788,10 +793,6 @@ export class etemplate2 } _load.apply(this, []); }, this); - - // Split the given data into array manager objects and pass those to the - // widget container - do this here because file is loaded async - this._widgetContainer.setArrayMgrs(this._createArrayManagers(_data)); }); } @@ -1389,8 +1390,7 @@ export class etemplate2 // set id in case serverside returned a different template this._DOMContainer.id = this.uniqueId = data.DOMNodeID; // @ts-ignore - this.load(data.name, data.url, data.data); - return true; + return this.load(data.name, data.url, data.data); } else { @@ -1411,8 +1411,7 @@ export class etemplate2 uniqueId = data.DOMNodeID.replace('.', '-') + '-' + data['open_target']; } const et2 = new etemplate2(node, data.menuaction, uniqueId); - et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']); - return true; + return et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']); } else { diff --git a/api/js/jsapi/egw_json.js b/api/js/jsapi/egw_json.js index e6be183e65..ebdd42f9dc 100644 --- a/api/js/jsapi/egw_json.js +++ b/api/js/jsapi/egw_json.js @@ -19,12 +19,13 @@ */ import './egw_core.js'; import './egw_utils.js'; +import {etemplate2} from "../etemplate/etemplate2"; /** * Module sending json requests * - * @param {string} _app application name object is instanciated for - * @param {object} _wnd window object is instanciated for + * @param {string} _app application name object is instantiated for + * @param {object} _wnd window object is instantiated for */ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) { @@ -308,6 +309,17 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) return; } + // defer apply's for app.* after et2_load is finished + let apply_app = []; + if (data.response.filter((res) => res.type === 'et2_load').length) + { + apply_app = data.response.filter((res) => res.type === 'apply' && res.data.func.substr(0, 4) === 'app.'); + if (apply_app.length) + { + data.response = data.response.filter((res) => !(res.type === 'apply' && res.data.func.substr(0, 4) === 'app.')); + } + } + // Flag for only data response - don't call callback if only data var only_data = (data.response.length > 0); @@ -328,7 +340,7 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) try { // Get a reference to the plugin var plugin = handler_level[res.type][j]; - if (res.type.match(/et2_load/)) + if (res.type === 'et2_load') { if (egw.preference('show_generation_time', 'common', false) == "1") { @@ -345,10 +357,15 @@ egw.extend('json', egw.MODULE_WND_LOCAL, function(_app, _wnd) } } // Call the plugin callback - plugin.callback.call( + const promise = plugin.callback.call( plugin.context ? plugin.context : this.context, res.type, res, this ); + // defer apply_app's after et2_load is finished (it returns a promise for that) + if (res.type === 'et2_load' && apply_app.length && typeof promise.then === 'function') + { + promise.then(() => this.handleResponse({response: apply_app})); + } } catch(e) { var msg = e.message ? e.message : e + ''; var stack = e.stack ? "\n-- Stack trace --\n" + e.stack : "";