Removed the old ExtJS based UI
@@ -1,61 +0,0 @@
|
||||
Ext.define('Duplicati.CountDownHelper', {
|
||||
mixins: {
|
||||
observable: 'Ext.util.Observable'
|
||||
},
|
||||
|
||||
statics: {
|
||||
callbackkeycounter: 0,
|
||||
callbacklookup: {},
|
||||
callbackhelper: function(id) {
|
||||
this.callbacklookup["" + id].onCallback();
|
||||
}
|
||||
},
|
||||
|
||||
expireTime: null,
|
||||
intervalId : null,
|
||||
|
||||
constructor: function(config) {
|
||||
this.mixins.observable.constructor.call(this, 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;
|
||||
var selfid = Duplicati.CountDownHelper.callbackkeycounter++;
|
||||
|
||||
Duplicati.CountDownHelper.callbacklookup["" + selfid] = this;
|
||||
this.intervalId = window.setInterval('Duplicati.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 k in Duplicati.CountDownHelper.callbacklookup) {
|
||||
if (Duplicati.CountDownHelper.callbacklookup[k] == this) {
|
||||
delete Duplicati.CountDownHelper.callbacklookup[k];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (activateEvent || activateEvent === undefined)
|
||||
this.fireEvent('count-down-complete');
|
||||
}
|
||||
|
||||
})
|
||||
@@ -1,118 +0,0 @@
|
||||
Ext.define('Duplicati.ListUtility', {
|
||||
statics: {
|
||||
|
||||
currentScheduleId: null,
|
||||
|
||||
showContextMenu: function(id, el) {
|
||||
var lst = Ext.getCmp('status-window-schedule-list');
|
||||
var mnu = lst.elementContextMenu;
|
||||
this.currentScheduleId = id;
|
||||
var elx = Ext.get(el);
|
||||
mnu.showBy(elx, 'tr-br?');
|
||||
},
|
||||
|
||||
formatSizeString: function(size) { return Duplicati.Utility.formatSizeString(size); },
|
||||
timeAgo: function(date) { return Duplicati.Utility.timeAgo(date); },
|
||||
formatCountString: function(count, singular, plural) {
|
||||
count = parseInt(count);
|
||||
if (count == 1)
|
||||
return 1 + ' ' + singluar;
|
||||
|
||||
//Make a string
|
||||
count = count + '';
|
||||
var tmp = '';
|
||||
while (count.length > 3) {
|
||||
tmp = count.substr(count.length - 3, 3) + (tmp.length == 0 ? '' : '.') + tmp;
|
||||
count = count.substr(0, count.length - 3);
|
||||
}
|
||||
|
||||
tmp = count + (tmp.length == 0 ? '' : '.') + tmp;
|
||||
|
||||
return tmp + ' ' + plural;
|
||||
},
|
||||
|
||||
formatBackupScheduleDetails: function(values)
|
||||
{
|
||||
var metadata = '';
|
||||
|
||||
if (values.Metadata != null)
|
||||
{
|
||||
var metadataItems = [];
|
||||
var mt = values.Metadata;
|
||||
|
||||
var sourceData = '';
|
||||
if (mt['source-file-size'] != null && mt['source-file-count'] != null)
|
||||
sourceData = this.formatSizeString(mt['source-file-size']) + ' (' + this.formatCountString(mt['source-file-count'], 'file', 'files') + ')';
|
||||
else if (mt['source-file-size'] != null)
|
||||
sourceData = this.formatSizeString(mt['source-file-size']);
|
||||
else if (mt['source-file-count'] != null)
|
||||
sourceData = this.formatCountString(mt['source-file-count'], 'file', 'files');
|
||||
|
||||
if (sourceData != '')
|
||||
metadataItems.push('<span class="backup-schedule-detail-source-size">Source: ' + sourceData + '</span>');
|
||||
|
||||
var backupData = '';
|
||||
if (mt['total-backup-size'] != null && parseInt(mt['total-backup-size']) > 0 && mt['total-file-count'] != null && parseInt(mt['total-file-count']) > 0)
|
||||
backupData = this.formatSizeString(mt['total-backup-size']) + ' (' + this.formatCountString(mt['total-file-count'], 'file', 'files') + ')';
|
||||
else if (mt['total-backup-size'] != null && parseInt(mt['total-backup-size']) > 0)
|
||||
backupData = this.formatSizeString(mt['total-backup-size']);
|
||||
else if (mt['total-file-count'] != null && parseInt(mt['total-file-count']) > 0)
|
||||
backupData = this.formatCountString(mt['total-file-count'], 'file', 'files');
|
||||
|
||||
if (backupData != '')
|
||||
metadataItems.push('<span class="backup-schedule-detail-backup-size">Backup: ' + backupData + '</span>');
|
||||
|
||||
if (mt['assigned-quota-space'] != null || mt['free-quota-space'] != null)
|
||||
{
|
||||
var spaceLeft = null;
|
||||
if (mt['assigned-quota-space'] != null && mt['free-quota-space'] != null && mt['total-backup-size'] != null)
|
||||
spaceLeft = Math.min(parseInt(mt['assigned-quota-space']) - parseInt(mt['total-backup-size']), parseInt(mt['free-quota-space']));
|
||||
else if (mt['assigned-quota-space'] != null && mt['total-backup-size'] != null)
|
||||
spaceLeft = parseInt(mt['assigned-quota-space']) - parseInt(mt['total-backup-size']);
|
||||
else if (mt['free-quota-space'] != null)
|
||||
spaceLeft = parseInt(mt['free-quota-space']);
|
||||
|
||||
var extracls = '';
|
||||
if (mt['total-backup-size'] != null)
|
||||
{
|
||||
//We mark it as low if there is less than 10% free
|
||||
if (spaceLeft < (parseInt(mt['total-backup-size']) * 0.1))
|
||||
extracls = 'class="low-storage-space"';
|
||||
}
|
||||
|
||||
if (spaceLeft != null)
|
||||
metadataItems.push('<span class="backup-schedule-detail-free-size">Free space: <span ' + extracls + '>' + this.formatSizeString(spaceLeft) + '</span></span>');
|
||||
}
|
||||
|
||||
if (mt['changed-file-count'] != null)
|
||||
{
|
||||
var changedFiles = parseInt(mt['changed-file-count']);
|
||||
var str = changedFiles + ' ' + changedFiles == 1 ? "change" : "changes";
|
||||
|
||||
if (mt['last-backup-date'] != null)
|
||||
str += ' since ' + this.timeAgo(new Date(mt['last-backup-date']));
|
||||
|
||||
metadataItems.push('<span class="backup-schedule-detail-change-count">' + str + '</span>');
|
||||
} else if (mt['last-backup-date'] != null || mt['last-backup-completed-time'] != null) {
|
||||
if (mt['last-backup-completed-time'] != null)
|
||||
metadataItems.push('<span class="backup-schedule-detail-last-backup">Last backup: <span class="marker-time-ago" title="' + (new Date(mt['last-backup-completed-time'])).toUTCString() + '"></span>' + this.timeAgo(new Date(mt['last-backup-completed-time'])) + '</span>');
|
||||
else
|
||||
metadataItems.push('<span class="backup-schedule-detail-last-backup">Last backup: <span class="marker-time-ago" title="' + (new Date(mt['last-backup-date'])).toUTCString() + '"></span>' + this.timeAgo(new Date(mt['last-backup-date'])) + '</span>');
|
||||
}
|
||||
|
||||
for(var ix = 0; ix < metadataItems.length; ix++)
|
||||
{
|
||||
/*if (ix != 0)
|
||||
metadata += ", "; */
|
||||
|
||||
metadata += metadataItems[ix];
|
||||
}
|
||||
}
|
||||
|
||||
if (metadata == '')
|
||||
metadata = '<span class="important-information">No backup information was found, <a title="Click here to run the backup now" href="#" onclick="Duplicati.service.runBackup(' + values.ID + ', false)">run a backup</a> to obtain data</span>'
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,319 +0,0 @@
|
||||
Ext.define('Duplicati.Service', {
|
||||
requires: [
|
||||
'Duplicati.CountDownHelper'
|
||||
],
|
||||
mixins: {
|
||||
observable: 'Ext.util.Observable'
|
||||
},
|
||||
|
||||
controlUrl: '/control.cgi',
|
||||
pauseCountDownHelper: null,
|
||||
lostConnectionCountDownHelper: null,
|
||||
updateRequestParams: {
|
||||
action: 'get-current-state',
|
||||
longpoll: true,
|
||||
lasteventid: '-1',
|
||||
duration: '5m'
|
||||
},
|
||||
updateRequestRunning: false,
|
||||
updateRequestFailureCount: 0,
|
||||
|
||||
//Cache for server responses
|
||||
serverdata: {
|
||||
'current-state': null,
|
||||
'installed-backends': null,
|
||||
'installed-compression-modules': null,
|
||||
'installed-encryption-modules': null,
|
||||
'installed-generic-modules': null,
|
||||
'backup-defaults': null,
|
||||
'system-info': null
|
||||
},
|
||||
|
||||
constructor: function(config) {
|
||||
this.mixins.observable.constructor.call(this, config);
|
||||
this.addEvents('current-state-updated', 'count-down-pause-timer', 'lost-connection', 'reconnected', 'lost-connection-retry', 'lost-connection-retry-delay');
|
||||
this.lostConnectionCountDownHelper = new Duplicati.CountDownHelper();
|
||||
this.pauseCountDownHelper = new Duplicati.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
|
||||
})
|
||||
|
||||
this.loadSystemInfo(false);
|
||||
},
|
||||
|
||||
loadSystemInfo: function(reload) {
|
||||
|
||||
if (reload || this.serverdata['system-info'] == null) {
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: {action: 'system-info'},
|
||||
scope: this,
|
||||
success: function(response) {
|
||||
var res = eval('(' + response.responseText + ')');
|
||||
if (res != null)
|
||||
this.serverdata['system-info'] = res;
|
||||
|
||||
if (this.serverdata['system-info'] == null)
|
||||
window.setTimeout(this.loadSystemInfo, 1000);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
if (this.serverdata['system-info'] == null)
|
||||
window.setTimeout(this.loadSystemInfo, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
reconnectNow: function() {
|
||||
if (this.lostConnectionCountDownHelper.isRunning()) {
|
||||
this.lostConnectionCountDownHelper.stop(true);
|
||||
}
|
||||
},
|
||||
|
||||
updateCurrentState: function(force) {
|
||||
if (this.updateRequestRunning && !force)
|
||||
return;
|
||||
|
||||
var params = force ? { action: 'get-current-state' } : this.updateRequestParams;
|
||||
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,
|
||||
params: params,
|
||||
timeout: timeout,
|
||||
scope: this,
|
||||
success: function(response) {
|
||||
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;
|
||||
this.updateRequestParams.lasteventid = res.LastEventID;
|
||||
|
||||
//Register next request
|
||||
if (!force)
|
||||
{
|
||||
this.updateRequestRunning = false;
|
||||
this.updateCurrentState();
|
||||
}
|
||||
|
||||
if (realUpdate)
|
||||
this.fireEvent('current-state-updated', this.serverdata['current-state']);
|
||||
if (this.getExpectedPauseEnd() != null)
|
||||
this.pauseCountDownHelper.start(this.getExpectedPauseEnd());
|
||||
else if (this.pauseCountDownHelper.isRunning())
|
||||
this.pauseCountDownHelper.stop(true);
|
||||
|
||||
}
|
||||
},
|
||||
failure: function(response, opts) {
|
||||
//Register next request
|
||||
if (!force)
|
||||
{
|
||||
Ext.log('Failure in longpoll: ' + response.status + ', timeout: ' + response.timedout);
|
||||
|
||||
this.updateRequestFailureCount++;
|
||||
this.updateRequestRunning = false;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getExpectedPauseEnd: function() {
|
||||
var pauseEnd = this.serverdata['current-state'].EstimatedPauseEnd;
|
||||
if (pauseEnd != null) {
|
||||
pauseEnd = Duplicati.Utility.parseJsonDate(pauseEnd);
|
||||
if (pauseEnd != null && pauseEnd.getTime() > new Date().getTime())
|
||||
return pauseEnd;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
|
||||
getSchedulePosition: function(id) {
|
||||
var state = this.serverdata['current-state'];
|
||||
if (state == null)
|
||||
return -1;
|
||||
|
||||
if (id == state.ActiveScheduleId)
|
||||
return 0;
|
||||
else if (state.SchedulerQueueIds != null)
|
||||
for(var i = 0; i < state.SchedulerQueueIds.length; i++)
|
||||
if (state.SchedulerQueueIds[i] == id)
|
||||
return i+1;
|
||||
|
||||
return -1;
|
||||
},
|
||||
|
||||
pause: function(duration) {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: {
|
||||
action: 'send-command',
|
||||
command: 'pause',
|
||||
duration: duration
|
||||
},
|
||||
success: function(response) { }
|
||||
});
|
||||
},
|
||||
|
||||
resume: function() {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: {
|
||||
action: 'send-command',
|
||||
command: 'resume'
|
||||
},
|
||||
success: function(response) { }
|
||||
});
|
||||
},
|
||||
|
||||
isPaused: function() {
|
||||
var state = this.serverdata['current-state'];
|
||||
if (state != null)
|
||||
return state.ProgramState != 'Running';
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
togglePause: function() {
|
||||
if (this.isPaused())
|
||||
this.resume();
|
||||
else
|
||||
this.pause();
|
||||
},
|
||||
|
||||
getInstalledCompressionModules: function(callback) {
|
||||
if (this.serverdata['installed-compression-modules'] == null) {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: { action: 'list-installed-compression-modules' },
|
||||
success: function(response) {
|
||||
self.serverdata['installed-compression-modules'] = eval('(' + response.responseText + ')');
|
||||
callback(self.serverdata['installed-compression-modules']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(this.serverdata['installed-compression-modules']);
|
||||
}
|
||||
},
|
||||
|
||||
getInstalledGenericModules: function(callback) {
|
||||
if (this.serverdata['installed-generic-modules'] == null) {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: { action: 'list-installed-generic-modules' },
|
||||
success: function(response) {
|
||||
self.serverdata['installed-generic-modules'] = eval('(' + response.responseText + ')');
|
||||
callback(self.serverdata['installed-generic-modules']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(this.serverdata['installed-generic-modules']);
|
||||
}
|
||||
},
|
||||
|
||||
getInstalledEncryptionModules: function(callback) {
|
||||
if (this.serverdata['installed-encryption-modules'] == null) {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: { action: 'list-installed-encryption-modules' },
|
||||
success: function(response) {
|
||||
self.serverdata['installed-encryption-modules'] = eval('(' + response.responseText + ')');
|
||||
callback(self.serverdata['installed-encryption-modules']);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
callback(this.serverdata['installed-encryption-modules']);
|
||||
}
|
||||
},
|
||||
|
||||
getBackupDefaults: function(callback) {
|
||||
if (this.serverdata['backup-defaults'] == null) {
|
||||
var self = this;
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: { action: 'get-backup-defaults' },
|
||||
success: function(response) {
|
||||
self.serverdata['backup-defaults'] = eval('(' + response.responseText + ')');
|
||||
callback(self.serverdata['backup-defaults']);
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
} else {
|
||||
callback(this.serverdata['backup-defaults']);
|
||||
}
|
||||
},
|
||||
|
||||
runBackup: function(id, full) {
|
||||
var self = this;
|
||||
|
||||
Ext.Ajax.request({
|
||||
url: this.controlUrl,
|
||||
params: {
|
||||
action: 'send-command',
|
||||
command: 'run-backup',
|
||||
id: id,
|
||||
full: full
|
||||
},
|
||||
success: function(response) {
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,168 +0,0 @@
|
||||
Ext.define('Duplicati.Utility', {
|
||||
statics: {
|
||||
|
||||
strings: {
|
||||
terabyteShort: 'TB',
|
||||
gigabyteShort: 'GB',
|
||||
megabyteShort: 'MB',
|
||||
kilobyteShort: 'KB',
|
||||
bytesShort: 'bytes',
|
||||
|
||||
prefixAgo: null,
|
||||
prefixFromNow: "in",
|
||||
suffixAgo: "ago",
|
||||
suffixFromNow: null,
|
||||
seconds: "less than a minute",
|
||||
minute: "about a minute",
|
||||
minutes: "%d minutes",
|
||||
hour: "about an hour",
|
||||
hours: "about %d hours",
|
||||
day: "a day",
|
||||
days: "%d days",
|
||||
month: "about a month",
|
||||
months: "%d months",
|
||||
year: "about a year",
|
||||
years: "%d years",
|
||||
wordSeparator: " "
|
||||
},
|
||||
|
||||
timeAgoTimer: window.setInterval('Duplicati.Utility.updateTimeAgoFields()', 1000 * 150),
|
||||
|
||||
//Simplified version of jquery.timeago
|
||||
timeAgo: function(date) {
|
||||
var distanceMillis = (new Date().getTime() - date.getTime());
|
||||
var strings = this.strings;
|
||||
|
||||
var prefix = strings.prefixAgo;
|
||||
var suffix = strings.suffixAgo;
|
||||
if (distanceMillis < 0) {
|
||||
prefix = strings.prefixFromNow;
|
||||
suffix = strings.suffixFromNow;
|
||||
}
|
||||
|
||||
var seconds = Math.abs(distanceMillis) / 1000;
|
||||
var minutes = seconds / 60;
|
||||
var hours = minutes / 60;
|
||||
var days = hours / 24;
|
||||
var years = days / 365;
|
||||
|
||||
var words = seconds < 45 && strings.seconds.replace(/%d/i, Math.round(seconds)) ||
|
||||
seconds < 90 && strings.minute ||
|
||||
minutes < 45 && strings.minutes.replace(/%d/i, Math.round(minutes)) ||
|
||||
minutes < 90 && strings.hour ||
|
||||
hours < 24 && strings.hours.replace(/%d/i, Math.round(hours)) ||
|
||||
hours < 42 && strings.day ||
|
||||
days < 30 && strings.days.replace(/%d/i, Math.round(days)) ||
|
||||
days < 45 && strings.month ||
|
||||
days < 365 && strings.months.replace(/%d/i, Math.round(days / 30)) ||
|
||||
years < 1.5 && strings.year ||
|
||||
strings.years.replace(/%d/i, Math.round(years));
|
||||
|
||||
var separator = strings.wordSeparator === undefined ? " " : strings.wordSeparator;
|
||||
return Ext.String.trim([prefix, words, suffix].join(separator));
|
||||
},
|
||||
|
||||
updateTimeAgoFields: function() {
|
||||
var parent = Ext.get(document);
|
||||
var els = parent.query('.marker-time-ago');
|
||||
for(var i = 0; i < els.length; i++) {
|
||||
var time = this.parseJsonDate(els[i].title);
|
||||
if (els[i].nextSibling.nodeType == 3)
|
||||
els[i].nextSibling.nodeValue = this.timeAgo(time);
|
||||
else if (els[i].nextSibling.innerText != null)
|
||||
els[i].nextSibling.innerText = this.timeAgo(time);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
parseJsonDate: function(str) {
|
||||
if (str == null)
|
||||
return null;
|
||||
|
||||
if (Ext.String.trim(str).length == 0)
|
||||
return null;
|
||||
|
||||
if (str.indexOf("\/") == 0)
|
||||
{
|
||||
var strtmp = str.replace(/\/Date\((-?\d+(((\+|\-)\d+)|Z|z)?)\)\//gi, "new Date($1)");
|
||||
return eval(strtmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new Date(str);
|
||||
}
|
||||
},
|
||||
|
||||
formatSecondsAsTime: function(seconds) {
|
||||
seconds = parseInt(seconds);
|
||||
var hours = parseInt(Math.floor(seconds / (60 * 60)));
|
||||
seconds = seconds - (hours * 60 * 60);
|
||||
var minutes = parseInt(Math.floor(seconds / 60));
|
||||
seconds = seconds - (minutes * 60);
|
||||
var res = '';
|
||||
if (hours > 0) {
|
||||
res += hours + ':';
|
||||
if (minutes < 10)
|
||||
res += '0';
|
||||
}
|
||||
|
||||
res += minutes + ':' ;
|
||||
if (seconds < 10)
|
||||
res += '0';
|
||||
res += seconds;
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
formatSizeString: function(size) {
|
||||
size = parseInt(size);
|
||||
if (isNaN(size))
|
||||
return size;
|
||||
|
||||
var strings = this.strings;
|
||||
|
||||
if (size >= 1024 * 1024 * 1024 * 1024)
|
||||
return (size / (1024 * 1024 * 1024 * 1024.0)).toFixed(2) + ' ' + strings.terabyteShort;
|
||||
else if (size >= 1024 * 1024 * 1024)
|
||||
return (size / (1024 * 1024 * 1024.0)).toFixed(2) + ' ' + strings.gigabyteShort;
|
||||
else if (size >= 1024 * 1024)
|
||||
return (size / (1024 * 1024.0)).toFixed(2) + ' ' + strings.megabyteShort;
|
||||
else if (size >= 1024)
|
||||
return (size / (1024.0)).toFixed(2) + ' ' + strings.kilobyteShort;
|
||||
else
|
||||
return size + ' ' + strings.bytesShort;
|
||||
},
|
||||
|
||||
estimatedFreeSpace: function(assignedQuota, freeQuota, backupSize) {
|
||||
if (assignedQuota != null && freeQuota != null && backupSize != null)
|
||||
return Math.min(parseInt(assignedQuota) - parseInt(backupSize), parseInt(freeQuota));
|
||||
else if (assignedQuota != null && backupSize != null)
|
||||
return parseInt(assignedQuota) - parseInt(backupSize);
|
||||
else if (freeQuota != null)
|
||||
return parseInt(freeQuota);
|
||||
else
|
||||
return null;
|
||||
},
|
||||
|
||||
createRecordFromData: function(classname, data) {
|
||||
var entry = Ext.create(classname);
|
||||
var model = Ext.ModelManager.getModel(classname);
|
||||
var fields = model.getFields();
|
||||
var associations = entry.associations;
|
||||
|
||||
for(var i = 0; i < fields.length; i++) {
|
||||
var key = fields[i].mapping || fields[i].name;
|
||||
if (data[key] !== undefined)
|
||||
entry.set(fields[i].name, data[key]);
|
||||
}
|
||||
|
||||
for(var i = 0; i < associations.length; i++) {
|
||||
var a = associations.getAt(i);
|
||||
if (data[a.associationKey])
|
||||
entry[a.setterName](Duplicati.Utility.createRecordFromData(a.associatedName, data[a.associationKey]));
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,168 +0,0 @@
|
||||
Ext.define('Duplicati.controller.BackupConfig', {
|
||||
extend: 'Ext.app.Controller',
|
||||
|
||||
onBtnGeneratePasswordClick: function(button, e, options) {
|
||||
var text = "";
|
||||
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-.,_:;+#?=)(/&%$§";
|
||||
for( var i=0; i < 16; i++ ) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
var t = Ext.getCmp('sEncryptionPassword');
|
||||
var t2 = Ext.getCmp('sEncryptionPasswordCheck');
|
||||
var pwdButton = Ext.getCmp('btnPassword');
|
||||
t.inputEl.dom.type = t2.inputEl.dom.type = 'text';
|
||||
pwdButton.setText( 'Hide Password' );
|
||||
t.setValue(text);
|
||||
},
|
||||
|
||||
onButtonClick: function(button, e, options) {
|
||||
var t = Ext.getCmp('sEncryptionPassword');
|
||||
var t2 = Ext.getCmp('sEncryptionPasswordCheck');
|
||||
var pwdButton = Ext.getCmp('btnPassword');
|
||||
t.inputEl.dom.type = t2.inputEl.dom.type = ( t.inputEl.dom.type == 'password' )?'text':'password';
|
||||
pwdButton.setText( ( t.inputEl.dom.type == 'password' )?'Show Password':'Hide Password' );
|
||||
},
|
||||
|
||||
onTAddLocationClick: function(tool, e, options) {
|
||||
Ext.create('Duplicati.view.wndAddLocation').show();
|
||||
},
|
||||
|
||||
onTAddFilterClick: function(tool, e, options) {
|
||||
Ext.create('Duplicati.view.wndAddFilter').show();
|
||||
},
|
||||
|
||||
onBtnBuildURLClick: function(button, e, options) {
|
||||
Ext.create('Duplicati.view.wndCreateConnection').show();
|
||||
},
|
||||
|
||||
onTAddOverrideClick: function(tool, e, options) {
|
||||
Ext.create('Duplicati.view.wndAddOverride').show();
|
||||
},
|
||||
|
||||
onBtnNextClick: function(button, e, options) {
|
||||
if( Ext.getCmp('btnNext').getText() == 'Finish' ) {
|
||||
// Save all the stuff and Finish work
|
||||
// Checks need to happen here, if everything is filled in proper
|
||||
alert( "DONE!!");
|
||||
button.findParentByType('window').destroy();
|
||||
} else {
|
||||
var wizardTabs = Ext.getCmp('panelWizard');
|
||||
wizardTabs.setActiveTab( wizardTabs.getActiveTab().activeItem + 1 );
|
||||
}
|
||||
},
|
||||
|
||||
onAddLocationsClick: function(button, e, options) {
|
||||
var records = Ext.getCmp('treeLocation').getChecked();
|
||||
Ext.getCmp('Locations').getStore().removeAll();
|
||||
|
||||
Ext.Array.each(records, function(rec){
|
||||
Ext.getCmp('Locations').getStore().add(
|
||||
new Duplicati.model.TaskItems.BackupLocation ( {
|
||||
Location: rec.get('id'),
|
||||
TotalSize:0
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
Ext.getCmp("winAddLocation").destroy();
|
||||
},
|
||||
|
||||
onBtnCloseClick: function(button, e, options) {
|
||||
button.findParentByType('window').destroy();
|
||||
},
|
||||
|
||||
afterWindowLayout: function(abstractcontainer, layout, options) {
|
||||
if (abstractcontainer.scheduleId == null || abstractcontainer.scheduleId < 0)
|
||||
{
|
||||
Duplicati.service.getBackupDefaults(function(data) {
|
||||
var entry = Duplicati.Utility.createRecordFromData('Duplicati.model.BackupJob', data.data);
|
||||
abstractcontainer.loadRecord(entry);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
var backupJobModel = Ext.ModelManager.getModel('Duplicati.model.BackupJob');
|
||||
backupJobModel.load(abstractcontainer.scheduleId, {
|
||||
scope: this,
|
||||
success : function(backupJobResult, operation) {
|
||||
abstractcontainer.loadRecord(backupJobResult);
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onControllerClickStub: function() {
|
||||
|
||||
},
|
||||
|
||||
onWizardTabChange: function(tabPanel, newCard, oldCard, eOpts) {
|
||||
if (newCard.activeItem == -1) {
|
||||
Ext.getCmp('btnNext').setText( 'Finish' );
|
||||
} else {
|
||||
Ext.getCmp('btnNext').setText( 'Next >' );
|
||||
}
|
||||
},
|
||||
|
||||
afterEncryptionModuleChange: function() {
|
||||
var combo = Ext.getCmp('EncryptionModule');
|
||||
var t = Ext.getCmp('sEncryptionPassword');
|
||||
var t2 = Ext.getCmp('sEncryptionPasswordCheck');
|
||||
var b = Ext.getCmp('btnPassword');
|
||||
var b2 = Ext.getCmp('btnGeneratePassword');
|
||||
|
||||
if (combo.getValue() == '') {
|
||||
t.disable();
|
||||
t2.disable();
|
||||
b.disable();
|
||||
b2.disable();
|
||||
} else {
|
||||
t.enable();
|
||||
t2.enable();
|
||||
b.enable();
|
||||
b2.enable();
|
||||
}
|
||||
},
|
||||
|
||||
init: function() {
|
||||
this.control({
|
||||
"#btnGeneratePassword": {
|
||||
click: this.onBtnGeneratePasswordClick
|
||||
},
|
||||
"#btnPassword": {
|
||||
click: this.onButtonClick
|
||||
},
|
||||
"#tAddLocation": {
|
||||
click: this.onTAddLocationClick
|
||||
},
|
||||
"#tAddFilter": {
|
||||
click: this.onTAddFilterClick
|
||||
},
|
||||
"#tAddOverride": {
|
||||
click: this.onTAddOverrideClick
|
||||
},
|
||||
"#btnBuildURL": {
|
||||
click: this.onBtnBuildURLClick
|
||||
},
|
||||
"#btnNext": {
|
||||
click: this.onBtnNextClick
|
||||
},
|
||||
"#btnAddLocations": {
|
||||
click: this.onAddLocationsClick
|
||||
},
|
||||
"#btnClose": {
|
||||
click: this.onBtnCloseClick
|
||||
},
|
||||
"backupconfigpanel": {
|
||||
afterlayout: this.afterWindowLayout
|
||||
},
|
||||
"#EncryptionModule": {
|
||||
change: this.afterEncryptionModuleChange
|
||||
},
|
||||
"#panelWizard": {
|
||||
tabchange: this.onWizardTabChange
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
Ext.define('Duplicati.controller.Schedules', {
|
||||
extend: 'Ext.app.Controller',
|
||||
stores: [ 'Schedules' ],
|
||||
views: [ 'schedule.List' ],
|
||||
|
||||
refs: [{
|
||||
selector: 'viewport > panel',
|
||||
ref: 'basicPanel'
|
||||
}],
|
||||
|
||||
lastEventID: -1,
|
||||
|
||||
init: function() {
|
||||
this.control({
|
||||
'viewport > panel': {
|
||||
render: this.onPanelRendered
|
||||
}
|
||||
});
|
||||
|
||||
this.application.on({
|
||||
'current-state-updated': function(data) {
|
||||
|
||||
//Update the data store if there is new metadata
|
||||
if (data.LastEventID != this.lastEventID)
|
||||
this.getSchedulesStore().load();
|
||||
|
||||
this.lastEventID = data.LastEventID;
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
},
|
||||
|
||||
onPanelRendered: function() {
|
||||
//console.log('The panel was rendered');
|
||||
}
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
Ext.define('Duplicati.controller.StatuswindowFooter', {
|
||||
extend: 'Ext.app.Controller',
|
||||
views: [ 'StatuswindowFooter' ],
|
||||
|
||||
init: function() {
|
||||
this.control({
|
||||
'viewport > panel': {
|
||||
render: this.onPanelRendered
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onPanelRendered: function() {
|
||||
//console.log('The footer panel was rendered');
|
||||
}
|
||||
});
|
||||
@@ -1,60 +0,0 @@
|
||||
Ext.define('Duplicati.controller.StatuswindowHeader', {
|
||||
extend: 'Ext.app.Controller',
|
||||
views: [ 'StatuswindowHeader' ],
|
||||
|
||||
refs: [{
|
||||
selector: '#status-window-header-pause-button',
|
||||
ref: 'pauseButton'
|
||||
}, {
|
||||
selector: '#status-window-header-pause-menu',
|
||||
ref: 'pauseMenu'
|
||||
}],
|
||||
|
||||
init: function() {
|
||||
this.application.on({
|
||||
'current-state-updated': function() {
|
||||
var btn = this.getPauseButton();
|
||||
var mnu = this.getPauseMenu();
|
||||
var srv = this.application.service;
|
||||
var util = Duplicati.Utility;
|
||||
|
||||
if (srv.isPaused()) {
|
||||
if (srv.getExpectedPauseEnd() != null)
|
||||
btn.setText('Resume (' + util.formatSecondsAsTime((srv.getExpectedPauseEnd().getTime() - new Date().getTime()) / 1000) + ')');
|
||||
else
|
||||
btn.setText('Resume');
|
||||
btn.setIconCls('backups-paused');
|
||||
mnu.setText('Resume Duplicati');
|
||||
} else {
|
||||
btn.setText('Pause');
|
||||
btn.setIconCls('backups-running');
|
||||
mnu.setText('Pause Duplicati');
|
||||
}
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
|
||||
this.application.on({
|
||||
'count-down-pause-timer': function(data) {
|
||||
var btn = this.getPauseButton();
|
||||
var srv = this.application.service;
|
||||
var util = Duplicati.Utility;
|
||||
if (data <= 0)
|
||||
btn.setText('Pause');
|
||||
else
|
||||
btn.setText('Resume (' + util.formatSecondsAsTime(data) + ')');
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
|
||||
this.control({
|
||||
'viewport > panel': {
|
||||
render: this.onPanelRendered
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onPanelRendered: function() {
|
||||
//console.log('The header panel was rendered');
|
||||
}
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
Ext.define('Duplicati.model.BackupJob', {
|
||||
extend: 'Ext.data.Model',
|
||||
uses: [
|
||||
'Duplicati.model.Task',
|
||||
'Duplicati.model.Schedule'
|
||||
],
|
||||
|
||||
proxy: {
|
||||
type: 'ajax',
|
||||
url: '/control.cgi?action=get-schedule-details',
|
||||
reader: {
|
||||
type: 'json',
|
||||
idProperty: 'ID',
|
||||
root: 'data'
|
||||
}
|
||||
},
|
||||
|
||||
associations: [{
|
||||
name: 'task',
|
||||
type: 'hasOne',
|
||||
associationKey: 'Task',
|
||||
primaryKey: 'ID',
|
||||
getterName: 'getTask',
|
||||
setterName: 'setTask',
|
||||
model: 'Duplicati.model.Task'
|
||||
},{
|
||||
name: 'schedule',
|
||||
type: 'hasOne',
|
||||
associationKey: 'Schedule',
|
||||
primaryKey: 'ID',
|
||||
getterName: 'getSchedule',
|
||||
setterName: 'setSchedule',
|
||||
model: 'Duplicati.model.Schedule'
|
||||
}]
|
||||
});
|
||||
@@ -1,41 +0,0 @@
|
||||
Ext.define('Duplicati.model.Schedule', {
|
||||
extend: 'Ext.data.Model',
|
||||
|
||||
associations: [{
|
||||
name: 'task',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.Task',
|
||||
primaryKey: 'ID',
|
||||
foreignKey: 'ScheduleID'
|
||||
}],
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'NextScheduledTime'
|
||||
},
|
||||
{
|
||||
name: 'AllowedWeekdays'
|
||||
},
|
||||
{
|
||||
name: 'ID'
|
||||
},
|
||||
{
|
||||
name: 'Name'
|
||||
},
|
||||
{
|
||||
name: 'Path'
|
||||
},
|
||||
{
|
||||
name: 'When'
|
||||
},
|
||||
{
|
||||
name: 'Repeat'
|
||||
},
|
||||
{
|
||||
name: 'Weekdays'
|
||||
},
|
||||
{
|
||||
name: 'Metadata'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,73 +0,0 @@
|
||||
Ext.define('Duplicati.model.ScheduleItems.Metadata', {
|
||||
extend: 'Ext.data.Model',
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'total_size',
|
||||
mapping: 'total-size'
|
||||
},
|
||||
{
|
||||
name: 'full_backup_count'
|
||||
},
|
||||
{
|
||||
name: 'total_volume_count'
|
||||
},
|
||||
{
|
||||
name: 'total_file_count'
|
||||
},
|
||||
{
|
||||
name: 'longest_chain_length'
|
||||
},
|
||||
{
|
||||
name: 'current_chain_length'
|
||||
},
|
||||
{
|
||||
name: 'current_full_date'
|
||||
},
|
||||
{
|
||||
name: 'current_chain_size'
|
||||
},
|
||||
{
|
||||
name: 'last_backup_date'
|
||||
},
|
||||
{
|
||||
name: 'last_backup_size'
|
||||
},
|
||||
{
|
||||
name: 'total_backup_size'
|
||||
},
|
||||
{
|
||||
name: 'total_quota_space'
|
||||
},
|
||||
{
|
||||
name: 'free_quota_space'
|
||||
},
|
||||
{
|
||||
name: 'source_file_size'
|
||||
},
|
||||
{
|
||||
name: 'source_file_count'
|
||||
},
|
||||
{
|
||||
name: 'source_folder_count'
|
||||
},
|
||||
{
|
||||
name: 'orphan_file_count'
|
||||
},
|
||||
{
|
||||
name: 'orphan_file_size'
|
||||
},
|
||||
{
|
||||
name: 'alien_file_count'
|
||||
},
|
||||
{
|
||||
name: 'alien_file_size'
|
||||
},
|
||||
{
|
||||
name: 'total_backup_sets'
|
||||
},
|
||||
{
|
||||
name: 'last_backup_completed_time'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,88 +0,0 @@
|
||||
Ext.define('Duplicati.model.Task', {
|
||||
extend: 'Ext.data.Model',
|
||||
uses: [
|
||||
'Duplicati.model.TaskItems.BackendSettings',
|
||||
'Duplicati.model.TaskItems.Extensions',
|
||||
'Duplicati.model.TaskItems.Overrides',
|
||||
'Duplicati.model.TaskItems.EncryptionSettings',
|
||||
'Duplicati.model.TaskItems.CompressionSettings'
|
||||
],
|
||||
|
||||
associations: [{
|
||||
name: 'backendSettings',
|
||||
associationKey: 'BackendSettings',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.TaskItems.BackendSettings',
|
||||
getterName: 'getBackendSettings',
|
||||
setterName: 'setBackendSettings'
|
||||
},{
|
||||
name: 'extensions',
|
||||
associationKey: 'Extensions',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.TaskItems.Extensions',
|
||||
getterName: 'getExtensions',
|
||||
setterName: 'setExtensions'
|
||||
},{
|
||||
name: 'overrides',
|
||||
associationKey: 'Overrides',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.TaskItems.Overrides',
|
||||
getterName: 'getOverrides',
|
||||
setterName: 'setOverrides'
|
||||
},{
|
||||
name: 'encryptionSettings',
|
||||
associationKey: 'EncryptionSettings',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.TaskItems.EncryptionSettings',
|
||||
getterName: 'getEncryptionSettings',
|
||||
setterName: 'setEncryptionSettings'
|
||||
},{
|
||||
name: 'compressionSettings',
|
||||
associationKey: 'CompressionSettings',
|
||||
type: 'hasOne',
|
||||
model: 'Duplicati.model.TaskItems.CompressionSettings',
|
||||
getterName: 'getCompressionSettings',
|
||||
setterName: 'setCompressionSettings'
|
||||
}],
|
||||
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'Filter'
|
||||
},
|
||||
{
|
||||
name: 'ID'
|
||||
},
|
||||
{
|
||||
name: 'Service'
|
||||
},
|
||||
{
|
||||
name: 'Encryptionkey'
|
||||
},
|
||||
{
|
||||
name: 'SourcePath'
|
||||
},
|
||||
{
|
||||
name: 'ScheduleID'
|
||||
},
|
||||
{
|
||||
name: 'KeepFull'
|
||||
},
|
||||
{
|
||||
name: 'KeepTime'
|
||||
},
|
||||
{
|
||||
name: 'FullAfter'
|
||||
},
|
||||
{
|
||||
name: 'IncludeSetup',
|
||||
type: 'boolean'
|
||||
},
|
||||
{
|
||||
name: 'EncryptionModule'
|
||||
},
|
||||
{
|
||||
name: 'CompressionModule'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.BackendSettings', {
|
||||
extend: 'Ext.data.Model',
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'UI_Checked_empty'
|
||||
},
|
||||
{
|
||||
name: 'Destination'
|
||||
},
|
||||
{
|
||||
name: 'Username'
|
||||
},
|
||||
{
|
||||
name: 'Password'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.BackupLocation', {
|
||||
extend: 'Ext.data.Model',
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'Location',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
name: 'TotalSize',
|
||||
type: 'float'
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.CompressionSettings', {
|
||||
extend: 'Ext.data.Model'
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.EncryptionSettings', {
|
||||
extend: 'Ext.data.Model'
|
||||
});
|
||||
@@ -1,120 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.Extensions', {
|
||||
extend: 'Ext.data.Model',
|
||||
|
||||
fields: [
|
||||
{
|
||||
name: 'UploadBandwidth',
|
||||
mapping: "Upload Bandwidth"
|
||||
},
|
||||
{
|
||||
name: 'DownloadBandwidth',
|
||||
mapping: 'Download Bandwidth'
|
||||
},
|
||||
{
|
||||
name: 'MaxUploadSize',
|
||||
mapping: 'Max Upload Size'
|
||||
},
|
||||
{
|
||||
name: 'VolumeSize',
|
||||
mapping: 'Volume Size'
|
||||
},
|
||||
{
|
||||
name: 'ThreadPriority',
|
||||
mapping: 'Thread Priority'
|
||||
},
|
||||
{
|
||||
name: 'AsyncTransfer',
|
||||
mapping: 'Async Transfer',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'IgnoreTimestamps',
|
||||
mapping: 'Ignore Timestamps',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'FileSizeLimit',
|
||||
mapping: 'File Size Limit'
|
||||
},
|
||||
{
|
||||
name: 'DisableAESfallbackencryption',
|
||||
mapping: 'Disable AES fallback encryption',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesVersion',
|
||||
mapping: 'Select Files - Version',
|
||||
type: 'int',
|
||||
convert: parseInt
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesUseSimpleMode',
|
||||
mapping: 'Select Files - Use Simple Mode',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesIncludeDocuments',
|
||||
mapping: 'Select Files - Include Documents',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesIncludeDesktop',
|
||||
mapping: 'Select Files - Include Desktop',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesIncludeMusic',
|
||||
mapping: 'Select Files - Include Music',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesIncludeImages',
|
||||
mapping: 'Select Files - Include Images',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectFilesIncludeAppData',
|
||||
mapping: 'Select Files - Include AppData',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectWhenWarnedNoSchedule',
|
||||
mapping: 'Select When - Warned No Schedule',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectWhenWarnedTooManyIncrementals',
|
||||
mapping: 'Select When - Warned Too Many Incrementals',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'SelectWhenWarnedNoIncrementals',
|
||||
mapping: 'Select When - Warned No Incrementals',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'PasswordSettingsWarnedNoPassword',
|
||||
mapping: 'Password Settings - Warned No Password',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
},
|
||||
{
|
||||
name: 'CleanupSettingsWarnedNoCleanup',
|
||||
mapping: 'Cleanup Settings - Warned No Cleanup',
|
||||
type: 'boolean',
|
||||
convert: parseBool
|
||||
}
|
||||
]
|
||||
});
|
||||
@@ -1,3 +0,0 @@
|
||||
Ext.define('Duplicati.model.TaskItems.Overrides', {
|
||||
extend: 'Ext.data.Model'
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
Ext.define('Duplicati.model.override.BackupJob', {
|
||||
requires: 'Duplicati.model.BackupJob'
|
||||
}, function() {
|
||||
Ext.override(Duplicati.model.BackupJob, {
|
||||
|
||||
setValues: function(values, arrayField) {
|
||||
var me = this;
|
||||
|
||||
function setVal(fieldId, val) {
|
||||
if (arrayField) {
|
||||
fieldId = arrayField + '.' + fieldId;
|
||||
}
|
||||
var field = me.findField(fieldId);
|
||||
if (field) {
|
||||
field.setValue(val);
|
||||
if (me.trackResetOnLoad) {
|
||||
field.resetOriginalValue();
|
||||
}
|
||||
} else if(Ext.isObject(val)) {
|
||||
me.setValues(val, fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
alert( "Servus setValues" );
|
||||
|
||||
if (Ext.isArray(values)) {
|
||||
// array of objects
|
||||
Ext.each(values, function(val) {
|
||||
setVal(val.id, val.value);
|
||||
});
|
||||
} else {
|
||||
// object hash
|
||||
Ext.iterate(values, setVal);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
Ext.define('Duplicati.store.AdditionalPathStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
requires: [
|
||||
'Duplicati.model.TaskItems.BackupLocation'
|
||||
],
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
autoLoad: true,
|
||||
storeId: 'pathStore',
|
||||
model: 'Duplicati.model.TaskItems.BackupLocation',
|
||||
proxy: {
|
||||
type: 'memory',
|
||||
reader: {
|
||||
type: 'json',
|
||||
root: 'root'
|
||||
}
|
||||
}
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
Ext.define('Duplicati.store.BackupJob', {
|
||||
extend: 'Ext.data.Store',
|
||||
requires: [
|
||||
'Duplicati.model.BackupJob'
|
||||
],
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'MyJsonStore',
|
||||
model: 'Duplicati.model.BackupJob',
|
||||
proxy: {
|
||||
type: 'ajax',
|
||||
url: '/control.cgi?action=get-schedule-details',
|
||||
reader: {
|
||||
type: 'json',
|
||||
idProperty: 'ID',
|
||||
root: 'data'
|
||||
}
|
||||
}
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
Ext.define('Duplicati.store.DefaultSizeRangeStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'DefaultSizeRangeStore',
|
||||
data: [
|
||||
{key: 'b', value: 'bytes'},
|
||||
{key: 'kb', value: 'Kilobytes'},
|
||||
{key: 'mb', value: 'Megabytes'},
|
||||
{key: 'gb', value: 'Gigabytes'},
|
||||
{key: 'tb', value: 'Terabyts'}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
name: 'value'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
Ext.define('Duplicati.store.DefaultTimeRangesStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'DefaultTimeRangesStore',
|
||||
data: [
|
||||
{key: 's', value: 'Seconds'},
|
||||
{key: 'm', value: 'Minutes'},
|
||||
{key: 'h', value: 'Hours'},
|
||||
{key: 'D', value: 'Days'},
|
||||
{key: 'W', value: 'Weeks'},
|
||||
{key: 'M', value: 'Months'},
|
||||
{key: 'Y', value: 'Years'}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
name: 'value'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,24 +0,0 @@
|
||||
Ext.define('Duplicati.store.DeleteOldChainsWhenStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'DeleteOldChainsWhenStore',
|
||||
data: [
|
||||
{key: '0', value: 'When there are more chains than ...'},
|
||||
{key: '1', value: 'When the last backup in the chain is older than ...'},
|
||||
{key: '2', value: 'Never'}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
name: 'value'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,44 +0,0 @@
|
||||
Ext.define('Duplicati.store.EncryptionMethodStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
autoLoad: true,
|
||||
filterOnLoad: false,
|
||||
sortOnLoad: false,
|
||||
storeId: 'EncryptionMethodStore',
|
||||
|
||||
/*listeners: {
|
||||
load: function(records, success, eOpts) {
|
||||
alert('WS');
|
||||
}
|
||||
|
||||
//TODO: Should we add the empty row here instad?
|
||||
|
||||
},*/
|
||||
|
||||
fields: [
|
||||
{
|
||||
mapping: 'DisplayName',
|
||||
name: 'encryptionMethodName',
|
||||
type: 'string'
|
||||
},
|
||||
{
|
||||
mapping: 'FilenameExtension',
|
||||
name: 'encryptionMethodIdentifier',
|
||||
type: 'string'
|
||||
}
|
||||
],
|
||||
proxy: {
|
||||
type: 'ajax',
|
||||
url: '/control.cgi?action=list-installed-encryption-modules',
|
||||
reader: {
|
||||
type: 'array',
|
||||
idProperty: 'id'
|
||||
}
|
||||
}
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
Ext.define('Duplicati.store.LabelStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'LabelStore',
|
||||
data: [
|
||||
{name: 'private'},
|
||||
{name: 'important'},
|
||||
{name: 'weekly'}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'name'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,37 +0,0 @@
|
||||
Ext.define('Duplicati.store.MyComputerTreeStore', {
|
||||
extend: 'Ext.data.TreeStore',
|
||||
|
||||
constructor: function(cfg) {
|
||||
cfg = cfg || {};
|
||||
this.callParent([Ext.apply({
|
||||
autoLoad: true,
|
||||
filterOnLoad: false,
|
||||
sortOnLoad: false,
|
||||
storeId: 'MyComputerTreeStore',
|
||||
folderSort: false,
|
||||
nodeParam: 'path',
|
||||
root: {
|
||||
text: 'My Computer',
|
||||
id: '/',
|
||||
expanded: false
|
||||
},
|
||||
proxy: {
|
||||
type: 'ajax',
|
||||
url: '/control.cgi?action=get-folder-contents&onlyfolders=true',
|
||||
reader: {
|
||||
type: 'json',
|
||||
root: '/'
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
mapping: 'check',
|
||||
name: 'checked'
|
||||
},
|
||||
{
|
||||
name: 'text'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,25 +0,0 @@
|
||||
Ext.define('Duplicati.store.NewBackupChainWhenStore', {
|
||||
extend: 'Ext.data.Store',
|
||||
|
||||
constructor: function(cfg) {
|
||||
var me = this;
|
||||
cfg = cfg || {};
|
||||
me.callParent([Ext.apply({
|
||||
storeId: 'NewBackupChainWhenStore',
|
||||
data: [
|
||||
{key: '0', value: 'When there are more backups in the chain than ...'},
|
||||
{key: '1', value: 'When the chain is older than ...'},
|
||||
{key: '2', value: 'Once, and then backup changes only'},
|
||||
{key: '3', value: 'Every time'}
|
||||
],
|
||||
fields: [
|
||||
{
|
||||
name: 'key'
|
||||
},
|
||||
{
|
||||
name: 'value'
|
||||
}
|
||||
]
|
||||
}, cfg)]);
|
||||
}
|
||||
});
|
||||
@@ -1,95 +0,0 @@
|
||||
Ext.define('Duplicati.store.Schedules', {
|
||||
extend: 'Ext.data.Store',
|
||||
model: 'Duplicati.model.Schedule',
|
||||
autoLoad: true,
|
||||
|
||||
/* Sort order,
|
||||
* first by queue position
|
||||
* second by next schedule time,
|
||||
* third by last backup time
|
||||
* */
|
||||
sorters: [{
|
||||
sorterFn: function(a, b) {
|
||||
var schedulePosA = Duplicati.service.getSchedulePosition(a.get("ID"));
|
||||
var schedulePosB = Duplicati.service.getSchedulePosition(b.get("ID"));
|
||||
|
||||
if(schedulePosA >= 0 && schedulePosB >= 0)
|
||||
{
|
||||
if (schedulePosA == schedulePosB)
|
||||
return 0;
|
||||
else
|
||||
return schedulePosA < schedulePosB ? -1 : 1;
|
||||
}
|
||||
else if (schedulePosA >= 0)
|
||||
return -1;
|
||||
else if (schedulePosB >= 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}, {
|
||||
sorterFn: function(a, b) {
|
||||
var nextTimeA = Duplicati.Utility.parseJsonDate(a.get('NextScheduledTime'));
|
||||
var nextTimeB = Duplicati.Utility.parseJsonDate(b.get('NextScheduledTime'));
|
||||
var now = new Date().getTime();
|
||||
|
||||
//Scheduled time in past, but not in queue, so it will not run
|
||||
if (nextTimeA != null && nextTimeA < now)
|
||||
nextTimeA = null;
|
||||
if (nextTimeB != null && nextTimeB < now)
|
||||
nextTimeB = null;
|
||||
|
||||
if (nextTimeA != null && nextTimeB != null)
|
||||
{
|
||||
nextTimeA = nextTimeA.getTime();
|
||||
nextTimeB = nextTimeB.getTime();
|
||||
if (nextTimeA == nextTimeB)
|
||||
return 0;
|
||||
else
|
||||
return nextTimeA < nextTimeB ? -1 : 1;
|
||||
}
|
||||
else if (nextTimeA != null)
|
||||
return -1;
|
||||
else if (nextTimeB != null)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}, {
|
||||
sorterFn: function(a, b) {
|
||||
var lastTimeA = Duplicati.Utility.parseJsonDate(a.data.Metadata['last-backup-completed-time']);
|
||||
var lastTimeB = Duplicati.Utility.parseJsonDate(b.data.Metadata['last-backup-completed-time']);
|
||||
if (lastTimeA == null)
|
||||
lastTimeA = Duplicati.Utility.parseJsonDate(a.data.Metadata['last-backup-date']);
|
||||
if (lastTimeB == null)
|
||||
lastTimeB = Duplicati.Utility.parseJsonDate(b.data.Metadata['last-backup-date']);
|
||||
|
||||
if ((lastTimeA != null && lastTimeB != null))
|
||||
{
|
||||
lastTimeA = lastTimeA.getTime();
|
||||
lastTimeB = lastTimeB.getTime();
|
||||
|
||||
if (lastTimeA == lastTimeB)
|
||||
return 0;
|
||||
else
|
||||
return lastTimeA > lastTimeB ? -1 : 1;
|
||||
}
|
||||
else if (lastTimeA != null)
|
||||
return -1;
|
||||
else if (lastTimeB != null)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}, {
|
||||
property: 'Name'
|
||||
}],
|
||||
|
||||
proxy: {
|
||||
type: 'ajax',
|
||||
url: 'control.cgi?action=list-schedules',
|
||||
reader: {
|
||||
type: 'json'
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
Ext.define('Duplicati.view.AboutWindow', {
|
||||
statics: {
|
||||
dialogWindow: null,
|
||||
show: function() {
|
||||
if (this.dialogWindow == null)
|
||||
this.dialogWindow = Ext.create('Ext.window.Window', {
|
||||
title: 'About Duplicati',
|
||||
height: 200,
|
||||
width: 400,
|
||||
modal: true,
|
||||
resizable: false,
|
||||
buttonAlign: 'center',
|
||||
closeAction: 'hide',
|
||||
layout: 'auto',
|
||||
items: [{
|
||||
xtype: 'label',
|
||||
text: 'Duplicati 2.0'
|
||||
}],
|
||||
buttons: [{
|
||||
xtype: 'button',
|
||||
text: 'OK',
|
||||
handler: function() {
|
||||
Duplicati.view.AboutWindow.dialogWindow.hide();
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
this.dialogWindow.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,927 +0,0 @@
|
||||
Ext.define('Duplicati.view.BackupConfig', {
|
||||
extend: 'Ext.form.Panel',
|
||||
|
||||
xtype: 'backupconfigpanel',
|
||||
draggable: false,
|
||||
floating: false,
|
||||
frame: false,
|
||||
height: 550,
|
||||
width: 698,
|
||||
autoScroll: false,
|
||||
resizable: false,
|
||||
layout: {
|
||||
type: 'absolute'
|
||||
},
|
||||
bodyBorder: false,
|
||||
closable: false,
|
||||
collapsed: false,
|
||||
collapsible: false,
|
||||
frameHeader: false,
|
||||
hideCollapseTool: false,
|
||||
overlapHeader: false,
|
||||
preventHeader: true,
|
||||
title: 'Backup Job',
|
||||
titleCollapse: true,
|
||||
waitMsgTarget: true,
|
||||
|
||||
initComponent: function(params) {
|
||||
var me = this;
|
||||
|
||||
me.scheduleId = me.initialConfig.scheduleId;
|
||||
|
||||
me.initialConfig = Ext.apply({
|
||||
waitMsgTarget: true
|
||||
}, me.initialConfig);
|
||||
|
||||
Ext.applyIf(me, {
|
||||
items: [
|
||||
{
|
||||
xtype: 'tabpanel',
|
||||
height: 484,
|
||||
id: 'panelWizard',
|
||||
activeTab: 0,
|
||||
plain: true,
|
||||
layout: {
|
||||
deferredRender: true,
|
||||
type: 'card'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'panel',
|
||||
height: 409,
|
||||
activeItem: 0,
|
||||
layout: {
|
||||
type: 'anchor'
|
||||
},
|
||||
hideCollapseTool: false,
|
||||
title: 'General',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
collapsed: false,
|
||||
collapsible: false,
|
||||
title: 'Name & Label',
|
||||
formBind: false,
|
||||
items: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
id: 'Name',
|
||||
name: 'Schedule.Name',
|
||||
fieldLabel: 'Name of Backup',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
msgTarget: 'side',
|
||||
allowBlank: false,
|
||||
blankText: 'Required: Short Description to identify your Backup',
|
||||
disableKeyFilter: false,
|
||||
emptyText: 'Short Description to identify your Backup',
|
||||
enableKeyEvents: false,
|
||||
minLength: 6,
|
||||
anchor: '100%',
|
||||
formBind: false
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
id: 'sLabel',
|
||||
name: 'Schedule.Path',
|
||||
fieldLabel: 'Labels/Groups',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
msgTarget: 'side',
|
||||
blankText: 'Required: Select a Label or Create a new one',
|
||||
emptyText: 'Select a Label or Create a new one',
|
||||
displayField: 'name',
|
||||
multiSelect: true,
|
||||
store: 'LabelStore',
|
||||
anchor: '100%',
|
||||
formBind: false,
|
||||
listeners: {
|
||||
change: {
|
||||
fn: me.onSLabelChange,
|
||||
scope: me
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
height: 27,
|
||||
hidden: true,
|
||||
layout: {
|
||||
align: 'stretchmax',
|
||||
pack: 'end',
|
||||
type: 'hbox'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'button',
|
||||
text: 'Manage Labels',
|
||||
flex: 1
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
height: 220,
|
||||
hidden: false,
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
styleHtmlContent: false,
|
||||
activeItem: 0,
|
||||
title: 'Security',
|
||||
formBind: false,
|
||||
items: [
|
||||
{
|
||||
xtype: 'component',
|
||||
floating: false,
|
||||
html: 'It is recommended to encrypt all backups which are stored on remote Servers',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
tpl: [
|
||||
'<h1>sepp</h1>'
|
||||
],
|
||||
autoScroll: false
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
id: 'EncryptionModule',
|
||||
name: 'Task.EncryptionModule',
|
||||
fieldLabel: 'Encryption method',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
queryParam: 'encryptionMethodName',
|
||||
store: 'EncryptionMethodStore',
|
||||
displayField: 'encryptionMethodName',
|
||||
valueField: 'encryptionMethodIdentifier',
|
||||
anchor: '100%',
|
||||
formBind: false,
|
||||
queryMode: 'local'
|
||||
},
|
||||
{
|
||||
xtype: 'textfield',
|
||||
disabled: false,
|
||||
id: 'sEncryptionPassword',
|
||||
stateful: false,
|
||||
inputType: 'password',
|
||||
name: 'sEncryptionPassword',
|
||||
fieldLabel: 'Encryption Password',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
msgTarget: 'side',
|
||||
disableKeyFilter: false,
|
||||
minLength: 6,
|
||||
anchor: '100%',
|
||||
formBind: false
|
||||
},
|
||||
{
|
||||
xtype: 'textfield',
|
||||
validator: function(value) {
|
||||
var password1 = Ext.getCmp('sEncryptionPassword');
|
||||
return (value === password1.getValue() || value.length < 6) ? true : 'Passwords do not match.';
|
||||
},
|
||||
id: 'sEncryptionPasswordCheck',
|
||||
stateful: false,
|
||||
maintainFlex: false,
|
||||
inputType: 'password',
|
||||
name: 'sEncryptionPasswordCheck',
|
||||
readOnly: false,
|
||||
fieldLabel: 'Repeat Password',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
minLength: 6,
|
||||
anchor: '100%',
|
||||
formBind: false
|
||||
},
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'btnGeneratePassword',
|
||||
style: 'float:right',
|
||||
autoWidth: true,
|
||||
scale: 'medium',
|
||||
text: 'Generate Password',
|
||||
formBind: false
|
||||
},
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'btnPassword',
|
||||
style: 'float:right',
|
||||
autoWidth: true,
|
||||
repeat: false,
|
||||
scale: 'medium',
|
||||
text: 'Show Password',
|
||||
formBind: false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'panel',
|
||||
height: 367,
|
||||
activeItem: 1,
|
||||
title: 'Source Data',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Standard Locations',
|
||||
items: [
|
||||
{
|
||||
xtype: 'checkboxgroup',
|
||||
id: 'chkgStandardLocations',
|
||||
margin: '',
|
||||
width: 400,
|
||||
fieldLabel: '',
|
||||
columns: 1,
|
||||
vertical: true,
|
||||
name: 'Files',
|
||||
items: [
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkMyDocuments',
|
||||
name: 'Task.Extensions.SelectFilesIncludeDocuments',
|
||||
hideLabel: false,
|
||||
labelPad: 10,
|
||||
boxLabel: 'My Documents'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkMyMusic',
|
||||
name: 'Task.Extensions.SelectFilesIncludeMusic',
|
||||
boxLabel: 'My Music'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkMyVideos',
|
||||
name: 'Task.Extensions.SelectFilesIncludeMyVideos',
|
||||
boxLabel: 'My Videos'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkMyPictures',
|
||||
name: 'Task.Extensions.SelectFilesIncludeImages',
|
||||
boxLabel: 'My Pictures'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkAppSettings',
|
||||
name: 'Task.Extensions.SelectFilesIncludeAppData',
|
||||
boxLabel: 'Application Settings'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'gridpanel',
|
||||
id: 'Locations',
|
||||
margin: 10,
|
||||
bodyBorder: false,
|
||||
title: 'Additional Locations',
|
||||
sortableColumns: false,
|
||||
store: 'AdditionalPathStore',
|
||||
columnLines: false,
|
||||
tools: [
|
||||
{
|
||||
xtype: 'tool',
|
||||
id: 'tAddLocation',
|
||||
type: 'plus'
|
||||
}
|
||||
],
|
||||
listeners: {
|
||||
added: {
|
||||
fn: me.onLocationsAdded,
|
||||
scope: me
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
xtype: 'gridcolumn',
|
||||
renderer: function(value, metaData, record, rowIndex, colIndex, store, view) {
|
||||
// this works only for Windows ...
|
||||
// ...need to identify OS before
|
||||
if( value.indexOf("/") == 0 && value.length > 2 && value[2] == ':') {
|
||||
value = value.substr( 1, value.length);
|
||||
value = value.replace(/\//g,"\\");
|
||||
}
|
||||
return value;
|
||||
},
|
||||
dataIndex: 'Location',
|
||||
text: 'Location'
|
||||
},
|
||||
{
|
||||
xtype: 'numbercolumn',
|
||||
dataIndex: 'TotalSize',
|
||||
text: 'Size'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'gridpanel',
|
||||
margin: 10,
|
||||
bodyBorder: false,
|
||||
title: 'Filter Rules',
|
||||
forceFit: true,
|
||||
hideHeaders: true,
|
||||
scroll: 'none',
|
||||
sortableColumns: false,
|
||||
store: 'LabelStore',
|
||||
columnLines: false,
|
||||
columns: [
|
||||
{
|
||||
xtype: 'gridcolumn',
|
||||
id: 'fldFilter',
|
||||
margin: '',
|
||||
dataIndex: 'string',
|
||||
menuDisabled: false,
|
||||
text: 'Filter'
|
||||
}
|
||||
],
|
||||
tools: [
|
||||
{
|
||||
xtype: 'tool',
|
||||
id: 'tAddFilter',
|
||||
type: 'plus'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'panel',
|
||||
activeItem: 2,
|
||||
closable: false,
|
||||
title: 'Target',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
frame: false,
|
||||
id: 'pConnection',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Connection',
|
||||
items: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
id: 'sNameOfConnection',
|
||||
name: 'NameOfConnection',
|
||||
fieldLabel: 'Name of Connection',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
height: 27,
|
||||
layout: {
|
||||
align: 'stretch',
|
||||
type: 'hbox'
|
||||
},
|
||||
combineErrors: true,
|
||||
combineLabels: false,
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
msgTarget: 'side',
|
||||
anchor: '100%',
|
||||
formBind: false,
|
||||
items: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
floating: false,
|
||||
id: 'sConnectionURL',
|
||||
name: 'ConnectionURL',
|
||||
fieldLabel: 'Connection URL',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 140,
|
||||
flex: 10
|
||||
},
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'btnBuildURL',
|
||||
text: '...',
|
||||
flex: 1,
|
||||
margins: '0 0 5 5'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
padding: '0 0 0 145',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
height: 110,
|
||||
id: 'pCredentials',
|
||||
margin: '',
|
||||
padding: 10,
|
||||
layout: {
|
||||
align: 'stretchmax',
|
||||
pack: 'center',
|
||||
padding: 10,
|
||||
type: 'vbox'
|
||||
},
|
||||
collapsible: false,
|
||||
title: 'Credentials',
|
||||
items: [
|
||||
{
|
||||
xtype: 'textfield',
|
||||
id: 'sUsername',
|
||||
maxWidth: 330,
|
||||
minWidth: 330,
|
||||
width: 330,
|
||||
name: 'sUsername',
|
||||
fieldLabel: 'Username',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 70
|
||||
},
|
||||
{
|
||||
xtype: 'textfield',
|
||||
id: 'sPassword',
|
||||
maxWidth: 330,
|
||||
minWidth: 330,
|
||||
width: 330,
|
||||
inputType: 'password',
|
||||
name: 'sPassword',
|
||||
fieldLabel: 'Password',
|
||||
labelAlign: 'right',
|
||||
labelWidth: 70
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'button',
|
||||
margin: '5 0 0 0',
|
||||
text: 'Test Connection'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'panel',
|
||||
activeItem: 3,
|
||||
title: 'Run & Delete',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
id: 'fsSchedule',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Schedule',
|
||||
items: [
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
id: 'chkRunRegular',
|
||||
margin: '0 0 20 0',
|
||||
name: 'chkRunRegular',
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
boxLabel: 'Run regulary at the specified times',
|
||||
checked: true,
|
||||
anchor: '100%',
|
||||
listeners: {
|
||||
change: {
|
||||
fn: me.onChkRunRegularChange,
|
||||
scope: me
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
layout: {
|
||||
align: 'stretchmax',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'timefield',
|
||||
margin: '0 5 0 0',
|
||||
fieldLabel: 'Next Time',
|
||||
name: 'Schedule.When.Time',
|
||||
labelSeparator: ' ',
|
||||
flex: 5
|
||||
},
|
||||
{
|
||||
xtype: 'datefield',
|
||||
maintainFlex: false,
|
||||
name: 'Schedule.When.Date',
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
flex: 3
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
layout: {
|
||||
align: 'stretchmax',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: '',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'numberfield',
|
||||
margin: '0 5 0 0',
|
||||
fieldLabel: 'Run again every',
|
||||
name: 'Schedule.Repeat.Number',
|
||||
minValue: 0,
|
||||
labelSeparator: ' ',
|
||||
flex: 5
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
flex: 3,
|
||||
name: 'Schedule.Repeat.Suffix',
|
||||
store: 'DefaultTimeRangesStore',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
displayField: 'value',
|
||||
valueField: 'key'
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
layout: {
|
||||
align: 'stretchmax',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: '',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'checkboxgroup',
|
||||
width: 400,
|
||||
fieldLabel: 'Allowed Days',
|
||||
flex: 5,
|
||||
name: 'Schedule.AllowedWeekdays',
|
||||
items: [
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Mon'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Tue'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Wed'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Thu'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Fri'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Sat'
|
||||
},
|
||||
{
|
||||
xtype: 'checkboxfield',
|
||||
boxLabel: 'Sun'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Full or incremental',
|
||||
items: [
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Make a full backup',
|
||||
labelWidth: 120,
|
||||
store: 'NewBackupChainWhenStore',
|
||||
displayField: 'value',
|
||||
valueField: 'key',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
height: 27,
|
||||
layout: {
|
||||
align: 'stretch',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'numberfield',
|
||||
hideEmptyLabel: false,
|
||||
labelWidth: 120,
|
||||
minValue: 0,
|
||||
flex: 5,
|
||||
margins: '0 5 0 0'
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Label',
|
||||
store: 'DefaultTimeRangesStore',
|
||||
displayField: 'value',
|
||||
valueField: 'key',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
hideLabel: true,
|
||||
flex: 2
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 8
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Delete old backups',
|
||||
items: [
|
||||
{
|
||||
xtype: 'displayfield',
|
||||
margin: '0 0 10 0',
|
||||
value: 'Please note: A full and its incremental backups are called a chain. Duplicati deletes entire backup chains only. A chain is deleted, when all its backups match the specified rule.',
|
||||
hideEmptyLabel: false,
|
||||
labelWidth: 120,
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Delete backups',
|
||||
labelWidth: 120,
|
||||
store: 'DeleteOldChainsWhenStore',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
displayField: 'value',
|
||||
valueField: 'key',
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
height: 27,
|
||||
layout: {
|
||||
align: 'stretch',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'numberfield',
|
||||
hideEmptyLabel: false,
|
||||
labelWidth: 120,
|
||||
minValue: 0,
|
||||
flex: 5,
|
||||
margins: '0 5 0 0'
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Label',
|
||||
store: 'DefaultTimeRangesStore',
|
||||
displayField: 'value',
|
||||
valueField: 'key',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
hideLabel: true,
|
||||
flex: 2
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 8
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'panel',
|
||||
activeItem: -1,
|
||||
title: 'Options',
|
||||
items: [
|
||||
{
|
||||
xtype: 'fieldset',
|
||||
margin: 10,
|
||||
padding: 10,
|
||||
title: 'Options',
|
||||
items: [
|
||||
{
|
||||
xtype: 'combobox',
|
||||
fieldLabel: 'Missed Backup',
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'displayfield',
|
||||
margin: '0 0 30 0 0',
|
||||
value: 'When the computer or Duplicati turned off when a backup should have been made, this defines how Duplicati will handle this situation',
|
||||
hideEmptyLabel: false,
|
||||
hideLabel: false,
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'fieldcontainer',
|
||||
height: 27,
|
||||
layout: {
|
||||
align: 'stretch',
|
||||
type: 'hbox'
|
||||
},
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
items: [
|
||||
{
|
||||
xtype: 'numberfield',
|
||||
fieldLabel: 'Max archive size',
|
||||
name: 'Task.Extensions.VolumeSize.Number',
|
||||
minValue: 0,
|
||||
flex: 2,
|
||||
margins: '0 5 0 0'
|
||||
},
|
||||
{
|
||||
xtype: 'combobox',
|
||||
name: 'Task.Extensions.VolumeSize.Suffix',
|
||||
fieldLabel: 'Label',
|
||||
hideLabel: true,
|
||||
store: 'DefaultSizeRangeStore',
|
||||
valueField: 'key',
|
||||
displayField: 'value',
|
||||
editable: false,
|
||||
forceSelection: true,
|
||||
flex: 1
|
||||
},
|
||||
{
|
||||
xtype: 'container',
|
||||
flex: 4
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'displayfield',
|
||||
margin: '0 0 30 0',
|
||||
value: 'Duplicati splitts his backup archives into smaller chunks. This setting defines what the maximum filesize is for this chunks.',
|
||||
hideEmptyLabel: false,
|
||||
hideLabel: false,
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'textfield',
|
||||
fieldLabel: 'Backup file prefix',
|
||||
anchor: '100%'
|
||||
},
|
||||
{
|
||||
xtype: 'displayfield',
|
||||
value: 'This prefix is used for all backup file names. This must be set to unique values if multiple backups are stored in the same target path.',
|
||||
hideEmptyLabel: false,
|
||||
hideLabel: false,
|
||||
anchor: '100%'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'propertygrid',
|
||||
height: 115,
|
||||
margin: 10,
|
||||
autoScroll: true,
|
||||
title: 'Advanced',
|
||||
scroll: 'vertical',
|
||||
columnLines: true,
|
||||
nameColumnWidth: 180,
|
||||
tools: [
|
||||
{
|
||||
xtype: 'tool',
|
||||
id: 'tAddOverride',
|
||||
type: 'plus'
|
||||
}
|
||||
],
|
||||
source: {
|
||||
'Property 1': 'String',
|
||||
'Property 2': true,
|
||||
'Property 3': '2012-07-28T23:31:46',
|
||||
'Property 4': 123
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
dockedItems: [
|
||||
{
|
||||
xtype: 'toolbar',
|
||||
frame: false,
|
||||
height: 40,
|
||||
width: 40,
|
||||
vertical: true,
|
||||
dock: 'bottom',
|
||||
layout: {
|
||||
align: 'middle',
|
||||
pack: 'end',
|
||||
padding: 0,
|
||||
type: 'hbox'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'buttongroup',
|
||||
border: '',
|
||||
minButtonWidth: 100,
|
||||
titleCollapse: false,
|
||||
columns: 2,
|
||||
layout: {
|
||||
columns: 2,
|
||||
type: 'table'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'button',
|
||||
frame: false,
|
||||
id: 'btnClose',
|
||||
scale: 'medium',
|
||||
text: 'Close'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
xtype: 'buttongroup',
|
||||
border: '',
|
||||
minButtonWidth: 100,
|
||||
titleCollapse: false,
|
||||
columns: 2,
|
||||
layout: {
|
||||
columns: 2,
|
||||
type: 'table'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'btnNext',
|
||||
scale: 'medium',
|
||||
text: 'Next >'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
listeners: {
|
||||
}
|
||||
});
|
||||
|
||||
me.callParent(arguments);
|
||||
},
|
||||
|
||||
onSLabelChange: function(field, newValue, oldValue, options) {
|
||||
//Add new Label, by capturing the typing of the user
|
||||
|
||||
},
|
||||
|
||||
onLocationsAdded: function(abstractcomponent, container, pos, options) {
|
||||
// calculate consumed space of entry
|
||||
},
|
||||
|
||||
onChkRunRegularChange: function(field, newValue, oldValue, options) {
|
||||
Ext.Array.forEach(Ext.getCmp("fsSchedule").query("combobox,datefield,timefield,numberfield,checkboxgroup"), function(field) {
|
||||
field.setDisabled(!newValue);
|
||||
if (!Ext.isIE6 && field.el != null) {
|
||||
field.el.animate({opacity: !newValue ? .5 : 1});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,67 +0,0 @@
|
||||
Ext.define('Duplicati.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 (Duplicati.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 ' + Duplicati.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() {
|
||||
Duplicati.service.reconnectNow();
|
||||
}
|
||||
}]
|
||||
});
|
||||
|
||||
this.dialogWindow.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
Ext.define('Duplicati.view.StatuswindowFooter' ,{
|
||||
extend: 'Ext.panel.Panel',
|
||||
alias : 'widget.statuswindowfooter',
|
||||
|
||||
title : 'Status line',
|
||||
header: false,
|
||||
border: 0,
|
||||
layout: 'column',
|
||||
cls: 'footer-panel',
|
||||
|
||||
items: [{
|
||||
xtype: 'label',
|
||||
html: 'A <a href="http://www.duplicati.com/news/duplicati132available" target="_blank">new version</a> is available',
|
||||
handler: function() { window.open('https://www.facebook.com/pages/Duplicati/105118456272281') },
|
||||
columnWidth: 1,
|
||||
margin: 10
|
||||
},{
|
||||
xtype: 'button',
|
||||
text: 'Facebook',
|
||||
handler: function() { window.open('https://www.facebook.com/pages/Duplicati/105118456272281') },
|
||||
margin: 10
|
||||
},{
|
||||
xtype: 'button',
|
||||
text: 'Google+',
|
||||
handler: function() { window.open('https://plus.google.com/105271984558189185842/posts') },
|
||||
margin: 10
|
||||
},{
|
||||
xtype: 'button',
|
||||
text: 'Donate!',
|
||||
handler: function() { window.open('https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=paypal%40hexad%2edk&item_name=Duplicati%20Donation&no_shipping=2&no_note=1&tax=0¤cy_code=EUR&bn=PP%2dDonationsBF&charset=UTF%2d8&lc=US') },
|
||||
margin: 10
|
||||
}],
|
||||
|
||||
initComponent: function() {
|
||||
this.callParent(arguments);
|
||||
}
|
||||
});
|
||||
@@ -1,138 +0,0 @@
|
||||
Ext.define('Duplicati.view.StatuswindowHeader' ,{
|
||||
extend: 'Ext.panel.Panel',
|
||||
alias : 'widget.statuswindowheader',
|
||||
|
||||
title : 'Main App controls',
|
||||
header: false,
|
||||
border: 0,
|
||||
layout: 'column',
|
||||
cls: 'header-panel',
|
||||
|
||||
items: [{
|
||||
xtype: 'button',
|
||||
cls: 'main-button',
|
||||
text: 'Duplicati',
|
||||
iconCls: 'unused-marker',
|
||||
minHeight: 35,
|
||||
id: 'status-window-header-main-button',
|
||||
margin: 10,
|
||||
menu: [{
|
||||
id: 'status-window-header-pause-menu',
|
||||
text: 'Pause Duplicati',
|
||||
handler: function() { Duplicati.service.togglePause(); }
|
||||
},{
|
||||
id: 'status-window-header-pause-duration-menu',
|
||||
xtype: 'form',
|
||||
layout: 'hbox',
|
||||
plain: true,
|
||||
items: [{
|
||||
xtype: 'label',
|
||||
cls: 'x-menu-item-text',
|
||||
text: 'Pause '
|
||||
},{
|
||||
id: 'status-window-header-pause-duration-picker',
|
||||
cls: 'x-menu-item-text',
|
||||
xtype: 'numberfield',
|
||||
hideLabel: true,
|
||||
minValue: 1,
|
||||
maxValue: 600,
|
||||
value: 5,
|
||||
flex: 1,
|
||||
handler: function() { Duplicati.service.pause('5m'); }
|
||||
},{
|
||||
xtype: 'label',
|
||||
cls: 'x-menu-item-text',
|
||||
text: ' min.'
|
||||
}]
|
||||
}/*{
|
||||
text: 'Pause for x minutes',
|
||||
menu: [{
|
||||
text: 'Pause for 5 minutes',
|
||||
handler: function() { Duplicati.service.pause('5m'); }
|
||||
},{
|
||||
text: 'Pause for 15 minutes',
|
||||
handler: function() { Duplicati.service.pause('15m'); }
|
||||
},{
|
||||
text: 'Pause for 30 minutes',
|
||||
handler: function() { Duplicati.service.pause('30m'); }
|
||||
},{
|
||||
text: 'Pause for 60 minutes',
|
||||
handler: function() { Duplicati.service.pause('60m'); }
|
||||
}]
|
||||
}*/,{
|
||||
text: '*** Throttle ...'
|
||||
},{
|
||||
xtype: 'menuseparator'
|
||||
},{
|
||||
text: '*** Run backups',
|
||||
menu: [{
|
||||
text: 'All backups'
|
||||
},{
|
||||
text: 'Label: Work'
|
||||
},{
|
||||
text: 'Label: Home'
|
||||
},{
|
||||
text: 'Label: Amazon S3'
|
||||
}]
|
||||
},{
|
||||
xtype: 'menuseparator'
|
||||
},{
|
||||
text: '*** Settings'
|
||||
},{
|
||||
text: 'About',
|
||||
handler: function() { Duplicati.view.AboutWindow.show(); }
|
||||
}]
|
||||
},{
|
||||
xtype: 'label',
|
||||
html: ' ',
|
||||
columnWidth: 1
|
||||
},{
|
||||
xtype: 'panel',
|
||||
layout: 'vbox',
|
||||
align: 'center',
|
||||
margin: 5,
|
||||
border: 0,
|
||||
items: [{
|
||||
xtype: 'label',
|
||||
cls: 'throttle-label',
|
||||
text: '*** Throttle upload'
|
||||
},{
|
||||
xtype: 'slider',
|
||||
width: 200,
|
||||
value: 80,
|
||||
minValue: 0,
|
||||
maxValue: 100
|
||||
}]
|
||||
},{
|
||||
xtype: 'button',
|
||||
cls: 'add-new-backup-button',
|
||||
text: 'Add new backup',
|
||||
iconCls: 'unused-marker',
|
||||
minHeight: 35,
|
||||
margin: 10,
|
||||
handler: function() {
|
||||
Ext.create('Ext.window.Window', {
|
||||
title: 'Add new backup',
|
||||
bodyBorder: false,
|
||||
layout: 'fit',
|
||||
items: {
|
||||
xtype: 'backupconfigpanel'
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
},{
|
||||
xtype: 'button',
|
||||
id: 'status-window-header-pause-button',
|
||||
cls: 'pause-backup-button',
|
||||
iconCls: 'backups-paused',
|
||||
minHeight: 35,
|
||||
text: 'Pause',
|
||||
margin: 10,
|
||||
handler: function() { Duplicati.service.togglePause(); }
|
||||
}],
|
||||
|
||||
initComponent: function() {
|
||||
|
||||
this.callParent(arguments);
|
||||
}
|
||||
});
|
||||
@@ -1,229 +0,0 @@
|
||||
Ext.define('Duplicati.view.override.BackupConfig', {
|
||||
requires: 'Duplicati.view.BackupConfig'
|
||||
}, function() {
|
||||
Ext.override(Duplicati.view.BackupConfig, {
|
||||
setFieldValue: function(fieldId, val) {
|
||||
var field = this.getForm().findField(fieldId);
|
||||
if (field) {
|
||||
|
||||
field.setValue(val);
|
||||
if (this.trackResetOnLoad) {
|
||||
field.resetOriginalValue();
|
||||
}
|
||||
return true;
|
||||
|
||||
} else {
|
||||
|
||||
Ext.log('No mapping for field ' + fieldId);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
setValues: function(record, parentFieldName) {
|
||||
var me = this;
|
||||
var form = me.getForm();
|
||||
|
||||
function setVal(fieldId, val) {
|
||||
if (parentFieldName) {
|
||||
fieldId = parentFieldName + '.' + fieldId;
|
||||
}
|
||||
var field = form.findField(fieldId);
|
||||
if (field) {
|
||||
field.setValue(val);
|
||||
if (me.trackResetOnLoad) {
|
||||
field.resetOriginalValue();
|
||||
}
|
||||
} else if(Ext.isObject(val)) {
|
||||
me.setValues(val, fieldId);
|
||||
} else {
|
||||
Ext.log('No mapping for field ' + fieldId);
|
||||
}
|
||||
}
|
||||
|
||||
if (record.getData) {
|
||||
Ext.iterate(record.getData(), setVal);
|
||||
|
||||
if (record.getAssociatedData && record.getAssociatedData()) {
|
||||
for(k in record.getAssociatedData()) {
|
||||
//The property name has a lower first character, but the method format is getCamelCase()
|
||||
var propName = k[0].toUpperCase() + k.substring(1);
|
||||
if (record['get' + propName] != null)
|
||||
me.setValues(record['get' + propName](), parentFieldName + '.' + propName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
updateRecord: function(record) {
|
||||
var values = this.getFieldValues(),
|
||||
name,
|
||||
obj = {};
|
||||
|
||||
alert( "Servus updateRecord" );
|
||||
|
||||
function populateObj(record, values) {
|
||||
var obj = {},
|
||||
name;
|
||||
|
||||
record.fields.each(function(field) {
|
||||
name = field.name;
|
||||
if (field.model) {
|
||||
var nestedValues = {};
|
||||
var hasValues = false;
|
||||
for(var v in values) {
|
||||
if (v.indexOf('.') > 0) {
|
||||
var parent = v.substr(0, v.indexOf('.'));
|
||||
if (parent == field.name) {
|
||||
var key = v.substr(v.indexOf('.') + 1);
|
||||
nestedValues[key] = values[v];
|
||||
hasValues = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (hasValues) {
|
||||
obj[name] = populateObj(Ext.create(field.model), nestedValues);
|
||||
}
|
||||
} else if (name in values) {
|
||||
obj[name] = values[name];
|
||||
}
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj = populateObj(record, values);
|
||||
|
||||
record.beginEdit();
|
||||
record.set(obj);
|
||||
record.endEdit();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
setEncryptionModule: function(value) {
|
||||
value = value || '';
|
||||
var form = this.getForm();
|
||||
var combo = form.findField('Task.EncryptionModule');
|
||||
var store = combo.getStore();
|
||||
|
||||
//Make sure we have the empty element
|
||||
if (store.findRecord("id", -1) == null) {
|
||||
store.add({encryptionMethodName: 'No encryption', encryptionMethodIdentifier: '', id: -1});
|
||||
}
|
||||
|
||||
this.setFieldValue('Task.EncryptionModule', value);
|
||||
},
|
||||
|
||||
setEncryptionPassword: function(value) {
|
||||
value = value || '';
|
||||
|
||||
this.setFieldValue('sEncryptionPassword', value);
|
||||
this.setFieldValue('sEncryptionPasswordCheck', value);
|
||||
},
|
||||
|
||||
setBackupRepeatAndWhen: function(valueRepeat, valueWhen) {
|
||||
valueWhen = valueWhen || '';
|
||||
valueRepeat = valueRepeat || '';
|
||||
|
||||
var form = this.getForm();
|
||||
var chk = form.findField('chkRunRegular');
|
||||
|
||||
if (valueWhen == '' || valueRepeat == '') {
|
||||
chk.setValue(false);
|
||||
} else {
|
||||
chk.setValue(true);
|
||||
|
||||
var date = Duplicati.Utility.parseJsonDate(valueWhen);
|
||||
|
||||
this.setFieldValue('Schedule.Repeat.Number', parseInt(valueRepeat.substr(0, valueRepeat.length - 1)));
|
||||
this.setFieldValue('Schedule.Repeat.Suffix', valueRepeat.substr(valueRepeat.length - 1, 1));
|
||||
|
||||
this.setFieldValue('Schedule.When.Time', date);
|
||||
this.setFieldValue('Schedule.When.Date', date);
|
||||
}
|
||||
},
|
||||
|
||||
setAllowedWeekdays: function(value) {
|
||||
var form = this.getForm();
|
||||
|
||||
var t = form.findField('Schedule.AllowedWeekdays');
|
||||
},
|
||||
|
||||
setVolumeSize: function(value) {
|
||||
value = value || '';
|
||||
|
||||
var number = parseInt(value);
|
||||
var suffix = 'b';
|
||||
|
||||
function isNumber(n) {
|
||||
switch(n) {
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (value != '' && value.length >= 2) {
|
||||
|
||||
var i = value.length - 1;
|
||||
while(i >= 0 && !isNumber(value[i]))
|
||||
i--;
|
||||
|
||||
i++;
|
||||
|
||||
suffix = value.substr(i, value.length - i).toLowerCase();
|
||||
number = parseInt(value.substr(0, i));
|
||||
}
|
||||
|
||||
this.setFieldValue('Task.Extensions.VolumeSize.Number', number);
|
||||
this.setFieldValue('Task.Extensions.VolumeSize.Suffix', suffix);
|
||||
},
|
||||
|
||||
setSourcePaths: function(value) {
|
||||
var store = Ext.getCmp('Locations').getStore();
|
||||
store.removeAll();
|
||||
|
||||
if (value != null && value != '') {
|
||||
var pathSep = Duplicati.service.serverdata['system-info'].PathSeparator;
|
||||
|
||||
Ext.Array.each(value.split(pathSep), function(rec){
|
||||
Ext.getCmp('Locations').getStore().add(
|
||||
new Duplicati.model.TaskItems.BackupLocation ( {
|
||||
Location: rec,
|
||||
TotalSize:0
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
loadRecord: function(loaderObject) {
|
||||
var schedule = loaderObject.getSchedule();
|
||||
var task = loaderObject.getTask();
|
||||
|
||||
this.setValues(schedule, "Schedule")
|
||||
this.setValues(task, "Task")
|
||||
|
||||
this.setEncryptionModule(task.getData().EncryptionModule);
|
||||
this.setEncryptionPassword(task.getData().Encryptionkey);
|
||||
|
||||
this.setBackupRepeatAndWhen(schedule.getData().Repeat, schedule.getData().When);
|
||||
this.setAllowedWeekdays(schedule.getData().AllowedWeekdays);
|
||||
this.setVolumeSize(task.getExtensions().getData().VolumeSize);
|
||||
this.setSourcePaths(task.getData().SourcePath);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
@@ -1,107 +0,0 @@
|
||||
Ext.define('Duplicati.view.schedule.List' ,{
|
||||
extend: 'Ext.grid.Panel',
|
||||
alias : 'widget.schedulelist',
|
||||
|
||||
title : 'All Schedules',
|
||||
header: false,
|
||||
border: 0,
|
||||
id: 'status-window-schedule-list',
|
||||
|
||||
hideHeaders: true,
|
||||
selType: 'rowmodel',
|
||||
|
||||
store: 'Schedules',
|
||||
|
||||
elementTemplateStr:
|
||||
'<span class="backup-schedule-name" title="Click for actions - {Name}" onclick="Duplicati.ListUtility.showContextMenu({ID}, this);"><span class="backup-schedule-name-inner">{Name}<div class="backup-schedule-drop-arrow"></div></span></span>' +
|
||||
'<span class="backup-schedule-details">' +
|
||||
'<span class="backup-schedule-details-inner">' +
|
||||
/* The template for this became un-readable, so now it is a function */
|
||||
'{[Duplicati.ListUtility.formatBackupScheduleDetails(values)]}' +
|
||||
'</span>' +
|
||||
'<tpl if="Path != null && Path != \'\'">' +
|
||||
'<div class="backup-schedule-tags">' +
|
||||
'<div class="schedule-tags-icon"></div><span>Labels: <span>{Path}</span></span>' +
|
||||
'</div>' +
|
||||
'</tpl>' +
|
||||
'</span>',
|
||||
|
||||
|
||||
elementTemplate: null,
|
||||
|
||||
elementContextMenu: Ext.create('Ext.menu.Menu', {
|
||||
floating: true,
|
||||
items: [{
|
||||
text: '*** Show files'
|
||||
},{
|
||||
text: 'Run now',
|
||||
handler: function() {
|
||||
Ext.log('Running backup ' + Duplicati.ListUtility.currentScheduleId);
|
||||
Duplicati.service.runBackup(Duplicati.ListUtility.currentScheduleId, false);
|
||||
}
|
||||
},{
|
||||
text: 'Run a full backup now',
|
||||
handler: function() {
|
||||
Ext.log('Running full backup ' + Duplicati.ListUtility.currentScheduleId);
|
||||
Duplicati.service.runBackup(Duplicati.ListUtility.currentScheduleId, true);
|
||||
}
|
||||
},{
|
||||
text: '*** Cancel'
|
||||
},{
|
||||
xtype: 'menuseparator'
|
||||
},{
|
||||
text: 'Edit settings',
|
||||
handler: function() {
|
||||
Ext.create('Ext.window.Window', {
|
||||
layout: 'fit',
|
||||
bodyBorder: false,
|
||||
title: 'Edit backup',
|
||||
items: [
|
||||
Ext.create('Duplicati.view.BackupConfig', {
|
||||
scheduleId: Duplicati.ListUtility.currentScheduleId
|
||||
})
|
||||
]
|
||||
}).show();
|
||||
}
|
||||
},{
|
||||
text: '*** Delete backup'
|
||||
},{
|
||||
xtype: 'menuseparator'
|
||||
},{
|
||||
text: '*** Create a copy'
|
||||
}]
|
||||
}),
|
||||
|
||||
columns:
|
||||
[
|
||||
{header: 'ScheduleTime', dataIndex: 'NextScheduledTime', flex: 0.8, align: 'center', renderer:
|
||||
function(value, metaData, record) {
|
||||
var schedulePos = Duplicati.service.getSchedulePosition(record.get("ID"));
|
||||
if (schedulePos == 0) {
|
||||
return '<div class="schedule-activity-text schedule-running-info"><div>In progress</div><div><a href="#" onclick="Duplicati.service.cancelJob('+ record.get("ID") +')">Cancel</a></div></div><div class="info-icon schedule-active"></div>';
|
||||
} else if (schedulePos > 0) {
|
||||
return '<div class="schedule-activity-text schedule-queue-info">No. ' + schedulePos + ' in queue</div><div class="info-icon schedule-in-queue"></div>';
|
||||
} else if (value != null) {
|
||||
var scdate = Duplicati.Utility.parseJsonDate(value);
|
||||
if (scdate > new Date().getTime())
|
||||
return '<div class="schedule-activity-text schedule-time-info" title="' + scdate.toLocaleString() + '"><span class="marker-time-ago" title="' + scdate.toUTCString() + '"></span>' + Duplicati.Utility.timeAgo(scdate) + '</div><div class="info-icon schedule-waiting"></div>';
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
},
|
||||
{header: 'Name', dataIndex: 'Name', flex: 1, align: 'center', renderer:
|
||||
function(value, metaData, record) {
|
||||
return this.elementTemplate.apply(record.data);
|
||||
}
|
||||
},
|
||||
{header: 'Path', dataIndex: 'ID', flex: 0.8, align: 'left', renderer:
|
||||
function(value) { return value; }
|
||||
}
|
||||
],
|
||||
|
||||
initComponent: function() {
|
||||
this.elementTemplate = new Ext.XTemplate(this.elementTemplateStr, {formatSizeString: function(v) { return Duplicati.Utility.formatSizeString(v); } });
|
||||
this.callParent(arguments);
|
||||
}
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
Ext.define('Duplicati.view.wndAddFilter', {
|
||||
extend: 'Ext.window.Window',
|
||||
|
||||
height: 342,
|
||||
width: 550,
|
||||
title: 'Add Filter',
|
||||
|
||||
initComponent: function() {
|
||||
var me = this;
|
||||
|
||||
me.callParent(arguments);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
Ext.define('Duplicati.view.wndAddLocation', {
|
||||
extend: 'Ext.window.Window',
|
||||
|
||||
height: 539,
|
||||
id: 'winAddLocation',
|
||||
width: 349,
|
||||
title: 'Add Location',
|
||||
modal: true,
|
||||
|
||||
initComponent: function() {
|
||||
var me = this;
|
||||
|
||||
Ext.applyIf(me, {
|
||||
items: [
|
||||
{
|
||||
xtype: 'treepanel',
|
||||
height: 467,
|
||||
id: 'treeLocation',
|
||||
iconCls: '',
|
||||
title: 'My Computer',
|
||||
hideHeaders: true,
|
||||
store: 'MyComputerTreeStore',
|
||||
folderSort: true,
|
||||
rootVisible: false,
|
||||
singleExpand: true,
|
||||
useArrows: true,
|
||||
viewConfig: {
|
||||
autoScroll: true,
|
||||
blockRefresh: true,
|
||||
loadingText: 'Retrieving File Structure',
|
||||
multiSelect: true,
|
||||
simpleSelect: true
|
||||
}
|
||||
}
|
||||
],
|
||||
dockedItems: [
|
||||
{
|
||||
xtype: 'toolbar',
|
||||
dock: 'bottom',
|
||||
layout: {
|
||||
pack: 'end',
|
||||
type: 'hbox'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'buttongroup',
|
||||
columns: 2,
|
||||
layout: {
|
||||
columns: 2,
|
||||
type: 'table'
|
||||
},
|
||||
items: [
|
||||
{
|
||||
xtype: 'button',
|
||||
id: 'btnAddLocations',
|
||||
maintainFlex: false,
|
||||
scale: 'medium',
|
||||
text: 'Add selected Nodes'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
me.callParent(arguments);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
Ext.define('Duplicati.view.wndCreateConnection', {
|
||||
extend: 'Ext.window.Window',
|
||||
|
||||
height: 342,
|
||||
id: 'wndCreateConnection',
|
||||
width: 418,
|
||||
title: 'Create Connection',
|
||||
titleCollapse: false,
|
||||
|
||||
initComponent: function() {
|
||||
var me = this;
|
||||
|
||||
me.callParent(arguments);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1,162 +0,0 @@
|
||||
function parseBool(v) {
|
||||
if (v == null)
|
||||
return false;
|
||||
if (typeof(v) == 'boolean')
|
||||
return v;
|
||||
v = (v + '').toLowerCase();
|
||||
return (v == 'true' || v == 'on' || v == '1');
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// This helps in a debug setup
|
||||
var extdefined = false;
|
||||
try { extdefined = Ext !== undefined; } catch (e) {}
|
||||
if (!extdefined) { alert('ExtJS failed to load!\n If this is a debug build,\nplease download and extract:\n\nExtJS 4.2.0\n\ninto the folder:\nDuplicati/Server/webroot/extjs-4.2.0'); }
|
||||
delete extdefined;
|
||||
}
|
||||
|
||||
Ext.application({
|
||||
requires: [
|
||||
'Ext.container.Viewport',
|
||||
'Ext.layout.container.Border',
|
||||
'Duplicati.Service',
|
||||
'Duplicati.Utility',
|
||||
'Duplicati.ListUtility',
|
||||
'Duplicati.view.AboutWindow',
|
||||
'Duplicati.view.LostConnectionWindow',
|
||||
|
||||
// Override for handling nested Model 2 Form
|
||||
'Duplicati.view.override.BackupConfig',
|
||||
'Duplicati.model.override.BackupJob'
|
||||
],
|
||||
name: 'Duplicati',
|
||||
appFolder: 'Duplicati',
|
||||
|
||||
controllers: [
|
||||
'Schedules',
|
||||
'StatuswindowHeader',
|
||||
'StatuswindowFooter',
|
||||
'BackupConfig'
|
||||
],
|
||||
|
||||
stores: [
|
||||
'LabelStore',
|
||||
'EncryptionMethodStore',
|
||||
'AdditionalPathStore',
|
||||
'MyComputerTreeStore',
|
||||
'BackupJob',
|
||||
'DefaultTimeRangesStore',
|
||||
'NewBackupChainWhenStore',
|
||||
'DeleteOldChainsWhenStore',
|
||||
'DefaultSizeRangeStore'
|
||||
],
|
||||
|
||||
views: [
|
||||
'BackupConfig',
|
||||
'wndAddLocation',
|
||||
'wndAddFilter',
|
||||
'wndCreateConnection'
|
||||
],
|
||||
|
||||
models: [
|
||||
'BackupJob',
|
||||
'Schedule',
|
||||
'Task',
|
||||
'TaskItems.BackupLocation',
|
||||
'TaskItems.CompressionSettings',
|
||||
'TaskItems.EncryptionSettings',
|
||||
'TaskItems.Overrides',
|
||||
'TaskItems.Extensions',
|
||||
'TaskItems.BackendSettings',
|
||||
'ScheduleItems.Metadata'
|
||||
],
|
||||
|
||||
//This object encapsulates all access to the web service
|
||||
service: null,
|
||||
|
||||
launch: function() {
|
||||
|
||||
Ext.log = function(msg) {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
//Pretend the app fires events too
|
||||
this.addEvents('current-state-updated', 'count-down-pause-timer');
|
||||
|
||||
//Make a service object
|
||||
this.service = new Duplicati.Service();
|
||||
//Make the service globally accesible
|
||||
Duplicati.service = this.service;
|
||||
Duplicati.instance = this;
|
||||
Duplicati.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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
Duplicati.view.LostConnectionWindow.show();
|
||||
},
|
||||
|
||||
'reconnected': function() {
|
||||
Duplicati.view.LostConnectionWindow.hide();
|
||||
},
|
||||
|
||||
'lost-connection-retry': function() {
|
||||
Duplicati.view.LostConnectionWindow.setStatusAsRunning();
|
||||
},
|
||||
|
||||
'lost-connection-retry-delay': function(seconds) {
|
||||
Duplicati.view.LostConnectionWindow.setStatusAsWaiting(seconds);
|
||||
},
|
||||
|
||||
'count-down-pause-timer': function(e) {
|
||||
Duplicati.instance.fireEvent('count-down-pause-timer', e);
|
||||
},
|
||||
scope: this
|
||||
});
|
||||
|
||||
//Fetch the current status
|
||||
this.service.updateCurrentState();
|
||||
|
||||
Ext.create('Ext.container.Viewport', {
|
||||
layout: 'border',
|
||||
items: [{
|
||||
region: 'center',
|
||||
xtype: 'schedulelist'
|
||||
},{
|
||||
region: 'north',
|
||||
xtype: 'statuswindowheader'
|
||||
},{
|
||||
region: 'south',
|
||||
xtype: 'statuswindowfooter'
|
||||
}],
|
||||
|
||||
renderTo: Ext.getBody()
|
||||
|
||||
});
|
||||
}
|
||||
});
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 945 B |
|
Before Width: | Height: | Size: 988 B |
|
Before Width: | Height: | Size: 978 B |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 717 B |
|
Before Width: | Height: | Size: 943 B |
|
Before Width: | Height: | Size: 882 B |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
@@ -1,22 +0,0 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9" />
|
||||
<title>Duplicati Backup</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="http://cdn.sencha.io/ext-4.2.0-gpl/resources/css/ext-all.css" />
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/common.css" />
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/status-window.css" />
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/customized.css" />
|
||||
|
||||
<script type="text/javascript" src="http://cdn.sencha.io/ext-4.2.0-gpl/ext-all.js"></script>
|
||||
<script type="text/javascript" src="app.js"></script>
|
||||
<script type="text/javascript" src="scripts/customized.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,217 +0,0 @@
|
||||
.footer-panel .x-panel-body
|
||||
{
|
||||
background-color: #ccffcc;
|
||||
}
|
||||
|
||||
.header-panel .x-panel-body
|
||||
{
|
||||
background-color: #ccffcc;
|
||||
}
|
||||
|
||||
.main-button .x-btn-arrow, .add-new-backup-button .x-btn-center, .pause-backup-button .x-btn-center
|
||||
{
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.main-button .x-btn-inner, .add-new-backup-button .x-btn-inner, .pause-backup-button .x-btn-inner
|
||||
{
|
||||
font-size: 2em;
|
||||
height: 20px;
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
.main-button .x-btn-icon, .add-new-backup-button .x-btn-icon, .pause-backup-button .x-btn-icon
|
||||
{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
top: 4px;
|
||||
}
|
||||
|
||||
.main-button .x-btn-icon
|
||||
{
|
||||
background-image: url(../icons/main-logo.png);
|
||||
}
|
||||
|
||||
.add-new-backup-button .x-btn-icon
|
||||
{
|
||||
background-image: url(../icons/add-backup.png);
|
||||
}
|
||||
|
||||
.pause-backup-button .backups-paused
|
||||
{
|
||||
background-image: url(../icons/status-play.png);
|
||||
}
|
||||
|
||||
.pause-backup-button .backups-running
|
||||
{
|
||||
background-image: url(../icons/status-pause.png);
|
||||
}
|
||||
|
||||
.throttle-label
|
||||
{
|
||||
font-size: 2em;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.low-storage-space
|
||||
{
|
||||
color: red;
|
||||
}
|
||||
|
||||
.important-information
|
||||
{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.info-icon
|
||||
{
|
||||
padding: 8px;
|
||||
margin-right: 4px;
|
||||
margin-top: 40px;
|
||||
background-repeat: no-repeat;
|
||||
background-position-y: 4px;
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.schedule-activity-text
|
||||
{
|
||||
margin-top: 40px;
|
||||
margin-right: 20px;
|
||||
display: inline;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.schedule-waiting
|
||||
{
|
||||
background: url(../icons/schedule-waiting.png)
|
||||
|
||||
}
|
||||
|
||||
.schedule-active
|
||||
{
|
||||
background: url(../icons/schedule-active.png)
|
||||
|
||||
}
|
||||
|
||||
.schedule-in-queue
|
||||
{
|
||||
background: url(../icons/schedule-in-queue.png)
|
||||
|
||||
}
|
||||
|
||||
.backup-schedule-tags
|
||||
{
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.marker-time-ago
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.backup-schedule-detail-source-size,
|
||||
.backup-schedule-detail-backup-size,
|
||||
.backup-schedule-detail-free-size,
|
||||
.backup-schedule-detail-change-count,
|
||||
.backup-schedule-detail-last-backup,
|
||||
.backup-schedule-tags
|
||||
{
|
||||
display: block;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.backup-schedule-details-inner
|
||||
{
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.backup-schedule-name
|
||||
{
|
||||
display: block;
|
||||
border: 1px lightgrey solid;
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
margin-top: 10px;
|
||||
background-color: #aaeeaa;
|
||||
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
-khtml-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.backup-schedule-drop-arrow
|
||||
{
|
||||
background-image: url(../extjs-4.1.0/resources/themes/images/default/button/arrow.gif);
|
||||
background-repeat: no-repeat;
|
||||
width: 16px;
|
||||
height: 10px;
|
||||
background-position-x: 6px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.backup-schedule-name-inner
|
||||
{
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
font-size: 1.5em;
|
||||
overflow: hidden;
|
||||
white-space: wrap;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.backup-schedule-details
|
||||
{
|
||||
display: block;
|
||||
border: 1px lightgrey solid;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 8px;
|
||||
padding-left: 4px;
|
||||
padding-right: 4px;
|
||||
overflow: auto;
|
||||
white-space: normal;
|
||||
margin-bottom: 10px;
|
||||
background-color: #ccffcc;
|
||||
margin-top: -2px;
|
||||
font-size: 1.2em;
|
||||
|
||||
-moz-border-radius: 6px;
|
||||
-webkit-border-radius: 6px;
|
||||
-khtml-border-radius: 6px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#status-window-header-pause-duration-menu
|
||||
{
|
||||
color: #222;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#status-window-header-pause-duration-menu div
|
||||
{
|
||||
padding-left: 32px;
|
||||
border: 0px none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#status-window-header-pause-duration-picker
|
||||
{
|
||||
margin-left: 6px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
#status-window-header-pause-duration-picker div
|
||||
{
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<form action="control.cgi" method="post">
|
||||
<input type="text" name="action" value="add-schedule" />
|
||||
<br/>
|
||||
<textarea name="schedule" cols=50 rows=12>{
|
||||
"NextScheduledTime": "\/Date(1343991600000+0200)\/",
|
||||
"AllowedWeekdays": null,
|
||||
"ID": 1,
|
||||
"Name": "Created from FORM",
|
||||
"Path": "",
|
||||
"When": "\/Date(1338289200000+0200)\/",
|
||||
"Repeat": "1D",
|
||||
"Weekdays": ""
|
||||
}</textarea>
|
||||
<br/>
|
||||
|
||||
<textarea name="task" cols=50 rows=20>{
|
||||
"ID": 1,
|
||||
"Service": "file",
|
||||
"Encryptionkey": "",
|
||||
"SourcePath": "C:\\boot\\",
|
||||
"ScheduleID": 1,
|
||||
"KeepFull": 4,
|
||||
"KeepTime": "",
|
||||
"FullAfter": "1M",
|
||||
"IncludeSetup": false,
|
||||
"EncryptionModule": "aes",
|
||||
"CompressionModule": "",
|
||||
"BackendSettings": {
|
||||
"UI: Checked empty": "true",
|
||||
"Destination": "C:\\testbackup_forms\\backup1_create_by_form",
|
||||
"Username": "",
|
||||
"Password": ""
|
||||
},
|
||||
"Extensions": {
|
||||
"Upload Bandwidth": "",
|
||||
"Download Bandwidth": "",
|
||||
"Max Upload Size": "",
|
||||
"Volume Size": "25MB",
|
||||
"Thread Priority": "",
|
||||
"Async Transfer": "True",
|
||||
"Ignore Timestamps": "False",
|
||||
"File Size Limit": "",
|
||||
"Disable AES fallback encryption": "True",
|
||||
"Select Files - Version": "2",
|
||||
"Select Files - Use Simple Mode": "False",
|
||||
"Select Files - Include Documents": "True",
|
||||
"Select Files - Include Desktop": "True",
|
||||
"Select Files - Include Music": "False",
|
||||
"Select Files - Include Images": "True",
|
||||
"Select Files - Include AppData": "False",
|
||||
"Select When - Warned No Schedule": "False",
|
||||
"Select When - Warned Too Many Incrementals": "False",
|
||||
"Select When - Warned No Incrementals": "False",
|
||||
"Password Settings - Warned No Password": "True",
|
||||
"Cleanup Settings - Warned No Cleanup": "False"
|
||||
},
|
||||
"Overrides": {},
|
||||
"EncryptionSettings": {},
|
||||
"CompressionSettings": {},
|
||||
|
||||
"Filter": ""
|
||||
}</textarea>
|
||||
<br/>
|
||||
|
||||
<input type="submit" value="Submit!" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,72 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<form action="control.cgi" method="post">
|
||||
<input type="text" name="action" value="update-schedule" />
|
||||
<input type="text" name="id" value="3" />
|
||||
<br/>
|
||||
<textarea name="schedule" cols=50 rows=12>{
|
||||
"NextScheduledTime": "\/Date(1343991600000+0200)\/",
|
||||
"AllowedWeekdays": null,
|
||||
"ID": 1,
|
||||
"Name": "Updated from FORM",
|
||||
"Path": "",
|
||||
"When": "\/Date(1338289200000+0200)\/",
|
||||
"Repeat": "1D",
|
||||
"Weekdays": ""
|
||||
}</textarea>
|
||||
<br/>
|
||||
|
||||
<textarea name="task" cols=50 rows=20>{
|
||||
"ID": 1,
|
||||
"Service": "file",
|
||||
"Encryptionkey": "",
|
||||
"SourcePath": "C:\\boot\\",
|
||||
"ScheduleID": 1,
|
||||
"KeepFull": 4,
|
||||
"KeepTime": "",
|
||||
"FullAfter": "1M",
|
||||
"IncludeSetup": false,
|
||||
"EncryptionModule": "aes",
|
||||
"CompressionModule": "",
|
||||
"BackendSettings": {
|
||||
"UI: Checked empty": "true",
|
||||
"Destination": "C:\\testbackup_forms\\backup1_update_by_form",
|
||||
"Username": "",
|
||||
"Password": ""
|
||||
},
|
||||
"Extensions": {
|
||||
"Upload Bandwidth": "",
|
||||
"Download Bandwidth": "",
|
||||
"Max Upload Size": "",
|
||||
"Volume Size": "50MB",
|
||||
"Thread Priority": "",
|
||||
"Async Transfer": "True",
|
||||
"Ignore Timestamps": "False",
|
||||
"File Size Limit": "",
|
||||
"Disable AES fallback encryption": "True",
|
||||
"Select Files - Version": "2",
|
||||
"Select Files - Use Simple Mode": "False",
|
||||
"Select Files - Include Documents": "True",
|
||||
"Select Files - Include Desktop": "True",
|
||||
"Select Files - Include Music": "False",
|
||||
"Select Files - Include Images": "True",
|
||||
"Select Files - Include AppData": "False",
|
||||
"Select When - Warned No Schedule": "False",
|
||||
"Select When - Warned Too Many Incrementals": "False",
|
||||
"Select When - Warned No Incrementals": "False",
|
||||
"Password Settings - Warned No Password": "True",
|
||||
"Cleanup Settings - Warned No Cleanup": "False"
|
||||
},
|
||||
"Overrides": {},
|
||||
"EncryptionSettings": {},
|
||||
"CompressionSettings": {},
|
||||
|
||||
"Filter": ""
|
||||
}</textarea>
|
||||
<br/>
|
||||
|
||||
<input type="submit" value="Submit!" />
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||