From 9d61a2ef176364e51de682efe25712498c5009c5 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 13 Jul 2023 16:18:11 -0600 Subject: [PATCH 1/9] Changes for opening etemplate in a dialog - openDialog() now returns Promise - fixed etemplate reload --- api/js/etemplate/Et2Dialog/Et2Dialog.ts | 2 +- api/js/etemplate/etemplate2.ts | 43 ++++++++++++++++--------- api/js/jsapi/egw_app.ts | 30 +++++++++++++---- api/js/jsapi/egw_open.js | 26 ++++++++++++--- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/api/js/etemplate/Et2Dialog/Et2Dialog.ts b/api/js/etemplate/Et2Dialog/Et2Dialog.ts index f2c7e8c8dd..d4c3fd73a7 100644 --- a/api/js/etemplate/Et2Dialog/Et2Dialog.ts +++ b/api/js/etemplate/Et2Dialog/Et2Dialog.ts @@ -477,7 +477,7 @@ export class Et2Dialog extends Et2Widget(SlotMixin(SlDialog)) */ close() { - this.hide(); + return this.hide(); } addOpenListeners() diff --git a/api/js/etemplate/etemplate2.ts b/api/js/etemplate/etemplate2.ts index e610d60218..812b4d040f 100644 --- a/api/js/etemplate/etemplate2.ts +++ b/api/js/etemplate/etemplate2.ts @@ -146,6 +146,7 @@ import './et2_extension_nextmatch'; import './et2_extension_customfields'; import './vfsSelectUI'; import {Et2Tabs} from "./Layout/Et2Tabs/Et2Tabs"; +import {Et2Dialog} from "./Et2Dialog/Et2Dialog"; /** @@ -317,7 +318,7 @@ export class etemplate2 */ public clear(_keep_app_object? : boolean, _keep_session? : boolean) { - jQuery(this._DOMContainer).trigger('clear'); + this.DOMContainer.dispatchEvent(new Event("clear", {bubbles: true})); // Remove any handlers on window (resize) if(this.uniqueId) @@ -1516,7 +1517,9 @@ export class etemplate2 // Check the parameters const data = _response.data; // window-close does NOT send data.DOMNodeID! - const dialog = document.querySelector('et2-dialog > form'+(data.DOMNodeID?'#'+data.DOMNodeID:'.dialog_content'))?.parentNode; + const dialog = document.querySelector('et2-dialog > form' + (data.DOMNodeID ? '#' + data.DOMNodeID : '.dialog_content'))?.parentNode ?? + // Reloaded into same container + (this?.DOMContainer?.parentNode instanceof Et2Dialog ? this.DOMContainer.parentNode : undefined); if (dialog) { @@ -1527,7 +1530,7 @@ export class etemplate2 // handle Api\Framework::refresh_opener() if(Array.isArray(data['refresh-opener'])) { - if(window.opener)// && typeof window.opener.egw_refresh == 'function') + if(window.opener || dialog)// && typeof window.opener.egw_refresh == 'function') { const egw = window.egw(dialog ? window : opener); egw.refresh.apply(egw, data['refresh-opener']); @@ -1564,9 +1567,7 @@ export class etemplate2 } if (dialog) { - dialog.close(); - dialog.parentNode.removeChild(dialog); - return Promise.resolve(); + return dialog.close(); } egw.close(); return true; @@ -1592,14 +1593,16 @@ export class etemplate2 // regular et2 re-load if(typeof data.url == "string" && typeof data.data === 'object') { + let load : Promise; // @ts-ignore if(this && typeof this.load == 'function') { // Called from etemplate // set id in case serverside returned a different template this._DOMContainer.id = this.uniqueId = data.DOMNodeID; + // @ts-ignore - return this.load(data.name, data.url, data.data); + load = this.load(data.name, data.url, data.data); } else { @@ -1623,20 +1626,30 @@ export class etemplate2 uniqueId = data.DOMNodeID.replace('.', '-') + '-' + data['open_target']; } const et2 = new etemplate2(node, data.data.menuaction, uniqueId); - return et2.load(data.name, data.url, data.data, null, null, null, data['fw-target']) - .then(() => - { - if(dialog) - { - dialog._adoptTemplateButtons(); - } - }); + load = 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); } } + + // Extra handling for being loaded into a Et2Dialog + if(dialog) + { + load.then(() => + { + // Move footer type buttons into dialog footer + const buttons = dialog._adoptTemplateButtons(); + + // Make sure adopted buttons are removed on clear + dialog.addEventListener("clear", () => + { + buttons.forEach(n => n.remove()); + }); + }); + } + return load; } throw("Error while parsing et2_load response"); diff --git a/api/js/jsapi/egw_app.ts b/api/js/jsapi/egw_app.ts index 6492cd9a19..f603d03c70 100644 --- a/api/js/jsapi/egw_app.ts +++ b/api/js/jsapi/egw_app.ts @@ -751,25 +751,43 @@ export abstract class EgwApp /** * Opens _menuaction in an Et2Dialog * - * Equivalent to egw.openDialog, thought this one works in popups too. + * Equivalent to egw.openDialog, though this one works in popups too. * * @param _menuaction - * @return Promise + * @return Promise */ openDialog(_menuaction : string) { - return this.egw.json(_menuaction.match(/^([^.:]+)/)[0] + '.jdots_framework.ajax_exec.template.' + _menuaction, + let resolver; + let rejector; + const dialog_promise = new Promise((resolve, reject) => + { + resolver = value => resolve(value); + rejector = reason => reject(reason); + }); + let request = this.egw.json(_menuaction.match(/^([^.:]+)/)[0] + '.jdots_framework.ajax_exec.template.' + _menuaction, ['index.php?menuaction=' + _menuaction, true], _response => { - if (Array.isArray(_response) && typeof _response[0] === 'string') + if(Array.isArray(_response) && typeof _response[0] === 'string') { - jQuery(_response[0]).appendTo(document.body); + let dialog = jQuery(_response[0]).appendTo(document.body); + if(dialog.length > 0 && dialog.get(0)) + { + resolver(dialog.get(0)); + } + else + { + console.log("Unable to add dialog with dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Unable to add dialog")); + } } else { - console.log("Invalid response to dialogExec('"+_menuaction+"')", _response); + console.log("Invalid response to dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Invalid response to dialogExec('" + _menuaction + "')")); } }).sendRequest(); + return dialog_promise; } /** diff --git a/api/js/jsapi/egw_open.js b/api/js/jsapi/egw_open.js index 63209bfda1..b3bdfe92fb 100644 --- a/api/js/jsapi/egw_open.js +++ b/api/js/jsapi/egw_open.js @@ -386,22 +386,40 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd) * For popups you have to use the app.ts method openDialog(), which creates the dialog in the correct window / popup. * * @param string _menuaction - * @return Promise + * @return Promise */ openDialog: function(_menuaction) { - return this.json(_menuaction.match(/^([^.:]+)/)[0] + '.jdots_framework.ajax_exec.template.' + _menuaction, + let resolver; + let rejector; + const dialog_promise = new Promise((resolve, reject) => + { + resolver = value => resolve(value); + rejector = reason => reject(reason); + }); + let request = egw.json(_menuaction.match(/^([^.:]+)/)[0] + '.jdots_framework.ajax_exec.template.' + _menuaction, ['index.php?menuaction=' + _menuaction, true], _response => { if (Array.isArray(_response) && typeof _response[0] === 'string') { - jQuery(_response[0]).appendTo(_wnd.document.body); + let dialog = jQuery(_response[0]).appendTo(_wnd.document.body); + if (dialog.length > 0 && dialog.get(0)) + { + resolver(dialog.get(0)); + } + else + { + console.log("Unable to add dialog with dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Unable to add dialog")); + } } else { - console.log("Invalid response to dialogExec('"+_menuaction+"')", _response); + console.log("Invalid response to dialogExec('" + _menuaction + "')", _response); + rejector(new Error("Invalid response to dialogExec('" + _menuaction + "')")); } }).sendRequest(); + return dialog_promise; }, /** From 4a7e5824c32d457f9d1975a6b027a86e79c384b7 Mon Sep 17 00:00:00 2001 From: nathan Date: Thu, 13 Jul 2023 16:29:14 -0600 Subject: [PATCH 2/9] Calendar: further adaptations to using openDialog() Fixes canceling quick add doesn't remove placeholder, invalid event gave no feedback --- calendar/inc/class.calendar_uiforms.inc.php | 43 +-------------------- calendar/js/app.ts | 26 +++---------- 2 files changed, 7 insertions(+), 62 deletions(-) diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 37ee762108..899dcfefea 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -33,8 +33,6 @@ class calendar_uiforms extends calendar_ui { var $public_functions = array( 'freetimesearch' => True, - 'ajax_add' => true, - 'ajax_conflicts' => true, 'edit' => true, 'process_edit' => true, 'export' => true, @@ -1025,7 +1023,7 @@ class calendar_uiforms extends calendar_ui } else { - $msg = lang('Error: saving the event !!!'); + $msg = ($msg ? $msg.'
':'') . lang('Error: saving the event !!!'); } break; @@ -1477,45 +1475,6 @@ class calendar_uiforms extends calendar_ui return strnatcasecmp($this->get_title($uid1), $this->get_title($uid2)); } - public function ajax_add() - { - // This tells etemplate to send as JSON response, not full - // This avoids errors from trying to send header again - if(Api\Json\Request::isJSONRequest()) - { - $GLOBALS['egw']->framework->response = Api\Json\Response::get(); - } - - $this->edit(); - } - - /** - * Get conflict dialog via ajax. Used by quick add. - * - */ - public function ajax_conflicts() - { - $content = $this->default_add_event(); - - // Process edit wants to see input values - $participants = array(1=> false); - $participants['cal_resources'] = ''; - foreach($content['participants'] as $id => $status) - { - $quantity = $role = ''; - calendar_so::split_status($status,$quantity,$role); - $participants[] = array( - 'uid' => $id, - 'status' => $status, - 'quantity' => $quantity, - 'role' => $role - ); - } - $content['participants'] = $participants; - $content['button'] = array('save' => true); - return $this->process_edit($content); - } - /** * Edit a calendar event * diff --git a/calendar/js/app.ts b/calendar/js/app.ts index 45fdb34d96..9c023dbf9a 100644 --- a/calendar/js/app.ts +++ b/calendar/js/app.ts @@ -2062,30 +2062,16 @@ export class CalendarApp extends EgwApp { menuaction += '&'+name+'='+encodeURIComponent(options[name]); } - return this.egw.openDialog(menuaction).then(_dialog => + return this.egw.openDialog(menuaction).then(dialog => { - // it would be much nicer if openDialog returns Promise, so we can use it, without searching the dialog - // in a window.setTimeout in the DOM ... - window.setTimeout(() => + // When the dialog is closed, clean up the placeholder + dialog.getComplete().then(() => { - const dialog = document.querySelector('et2-dialog > form.dialog_content')?.parentNode; - if (dialog) + if(event) { - dialog.callback = _button => - { - if (event) - { - event.destroy(); - } - // a little quicker than waiting for the server to close it - if (_button === "calendar-add_button[cancel]") - { - dialog.hide(); - } - return false; - } + event.destroy(); } - }, 500); + }); }); } From a379df8aeb8466dd979e6e83b4f80bb7ee5fc5b9 Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 14 Jul 2023 08:07:18 +0200 Subject: [PATCH 3/9] WIP Mail REST API: calendar quick-add show title required client-side --- calendar/templates/default/add.xet | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/calendar/templates/default/add.xet b/calendar/templates/default/add.xet index 29fac64885..27afe6e8b6 100644 --- a/calendar/templates/default/add.xet +++ b/calendar/templates/default/add.xet @@ -13,11 +13,11 @@ - + - + @@ -25,8 +25,8 @@ - - + + \ No newline at end of file From b1a4a45bb1526cd7e5367ae28635d16d1f5c3b7a Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 14 Jul 2023 08:54:07 +0200 Subject: [PATCH 4/9] add Promise return type to openDialog --- api/js/jsapi/egw_app.ts | 2 +- api/js/jsapi/egw_global.d.ts | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/api/js/jsapi/egw_app.ts b/api/js/jsapi/egw_app.ts index f603d03c70..fd243a08bb 100644 --- a/api/js/jsapi/egw_app.ts +++ b/api/js/jsapi/egw_app.ts @@ -756,7 +756,7 @@ export abstract class EgwApp * @param _menuaction * @return Promise */ - openDialog(_menuaction : string) + openDialog(_menuaction : string) : Promise { let resolver; let rejector; diff --git a/api/js/jsapi/egw_global.d.ts b/api/js/jsapi/egw_global.d.ts index 93e71adad2..2217e097aa 100644 --- a/api/js/jsapi/egw_global.d.ts +++ b/api/js/jsapi/egw_global.d.ts @@ -14,6 +14,7 @@ */ import type {EgwApp} from "./egw_app"; +import type {Et2Dialog} from "../etemplate/Et2Dialog/Et2Dialog"; /** * Global egw object (for now created by the diverse JavaScript files) with a TypeScript interface @@ -1130,9 +1131,9 @@ declare interface IegwWndLocal extends IegwGlobal * For popups you have to use the app.ts method openDialog(), which creates the dialog in the correct window / popup. * * @param string _menuaction - * @return Promise + * @return Promise */ - openDialog(_menuaction : string) : Promise; + openDialog(_menuaction : string) : Promise; /** * Open a (centered) popup window with given size and url From 40a4d387763a0307ef73ddaee3e2ad57c2e97cd9 Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 14 Jul 2023 10:41:48 +0200 Subject: [PATCH 5/9] fix not translated context menu window.egw with a reference to the main window in a popup, egw(_app, window) gives one with correct window context / translation in a popup (_app does not matter for translations, they are requested to be loaded from server-side) --- api/js/egw_action/EgwAction.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/js/egw_action/EgwAction.ts b/api/js/egw_action/EgwAction.ts index 9c1be07dd6..9243877669 100644 --- a/api/js/egw_action/EgwAction.ts +++ b/api/js/egw_action/EgwAction.ts @@ -271,7 +271,7 @@ export class EgwAction { this is an egw Object as defined in egw_core.js probably not because it changes on runtime */ - const localEgw: IegwAppLocal = window.egw(_app); + const localEgw: IegwAppLocal = window.egw(_app, window); //replaced jQuery calls if (Array.isArray(_actions)) { //_actions is now an object for sure From 76aa1b6b268862270c35e20994d1a432eca91882 Mon Sep 17 00:00:00 2001 From: ralf Date: Fri, 14 Jul 2023 10:45:42 +0200 Subject: [PATCH 6/9] clean up translations a bit --- preferences/lang/egw_de.lang | 1 - preferences/lang/egw_en.lang | 1 - preferences/lang/egw_fr.lang | 1 - preferences/lang/egw_it.lang | 1 - preferences/lang/egw_km.lang | 1 - preferences/lang/egw_sk.lang | 1 - 6 files changed, 6 deletions(-) diff --git a/preferences/lang/egw_de.lang b/preferences/lang/egw_de.lang index c7687cb690..4956ce8b41 100644 --- a/preferences/lang/egw_de.lang +++ b/preferences/lang/egw_de.lang @@ -144,7 +144,6 @@ re-enter your password preferences de Neues Passwort wiederholen read prefs for the specified application. preferences de Liest Einstellungen von den ausgewählten Anwendungen. reset qrcode preferences de QR-Code zurücksetzen revoke preferences de Zurückziehen -revoke acccess tokens preferences de Tokens zurückziehen revoke this token preferences de Diese(s) Token(s) zurückziehen rich text editor theme preferences de Farbschema des Editors scan qrcode with a time-based one-time password (totp) app: preferences de QR-Code mit einer App für zeitbasierende Einmalpasswörter (TOTP Time-based One-time Password) abscannen: diff --git a/preferences/lang/egw_en.lang b/preferences/lang/egw_en.lang index 9adf2d32e7..ca0209752a 100644 --- a/preferences/lang/egw_en.lang +++ b/preferences/lang/egw_en.lang @@ -144,7 +144,6 @@ re-enter your password preferences en Re-enter your password read prefs for the specified application. preferences en Read preferences for the specified application. reset qrcode preferences en Reset QRCode revoke preferences en Revoke -revoke acccess tokens preferences en Revoke Acccess Tokens revoke this token preferences en Revoke this token rich text editor theme preferences en Rich text editor theme scan qrcode with a time-based one-time password (totp) app: preferences en Scan QRCode with a Time-based One-time Password (TOTP) App: diff --git a/preferences/lang/egw_fr.lang b/preferences/lang/egw_fr.lang index 91cd39101c..b5878ad2ae 100644 --- a/preferences/lang/egw_fr.lang +++ b/preferences/lang/egw_fr.lang @@ -143,7 +143,6 @@ re-enter your password preferences fr Ré-entrez votre mot de passe read prefs for the specified application. preferences fr Lire les préférences pour l'apllication spécifiée. reset qrcode preferences fr Réinitialiser le QRCode revoke preferences fr Révoquer -revoke acccess tokens preferences fr Révoquer les jetons d'accès revoke this token preferences fr Révoquer ce jeton rich text editor theme preferences fr Thème de l'éditeur de texte scan qrcode with a time-based one-time password (totp) app: preferences fr Scanner le QRCode avec un mot de passe à durée limitée (TOTP) : diff --git a/preferences/lang/egw_it.lang b/preferences/lang/egw_it.lang index fe6185a53c..23ebd72c79 100644 --- a/preferences/lang/egw_it.lang +++ b/preferences/lang/egw_it.lang @@ -137,7 +137,6 @@ re-enter your password preferences it Reinserisci la password read prefs for the specified application. preferences it Legge le preferenze per l'applicazione specificata. reset qrcode preferences it Reimposta codice QR revoke preferences it Revoca -revoke acccess tokens preferences it Revoca i token di accesso revoke this token preferences it Revoca questo token rich text editor theme preferences it Tema editor avanzato scan qrcode with a time-based one-time password (totp) app: preferences it Scansiona il codice QR con una apposita app: diff --git a/preferences/lang/egw_km.lang b/preferences/lang/egw_km.lang index 7554c366e4..70eefe7d4a 100644 --- a/preferences/lang/egw_km.lang +++ b/preferences/lang/egw_km.lang @@ -142,7 +142,6 @@ re-enter your password preferences km បញ្ចូលពាក្យសម្ read prefs for the specified application. preferences km អានចំណូលចិត្តសម្រាប់កម្មវិធីដែលបានបញ្ជាក់។ reset qrcode preferences km កំណត់ QRCode ឡើងវិញ revoke preferences km ដកហូត -revoke acccess tokens preferences km ដកហូតសញ្ញាសម្ងាត់ចូលប្រើ revoke this token preferences km ដកហូតសញ្ញាសម្ងាត់នេះ។ rich text editor theme preferences km ស្បែកកម្មវិធីនិពន្ធអត្ថបទសម្បូរបែប scan qrcode with a time-based one-time password (totp) app: preferences km ស្កែន QRCode ដោយប្រើកម្មវិធី Time-based One-time Password (TOTP)៖ diff --git a/preferences/lang/egw_sk.lang b/preferences/lang/egw_sk.lang index f1922f1eea..9331f5933e 100644 --- a/preferences/lang/egw_sk.lang +++ b/preferences/lang/egw_sk.lang @@ -139,7 +139,6 @@ re-enter your password preferences sk Zadajte heslo znovu read prefs for the specified application. preferences sk Načítať predvoľby pre vybranú aplikáciu. reset qrcode preferences sk Reset QR kódu revoke preferences sk Odvolať -revoke acccess tokens preferences sk Odvolať prístupový token revoke this token preferences sk Odvolať tento token rich text editor theme preferences sk Téma textového editora scan qrcode with a time-based one-time password (totp) app: preferences sk Naskenujte QR kód aplikáciou pre časové jednorázové heslo (TOTP): From 1b6525eb127d22d994dbf983db40a035541427ec Mon Sep 17 00:00:00 2001 From: StefanU Date: Fri, 14 Jul 2023 11:46:33 +0200 Subject: [PATCH 7/9] Update MailFolderTrash.svg Change to new EGw design --- .../images/dhtmlxtree/MailFolderTrash.svg | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/api/templates/default/images/dhtmlxtree/MailFolderTrash.svg b/api/templates/default/images/dhtmlxtree/MailFolderTrash.svg index dbad469bb0..4f48e18f1b 100644 --- a/api/templates/default/images/dhtmlxtree/MailFolderTrash.svg +++ b/api/templates/default/images/dhtmlxtree/MailFolderTrash.svg @@ -1,14 +1,4 @@ - - - - - + + + From ffe6ca7d5d43988f727dae6ac993fe6b0bb95b16 Mon Sep 17 00:00:00 2001 From: StefanU Date: Fri, 14 Jul 2023 11:48:45 +0200 Subject: [PATCH 8/9] Update delete.svg Change to new EGw design --- api/templates/default/images/delete.svg | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/api/templates/default/images/delete.svg b/api/templates/default/images/delete.svg index dbad469bb0..4f48e18f1b 100644 --- a/api/templates/default/images/delete.svg +++ b/api/templates/default/images/delete.svg @@ -1,14 +1,4 @@ - - - - - + + + From 00b15e2f541402084bc053ffdd20a251086dbe18 Mon Sep 17 00:00:00 2001 From: StefanU Date: Fri, 14 Jul 2023 12:03:25 +0200 Subject: [PATCH 9/9] Update calendar.svg File optimisation, colour corrected --- api/templates/default/images/calendar.svg | 25 +++-------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/api/templates/default/images/calendar.svg b/api/templates/default/images/calendar.svg index 621159a8af..2d798bfa27 100644 --- a/api/templates/default/images/calendar.svg +++ b/api/templates/default/images/calendar.svg @@ -1,23 +1,4 @@ - - - - - - - - - - - + + +