From a8e10cdc648184f4b5a2f7361c6ef13381131373 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Fri, 9 Mar 2018 17:48:09 +0100 Subject: [PATCH] WIP framework's popup storage/restore: - Keep tracking of popup's window object and restore it when needed - Implement a method to check an already opened popup and execute a method in the selected popup context - Fix vcard import into opened compose windows not working --- addressbook/js/app.js | 61 +------------ api/js/framework/fw_base.js | 111 +++++++++++++++++++++++ api/js/jsapi/egw_open.js | 176 ++++++++++++++++++++---------------- api/js/jsapi/egw_utils.js | 6 ++ mail/js/app.js | 5 +- 5 files changed, 221 insertions(+), 138 deletions(-) diff --git a/addressbook/js/app.js b/addressbook/js/app.js index b60d3d5fa4..ad2a8ba827 100644 --- a/addressbook/js/app.js +++ b/addressbook/js/app.js @@ -769,72 +769,19 @@ app.classes.addressbook = AppJS.extend( */ adb_mail_vcard: function(_action, _elems) { - var app_registry = egw.link_get_registry('mail'); - var link = egw().link("/index.php","menuaction="+app_registry['add']['menuaction']); + var link = ''; var content = {vcard:{file:[], type:[]}}; - - // Get open compose windows - var compose = egw.getOpenWindows("mail", "(^compose_)|(^mail.compose)"); - for (var i = 0; i < _elems.length; i++) { var idToUse = _elems[i].id; var idToUseArray = idToUse.split('::'); idToUse = idToUseArray[1]; - link += "&preset[type][]="+encodeURIComponent("text/vcard; charset="+(egw.preference('vcard_charset', 'addressbook') || 'utf-8')); - link += "&preset[file][]="+encodeURIComponent("vfs://default/apps/addressbook/"+idToUse+"/.entry"); + link += "preset[type][]="+"text/vcard; charset="+(egw.preference('vcard_charset', 'addressbook') || 'utf-8')+'&'; + link += "preset[file][]="+"vfs://default/apps/addressbook/"+idToUse+"/.entry"+'&'; content.vcard.file.push("vfs://default/apps/addressbook/"+idToUse+"/.entry"); content.vcard.type.push("text/vcard; charset="+(egw.preference('vcard_charset', 'addressbook') || 'utf-8')); } - - if(compose.length == 1) - { - var popup = egw.open_link('',compose[0],'100x100','mail'); - popup.app.mail.setCompose(compose[0], content); - } - else if (compose.length > 1) - { - var buttons = [ - {text: this.egw.lang("Add"), id: "add", "class": "ui-priority-primary", "default": true}, - {text: this.egw.lang("Cancel"), id:"cancel"} - ]; - var c = []; - for(var i = 0; i < compose.length; i++) - { - var w = window.open('',compose[i],'100x100'); - if(w.closed) continue; - w.blur(); - c.push({label:w.document.title || egw.lang("compose"), compose:compose[i]}); - } - et2_createWidget("dialog", - { - callback: function(_button_id, _value) { - if (_value && _value.grid) - { - switch (_button_id) - { - case "add": - var w = egw.open_link('', _value.grid.compose,'100x100','mail'); - w.app.mail.setCompose(w.name, content); - return; - case "cancel": - } - } - }, - title: this.egw.lang("Select an opened compose dialog"), - buttons: buttons, - value:{content:{grid:c}}, - template: egw.webserverUrl+'/addressbook/templates/default/promptOpenedComposeDialog.xet?1', - resizable: false - }, et2_dialog._create_parent('addressbook')); - } - else if (typeof app_registry['view'] != 'undefined' && typeof app_registry['view_popup'] != 'undefined' ) - { - var w_h =app_registry['view_popup'].split('x'); - if (w_h[1] == 'egw_getWindowOuterHeight()') w_h[1] = (screen.availHeight>egw_getWindowOuterHeight()?screen.availHeight:egw_getWindowOuterHeight()); - egw_openWindowCentered2(link, '_blank', w_h[0], w_h[1], 'yes'); - } - + egw.openWithinWindow("mail", "setCompose", content, link, /mail.mail_compose.compose/); }, /** diff --git a/api/js/framework/fw_base.js b/api/js/framework/fw_base.js index bb576576a5..3c8b78dbb7 100644 --- a/api/js/framework/fw_base.js +++ b/api/js/framework/fw_base.js @@ -56,6 +56,9 @@ var fw_base = (function(){ "use strict"; return Class.extend( // Override the egw_getAppName function window.egw_getAppName = this.egw_getAppName; + + // keep track of opened popups + this.popups = []; }, /** @@ -133,6 +136,8 @@ var fw_base = (function(){ "use strict"; return Class.extend( } this.applications[this.appData.appName] = this.appData; + + this.popups.concat(this.registerOpenedPopus(app.name)); } // else display the default application @@ -793,6 +798,7 @@ var fw_base = (function(){ "use strict"; return Class.extend( var windowID = egw(parentWindow).openPopup(_url, _width, _height, _windowName, _app, true, _status, true); windowID.framework = this; + this.popups.push(windowID); if (navigate) { @@ -802,6 +808,111 @@ var fw_base = (function(){ "use strict"; return Class.extend( if (_returnID !== false) return windowID; }, + registerOpenedPopus: function (_app) + { + var w = Object.keys(egw.getOpenWindows(_app)); + var popups = []; + var popup; + for (var i=0; i < w.length; i++) + { + try{ + popup = window.open('', w[i], '100x100'); + if (popup.location.href == "about:blank") + { + popup.close(); + egw(window).windowClosed(_app, popup); + } + if (popup && egw.is_popup(popup)) popups.push(popup); + }catch(e) + { + if (popup) + { + popup.close(); + egw.windowClosed(_app, popup); + } + continue; + } + } + return popups; + }, + + /** + * Check if given window is a "popup" alike, returning integer or undefined if not + * + * @param {DOMWindow} _wnd + * @returns {Number|undefined} + */ + popup_idx: function(_wnd) + { + if (typeof window.framework.popups != 'undefined') + { + for (var i=0; i < window.framework.popups.length; i++) + { + if (window.framework.popups[i] === _wnd) + { + return i; + } + } + } + return undefined; + }, + + /** + * @param {window} _wnd window object which suppose to be closed + */ + popup_close:function (_wnd) + { + var i = this.popup_idx(_wnd); + + if (i !== undefined) + { + // Close the matched popup + this.popups.splice(i,1); + } + _wnd.close(); + }, + + /** + * Collect and close all already closed windowss + */ + popups_garbage_collector: function () + { + for (var i=0; i < this.popups.length; i++) + { + if (this.popups[i].closed) this.popups.splice(i,1); + } + }, + + /** + * get popups based on application name and regexp + * @param {string} _app app name + * @param {regexp} regex regular expression to check against location.href url + * + * @returns {Array} returns array of windows object + */ + popups_get: function(_app, regex) + { + var popups = []; + for (var i=0; i < this.popups.length; i++) + { + if (!this.popups[i].closed && this.popups[i].egw_appName == _app) { + popups.push(this.popups[i]); + + } + } + if (regex) + { + for (var j=0; j < popups.length; j++) + { + if (!popups[j].location.href.match(regex)) + { + popups.splice(j,1); + } + } + } + return popups; + }, + /** * Get application window * @param {type} _app diff --git a/api/js/jsapi/egw_open.js b/api/js/jsapi/egw_open.js index 6e38fe94ba..274eca001c 100644 --- a/api/js/jsapi/egw_open.js +++ b/api/js/jsapi/egw_open.js @@ -55,9 +55,6 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd) cc: match['cc'] || [], bcc: match['bcc'] || [] }; - var popup; - // Get open compose windows - var compose = egw.getOpenWindows("mail", /(^compose_)||(^mail.compose)/); // Encode html entities in the URI, otheerwise server XSS protection wont // allow it to pass, because it may get mistaken for some forbiden tags, @@ -65,82 +62,8 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd) // including "<" would get mistaken for tag, and server will cut it off. uri = uri.replace(//g,'>'); - if(compose.length == 0) - { - // No compose windows, might be no mail app.js - // We really want to use mail_compose() here - - // Accoring to microsoft, IE 10/11 can only accept a url with 2083 caharacters - // therefore we need to send request to compose window with POST method - // instead of GET. We create a temporary
and will post emails. - // ** WebServers and other browsers also have url length limit: - // Firefox:~ 65k, Safari:80k, Chrome: 2MB, Apache: 4k, Nginx: 4k - if (uri.length > 2083) - { - popup = egw.open('','mail','add','','compose__','mail'); - var $tmpForm = jQuery(document.createElement('form')).appendTo('body'); - var $tmpInput = jQuery(document.createElement('input')).attr({name:"preset[mailto]", type:"text", value: uri}); - var $tmpSubmitInput = jQuery(document.createElement('input')).attr({type:"submit"}); - // Set the temporary form's attributes - $tmpForm.attr({target:popup.name, action:"index.php?menuaction=mail.mail_compose.compose", method:"post"}) - .append($tmpInput) - .append($tmpSubmitInput); - $tmpForm.submit(); - // Remove the form after submit - $tmpForm.remove(); - } - else // simple GET request - { - egw.open('','mail','add',{'preset[mailto]': uri},'compose__','mail'); - } - } - if(compose.length == 1) - { - try { - popup = egw.open_link('',compose[0],'100x100','mail'); - popup.app.mail.setCompose(compose[0], content); - } catch(e) { - // Looks like a leftover window that wasn't removed from the list - egw.debug("warn", e.message); - popup.close(); - egw.windowClosed("mail",popup); - window.setTimeout(function() { - egw.open_link(uri); - console.debug("Trying again with ", uri); - }, 500); - } - } - else if(compose.length > 1) - { - // Need to prompt - var prompt = jQuery(document.createElement('ul')); - for(var i = 0; i < compose.length; i++) - { - var w = window.open('',compose[i],'100x100'); - if(w.closed) continue; - w.blur(); - var title = w.document.title || egw.lang("compose"); - jQuery("
  • "+ title + "
  • ") - .click(function() { - var w = egw.open_link('',jQuery(this).attr("data-window"),'100x100','mail'); - w.app.mail.setCompose(w.name, content); - prompt.dialog("close"); - }) - .appendTo(prompt); - } - _wnd.setTimeout(function() { - this.focus(); - }, 200); - var _buttons = {}; - _buttons[egw.lang("cancel")] = function() { - jQuery(this).dialog("close"); - }; - prompt.dialog({ - buttons: _buttons - }); - } + egw.openWithinWindow ("mail", "setCompose", content, {'preset[mailto]':uri}, /mail_compose.compose/); } - return { /** * View an EGroupware entry: opens a popup of correct size or redirects window.location to requested url @@ -509,6 +432,103 @@ egw.extend('open', egw.MODULE_WND_LOCAL, function(_egw, _wnd) popup.close(); return false; } + }, + + /** + * This function helps to append content/ run commands into an already + * opened popup window. Popup winodws now are getting stored in framework + * object and can be retrived/closed from framework. + * + * @param {string} _app name of application to be requested its popups + * @param {string} _method application method implemented in app.js + * @param {object} _content content to be passed to method + * @param {string|object} _extra url or object of extra + * @param {regex} _regexp regular expression to get specific popup with matched url + */ + openWithinWindow: function (_app, _method, _content, _extra, _regexp) + { + var popups = window.framework.popups_get(_app, _regexp); + + for(var i = 0; i < popups.length; i++) + { + if(popups[i].closed) + { + window.framework.popups_grabage_collector(); + } + } + + if(popups.length == 1) + { + try { + popups[0].app[_app][_method](popups[0], _content); + } + catch(e) { + window.setTimeout(function() { + egw.open('', _app, 'add', _extra, _app, _app); + }); + } + } + else if (popups.length > 1) + { + var buttons = [ + {text: this.lang("Add"), id: "add", "class": "ui-priority-primary", "default": true}, + {text: this.lang("Cancel"), id:"cancel"} + ]; + var c = []; + for(var i = 0; i < popups.length; i++) + { + c.push({label:popups[i].document.title || this.lang(_app), index:i}); + } + et2_createWidget("dialog", + { + callback: function(_button_id, _value) { + if (_value && _value.grid) + { + switch (_button_id) + { + case "add": + popups[_value.grid.index].app[_app][_method](popups[_value.grid.index], _content); + return; + case "cancel": + } + } + }, + title: this.lang("Select an opened dialog"), + buttons: buttons, + value:{content:{grid:c}}, + template: this.webserverUrl+'/api/templates/default/promptOpenedDialog.xet?1', + resizable: false + }, et2_dialog._create_parent(this.app_name())); + } + else + { + // No compose windows, might be no mail app.js + // We really want to use mail_compose() here + + // Accoring to microsoft, IE 10/11 can only accept a url with 2083 caharacters + // therefore we need to send request to compose window with POST method + // instead of GET. We create a temporary
    and will post emails. + // ** WebServers and other browsers also have url length limit: + // Firefox:~ 65k, Safari:80k, Chrome: 2MB, Apache: 4k, Nginx: 4k + if (_extra.length > 2083) + { + var popup = egw.open('', _app, 'add', '', '', _app); + var $tmpForm = jQuery(document.createElement('form')).appendTo('body'); + var $tmpInput = jQuery(document.createElement('input')).attr({name:Object.keys(_extra)[0], type:"text", value: _extra}); + var $tmpSubmitInput = jQuery(document.createElement('input')).attr({type:"submit"}); + // Set the temporary form's attributes + $tmpForm.attr({target:popup.name, action:"index.php?menuaction=mail.mail_compose.compose", method:"post"}) + .append($tmpInput) + .append($tmpSubmitInput); + $tmpForm.submit(); + // Remove the form after submit + $tmpForm.remove(); + } + else // simple GET request + { + egw.open('', _app, 'add', _extra, _app, _app); + } + } } }; }); diff --git a/api/js/jsapi/egw_utils.js b/api/js/jsapi/egw_utils.js index 21b426e4b1..f83de851e7 100644 --- a/api/js/jsapi/egw_utils.js +++ b/api/js/jsapi/egw_utils.js @@ -270,6 +270,12 @@ egw.extend('utils', egw.MODULE_GLOBAL, function() */ storeWindow: function(appname, popup) { + if (popup.opener) popup.opener.framework.popups_garbage_collector(); + if (popup.opener && popup.opener.framework && egw.is_popup(popup) + && typeof popup.opener.framework.popup_idx(popup) == 'undefined') + { + popup.opener.framework.popups.push(popup); + } // Don't store if it has no name if(!popup.name || ['_blank'].indexOf(popup.name) >= 0) { diff --git a/mail/js/app.js b/mail/js/app.js index 39cb717e82..d901e686e2 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -656,7 +656,7 @@ app.classes.mail = AppJS.extend( * @function * @memberOf mail * - * @param {String} window_name The name of an open content window. + * @param {window object} compose compose window object * @param {object} content * * @description content Data to set into the window's fields @@ -666,10 +666,9 @@ app.classes.mail = AppJS.extend( * * @return {boolean} Success */ - setCompose: function(window_name, content) + setCompose: function(compose, content) { // Get window - var compose = window.open('', window_name); if(!compose || compose.closed) return false; // Get etemplate of popup