From 178bca7f7a23a4661f0cdb50e4f46c4f7256d343 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 8 Oct 2014 20:02:59 +0000 Subject: [PATCH] fix redirect loop initiated eg. by saving a mail, caused by document.location=url triggering etemplate unload handler and destroying et2 request, identical redirect url detected by jdots framework causes refresh via nextmatch --> next redirect --- etemplate/inc/class.etemplate_request.inc.php | 2 ++ etemplate/js/et2_extension_nextmatch.js | 26 +++++++++++-------- etemplate/js/etemplate2.js | 18 ++++++++++++- mail/js/app.js | 20 ++++---------- 4 files changed, 39 insertions(+), 27 deletions(-) diff --git a/etemplate/inc/class.etemplate_request.inc.php b/etemplate/inc/class.etemplate_request.inc.php index 0b297ed2fd..7f36dd36b9 100644 --- a/etemplate/inc/class.etemplate_request.inc.php +++ b/etemplate/inc/class.etemplate_request.inc.php @@ -177,6 +177,8 @@ class etemplate_request list($app) = explode('.', $_GET['menuaction']); $index_url = isset($GLOBALS['egw_info']['apps'][$app]['index']) ? '/index.php?menuaction='.$GLOBALS['egw_info']['apps'][$app]['index'] : '/'.$app.'/index.php'; + // add a unique token to redirect to avoid client-side framework tries refreshing via nextmatch + $index_url .= (strpos($index_url, '?') ? '&' : '?').'redirect='.microtime(true); error_log(__METHOD__."('$id', ...) eT2 request not found / expired --> redirecting app $app to $index_url (_GET[menuaction]=$_GET[menuaction], isJSONRequest()=".array2string(egw_json_request::isJSONRequest()).')'); if (egw_json_request::isJSONRequest()) { diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 3fdc557b3e..cc2b13f2ba 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -113,7 +113,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], "description": "Hide the second filter", "default": et2_no_init, }, - + "onselect": { "name": "onselect", "type": "js", @@ -206,7 +206,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], // Unbind handler used for toggling autorefresh $j(this.getInstanceManager().DOMContainer.parentNode).off('show.et2_nextmatch'); $j(this.getInstanceManager().DOMContainer.parentNode).off('hide.et2_nextmatch'); - + // Free the grid components this.dataview.free(); this.rowProvider.free(); @@ -303,10 +303,14 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], * Implements the et2_IResizeable interface - lets the dynheight manager * update the width and height and then update the dataview container. */ - resize: function() { - this.dynheight.update(function(_w, _h) { - this.dataview.resize(_w, _h); - }, this); + resize: function() + { + if (this.dynheight) + { + this.dynheight.update(function(_w, _h) { + this.dataview.resize(_w, _h); + }, this); + } }, /** @@ -731,7 +735,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], if(this.options.settings.columnselection_pref && app) { var size_pref = this.options.settings.columnselection_pref +"-size"; - + // If columnselection pref is missing prefix, add it in if(size_pref.indexOf('nextmatch') == -1) { @@ -902,7 +906,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], var oldCols = this.activeFilters.selectcols ? this.activeFilters.selectcols : []; this.activeFilters.selectcols = colDisplay; - + // We don't need to re-query if they've removed a column var changed = []; ColLoop: @@ -1094,7 +1098,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], this.options.settings.dataStorePrefix = list[0]; } this.controller.setPrefix(this.options.settings.dataStorePrefix); - + // Load the initial order /*this.controller.loadInitialOrder(this._getInitialOrder( this.options.settings.rows, this.options.settings.row_id @@ -1437,7 +1441,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], this._autorefresh_timer = setTimeout(jQuery.proxy(function() { // Check in case it was stopped / destroyed since if(!this._autorefresh_timer || !this.getInstanceManager()) return; - + $j(this.getInstanceManager().DOMContainer.parentNode).one('show.et2_nextmatch', // Important to use anonymous function instead of just 'this.refresh' because // of the parameters passed @@ -1621,7 +1625,7 @@ var et2_nextmatch = et2_DOMWidget.extend([et2_IResizeable, et2_IInput], this.resize(); } }, - + /** * Actions are handled by the controller, so ignore these during init. * diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js index 5507f3bd30..46bce852d6 100644 --- a/etemplate/js/etemplate2.js +++ b/etemplate/js/etemplate2.js @@ -124,7 +124,7 @@ etemplate2.prototype.resize = function() etemplate2.prototype.clear = function() { $j(this.DOMContainer).trigger('clear'); - + // Remove any handlers on window (resize) if(this.uniqueId) { @@ -246,6 +246,22 @@ etemplate2.prototype.unbind_unload = function() delete this.destroy_session; }; +/** + * Download a URL not triggering our unload handler and therefore destroying our et2 request + * + * @param {string} _url + */ +etemplate2.prototype.download = function(_url) +{ + // need to unbind unload handler to NOT destroy et2 session + this.unbind_unload(); + + document.location = _url; + + // bind unload handler again (can NOT do it direct, as this would be quick enough to be still triggered!) + window.setTimeout(jQuery.proxy(this.bind_unload, this), 100); +}; + /** * Loads the template from the given URL and sets the data object * diff --git a/mail/js/app.js b/mail/js/app.js index 69f86e98aa..b8228889c7 100644 --- a/mail/js/app.js +++ b/mail/js/app.js @@ -2174,7 +2174,7 @@ app.classes.mail = AppJS.extend( url += 'menuaction=mail.mail_ui.saveMessage'; // todo compose for Draft folder url += '&id='+_elems[0].id; //window.open(url,'_blank','dependent=yes,width=100,height=100,scrollbars=yes,status=yes'); - document.location = url; + this.et2._inst.download(url); }, /** @@ -2310,8 +2310,7 @@ app.classes.mail = AppJS.extend( windowName = windowName+'displayAttachment_'+mailid+'_'+attgrid.partID; width = 870; height = 600; - //document.location = url; - //return; + break; } egw_openWindowCentered(url,windowName,width,height); }, @@ -2418,8 +2417,7 @@ app.classes.mail = AppJS.extend( windowName = windowName+'displayAttachment_'+attgrid.file.replace(/\//g,"_"); width = 870; height = 600; - //document.location = url; - //return; + break; } egw_openWindowCentered(url,windowName,width,height); }, @@ -2441,15 +2439,12 @@ app.classes.mail = AppJS.extend( attgrid = this.et2.getArrayMgr("content").getEntry('mail_displayattachments')[widget.id.replace(/\[save\]/,'')]; } var url = window.egw_webserverUrl+'/index.php?'; - var width; - var height; - var windowName ='mail'; url += 'menuaction=mail.mail_ui.getAttachment'; // todo compose for Draft folder url += '&mode=save'; url += '&id='+mailid; url += '&part='+attgrid.partID; url += '&is_winmail='+attgrid.winmailFlag; - document.location = url; + this.et2._inst.download(url); }, saveAllAttachmentsToZip: function(tag_info, widget) @@ -2469,13 +2464,10 @@ app.classes.mail = AppJS.extend( attgrid = this.et2.getArrayMgr("content").getEntry('mail_displayattachments')[widget.id.replace(/\[save\]/,'')]; } var url = window.egw_webserverUrl+'/index.php?'; - var width; - var height; - var windowName ='mail'; url += 'menuaction=mail.mail_ui.download_zip'; // todo compose for Draft folder url += '&mode=save'; url += '&id='+mailid; - document.location = url; + this.et2._inst.download(url); }, saveAttachmentToVFS: function(tag_info, widget) @@ -2573,8 +2565,6 @@ app.classes.mail = AppJS.extend( url += '&method=mail.mail_ui.vfsSaveMessage'; url += '&id='+_elems[0].id; url += '&label=Save'; - //window.open(url,'_blank','dependent=yes,width=100,height=100,scrollbars=yes,status=yes') - //document.location = url; egw_openWindowCentered(url,'vfs_save_message_'+_elems[0].id,'640','570',window.outerWidth/2,window.outerHeight/2); },