From d44595a616f388c7d1fa4e09ba80e33f712d212e Mon Sep 17 00:00:00 2001 From: "kenneth@hexad.dk" Date: Wed, 6 Jun 2012 13:01:53 +0000 Subject: [PATCH] Added a "connection lost" dialog, and made the countdown stuff modularized and event based. Fixed some IE7 compatibility, and included a header so the embedded WebBrowser shows things in IE9 mode (IE7 is default for some reason). git-svn-id: https://duplicati.googlecode.com/svn/trunk@1333 59da171f-624f-0410-aa54-27559c288bec --- Duplicati/Server/webroot/app.js | 70 +++++++++----- .../webroot/backup-app/CountDownHelper.js | 61 ++++++++++++ .../Server/webroot/backup-app/Service.js | 94 ++++++++++++++----- .../controller/StatuswindowHeader.js | 2 +- .../webroot/backup-app/store/Schedules.js | 2 +- .../backup-app/view/LostConnectionWindow.js | 67 +++++++++++++ Duplicati/Server/webroot/status-window.html | 3 +- 7 files changed, 246 insertions(+), 53 deletions(-) create mode 100644 Duplicati/Server/webroot/backup-app/CountDownHelper.js create mode 100644 Duplicati/Server/webroot/backup-app/view/LostConnectionWindow.js diff --git a/Duplicati/Server/webroot/app.js b/Duplicati/Server/webroot/app.js index 2ada150f0..c947420d9 100644 --- a/Duplicati/Server/webroot/app.js +++ b/Duplicati/Server/webroot/app.js @@ -5,7 +5,8 @@ Ext.application({ 'BackupApp.Service', 'BackupApp.Utility', 'BackupApp.ListUtility', - 'BackupApp.view.AboutWindow' + 'BackupApp.view.AboutWindow', + 'BackupApp.view.LostConnectionWindow' ], name: 'BackupApp', @@ -37,32 +38,51 @@ Ext.application({ BackupApp.utility = this.utility; //Pretend the app can fire the update event, this helps with initialization in controllers - this.service.on('current-state-updated', function(e) { - var updatedTitle = false; - if (e.ActiveScheduleId >= 0) { - var store = Ext.getStore('Schedules'); - if (store != null) { - var index = store.find('ID', e.ActiveScheduleId); - if (index >= 0) { - updatedTitle = true; - document.title = 'Duplicati backup - running ' + store.getAt(index).get('Name'); + this.service.on({ + 'current-state-updated': function(e) { + var updatedTitle = false; + if (e.ActiveScheduleId >= 0) { + var store = Ext.getStore('Schedules'); + if (store != null) { + var index = store.find('ID', e.ActiveScheduleId); + if (index >= 0) { + updatedTitle = true; + document.title = 'Duplicati backup - running ' + store.getAt(index).get('Name'); + } } - } - } - - if (!updatedTitle) - { - if (e.SuggestedStatusIcon == 'Paused') - document.title = 'Duplicati backup - Paused'; - else - document.title = 'Duplicati backup - Ready'; - } - - BackupApp.instance.fireEvent('current-state-updated', e); - }); + } + + if (!updatedTitle) + { + if (e.SuggestedStatusIcon == 'Paused') + document.title = 'Duplicati backup - Paused'; + else + document.title = 'Duplicati backup - Ready'; + } + + this.fireEvent('current-state-updated', e); + }, + + 'lost-connection': function() { + BackupApp.view.LostConnectionWindow.show(); + }, - this.service.on('count-down-pause-timer', function(e) { - BackupApp.instance.fireEvent('count-down-pause-timer', e); + 'reconnected': function() { + BackupApp.view.LostConnectionWindow.hide(); + }, + + 'lost-connection-retry': function() { + BackupApp.view.LostConnectionWindow.setStatusAsRunning(); + }, + + 'lost-connection-retry-delay': function(seconds) { + BackupApp.view.LostConnectionWindow.setStatusAsWaiting(seconds); + }, + + 'count-down-pause-timer': function(e) { + BackupApp.instance.fireEvent('count-down-pause-timer', e); + }, + scope: this }); //Fetch the current status diff --git a/Duplicati/Server/webroot/backup-app/CountDownHelper.js b/Duplicati/Server/webroot/backup-app/CountDownHelper.js new file mode 100644 index 000000000..225159b50 --- /dev/null +++ b/Duplicati/Server/webroot/backup-app/CountDownHelper.js @@ -0,0 +1,61 @@ +Ext.define('BackupApp.CountDownHelper', { + mixins: { + observable: 'Ext.util.Observable' + }, + + statics: { + callbacklookup: [], + callbackhelper: function(id) { + this.callbacklookup[id].onCallback(); + } + }, + + expireTime: null, + intervalId : null, + + //Bugfix for extjs + hasListeners: {}, + + constructor: function(config) { + this.addEvents('count-down-tick', 'count-down-complete'); + }, + + isRunning: function() { + return this.intervalId != null; + }, + + start: function(time) { + if (this.intervalId != null) + this.stop(false); + + this.expireTime = time; + BackupApp.CountDownHelper.callbacklookup.push(this); + var selfid = BackupApp.CountDownHelper.callbacklookup.length - 1; + this.intervalId = window.setInterval('BackupApp.CountDownHelper.callbackhelper(' + selfid + ');', 500); + }, + + onCallback: function() { + var secondsLeft = parseInt((this.expireTime.getTime() - new Date().getTime()) / 1000); + this.fireEvent('count-down-tick', Math.max(0, secondsLeft)); + if (secondsLeft <= 0) + this.stop(true); + }, + + stop: function(activateEvent) { + if (this.intervalId != null) { + window.clearInterval(this.intervalId); + this.intervalId = null; + } + + for(var i = 0; i < BackupApp.CountDownHelper.callbacklookup.length; i++) { + if (BackupApp.CountDownHelper.callbacklookup[i] == this) { + BackupApp.CountDownHelper.callbacklookup.splice(i, 1); + break; + } + } + + if (activateEvent || activateEvent === undefined) + this.fireEvent('count-down-complete'); + } + +}) diff --git a/Duplicati/Server/webroot/backup-app/Service.js b/Duplicati/Server/webroot/backup-app/Service.js index 20654d266..a43d31e15 100644 --- a/Duplicati/Server/webroot/backup-app/Service.js +++ b/Duplicati/Server/webroot/backup-app/Service.js @@ -1,17 +1,22 @@ Ext.define('BackupApp.Service', { + requires: [ + 'BackupApp.CountDownHelper' + ], mixins: { observable: 'Ext.util.Observable' }, controlUrl: '/control.cgi', - pauseCountDownTimer: null, + pauseCountDownHelper: null, + lostConnectionCountDownHelper: null, updateRequestParams: { action: 'get-current-state', longpoll: true, lasteventid: '-1', - duration: '5m', + duration: '5m' }, updateRequestRunning: false, + updateRequestFailureCount: 0, //Bugfix for extjs hasListeners: {}, @@ -26,7 +31,36 @@ Ext.define('BackupApp.Service', { }, constructor: function(config) { - this.addEvents('current-state-updated', 'count-down-pause-timer'); + this.addEvents('current-state-updated', 'count-down-pause-timer', 'lost-connection', 'reconnected', 'lost-connection-retry', 'lost-connection-retry-delay'); + this.lostConnectionCountDownHelper = new BackupApp.CountDownHelper(); + this.pauseCountDownHelper = new BackupApp.CountDownHelper(); + + this.pauseCountDownHelper.on({ + 'count-down-tick': function(secondsleft) { + this.fireEvent('count-down-pause-timer', secondsleft); + }, + 'count-down-complete': function() { + this.fireEvent('count-down-pause-timer', 0); + }, + scope: this + }); + + this.lostConnectionCountDownHelper.on({ + 'count-down-tick': function(seconds) { + this.fireEvent('lost-connection-retry-delay', seconds); + }, + 'count-down-complete': function() { + this.updateCurrentState(); + this.fireEvent('lost-connection-retry'); + }, + scope: this + }) + }, + + reconnectNow: function() { + if (this.lostConnectionCountDownHelper.isRunning()) { + this.lostConnectionCountDownHelper.stop(true); + } }, updateCurrentState: function(force) { @@ -37,7 +71,17 @@ Ext.define('BackupApp.Service', { var timeout = force ? 5000 : (1000 * ((60 * 5) + 5)); if (!force) + { this.updateRequestRunning = true; + if (this.updateRequestFailureCount > 0) + { + //Last request failed, so lets retry, + // with a short timeout and make sure we + // don't wait for an update + timeout = 10000; //10 sec + this.updateRequestParams.lasteventid = -1; + } + } Ext.Ajax.request({ url: this.controlUrl, @@ -48,6 +92,10 @@ Ext.define('BackupApp.Service', { var res = eval('(' + response.responseText + ')'); if (res != null) { + if (this.updateRequestFailureCount >= 2) + this.fireEvent('reconnected'); + + this.updateRequestFailureCount = 0; Ext.log('Got update: ' + res.LastEventID + ', last: ' + this.updateRequestParams.lasteventid); var realUpdate = this.updateRequestParams.lasteventid != res.LastEventID; this.serverdata['current-state'] = res; @@ -63,40 +111,36 @@ Ext.define('BackupApp.Service', { if (realUpdate) this.fireEvent('current-state-updated', this.serverdata['current-state']); if (this.getExpectedPauseEnd() != null) - this.startPauseCountDownTimer(); + this.pauseCountDownHelper.start(this.getExpectedPauseEnd()); + else if (this.pauseCountDownHelper.isRunning()) + this.pauseCountDownHelper.stop(true); } }, failure: function(response, opts) { - //TODO: Grey out or similar - - //Register next request if (!force) { Ext.log('Failure in longpoll: ' + response.status + ', timeout: ' + response.timedout); + + this.updateRequestFailureCount++; this.updateRequestRunning = false; - this.updateCurrentState(); + + if (this.updateRequestFailureCount == 2) + this.fireEvent('lost-connection'); + + if (this.updateRequestFailureCount >= 2) { + + this.lostConnectionCountDownHelper.start(Ext.Date.add(new Date(), Ext.Date.SECOND, 30)); + } + else + { + this.updateCurrentState(); + } } } }); }, - - startPauseCountDownTimer: function() { - if (this.pauseCountDownTimer == null) - this.pauseCountDownTimer = window.setTimeout('BackupApp.service.onPauseCountDownTimer()', 500); - }, - - onPauseCountDownTimer: function() { - this.pauseCountDownTimer = null; - var pauseEnd = this.getExpectedPauseEnd(); - if (pauseEnd == null) { - this.fireEvent('count-down-pause-timer', 0); - } else { - this.fireEvent('count-down-pause-timer', parseInt((pauseEnd.getTime() - new Date().getTime()) / 1000)); - this.startPauseCountDownTimer(); - } - }, - + getExpectedPauseEnd: function() { var pauseEnd = this.serverdata['current-state'].EstimatedPauseEnd; if (pauseEnd != null) { diff --git a/Duplicati/Server/webroot/backup-app/controller/StatuswindowHeader.js b/Duplicati/Server/webroot/backup-app/controller/StatuswindowHeader.js index 048d10d1a..38c8e404f 100644 --- a/Duplicati/Server/webroot/backup-app/controller/StatuswindowHeader.js +++ b/Duplicati/Server/webroot/backup-app/controller/StatuswindowHeader.js @@ -7,7 +7,7 @@ Ext.define('BackupApp.controller.StatuswindowHeader', { ref: 'pauseButton' }, { selector: '#status-window-header-pause-menu', - ref: 'pauseMenu', + ref: 'pauseMenu' }], init: function() { diff --git a/Duplicati/Server/webroot/backup-app/store/Schedules.js b/Duplicati/Server/webroot/backup-app/store/Schedules.js index 307f1a3cb..48fcc58bf 100644 --- a/Duplicati/Server/webroot/backup-app/store/Schedules.js +++ b/Duplicati/Server/webroot/backup-app/store/Schedules.js @@ -82,7 +82,7 @@ Ext.define('BackupApp.store.Schedules', { return 0; } }, { - property: 'Name', + property: 'Name' }], proxy: { diff --git a/Duplicati/Server/webroot/backup-app/view/LostConnectionWindow.js b/Duplicati/Server/webroot/backup-app/view/LostConnectionWindow.js new file mode 100644 index 000000000..50c934f49 --- /dev/null +++ b/Duplicati/Server/webroot/backup-app/view/LostConnectionWindow.js @@ -0,0 +1,67 @@ +Ext.define('BackupApp.view.LostConnectionWindow', { + statics: { + dialogWindow: null, + setStatusAsRunning: function() { + if (this.dialogWindow != null) { + var txt = this.dialogWindow.queryById('status-window-lost-connection-text'); + var btn = this.dialogWindow.queryById('status-window-lost-connection-retry-button'); + btn.setDisabled(true); + txt.setText('Connection to server is lost, retrying ...'); + } + }, + + setStatusAsWaiting: function(seconds) { + if (BackupApp.service.updateRequestRunning) + { + this.setStatusAsRunning(); + } + else + { + if (this.dialogWindow != null) { + var txt = this.dialogWindow.queryById('status-window-lost-connection-text'); + var btn = this.dialogWindow.queryById('status-window-lost-connection-retry-button'); + txt.setText('Connection to server is lost, retry in ' + BackupApp.Utility.formatSecondsAsTime(seconds)); + btn.setDisabled(false); + } + } + }, + + hide: function() { + if (this.dialogWindow != null) + this.dialogWindow.hide(); + + }, + + show: function() { + if (this.dialogWindow == null) + this.dialogWindow = Ext.create('Ext.window.Window', { + title: 'Lost connection to server', + height: 200, + width: 400, + modal: true, + closable: false, + resizable: false, + buttonAlign: 'center', + closeAction: 'hide', + layout: 'fit', + items: [{ + xtype: 'label', + id: 'status-window-lost-connection-text', + text: 'Connection to server is lost, retrying ...' + }], + buttons: [{ + xtype: 'button', + id: 'status-window-lost-connection-retry-button', + text: 'Retry now', + disabled: true, + handler: function() { + BackupApp.service.reconnectNow(); + } + }] + }); + + this.dialogWindow.show(); + } + } +}); + diff --git a/Duplicati/Server/webroot/status-window.html b/Duplicati/Server/webroot/status-window.html index c95ec0d89..2194fe202 100644 --- a/Duplicati/Server/webroot/status-window.html +++ b/Duplicati/Server/webroot/status-window.html @@ -1,7 +1,8 @@ - + + Duplicati Backup