Added new web-stuff

git-svn-id: https://duplicati.googlecode.com/svn/trunk@1322 59da171f-624f-0410-aa54-27559c288bec
This commit is contained in:
kenneth@hexad.dk
2012-05-31 15:23:16 +00:00
parent 09096b06c5
commit f4ad2e0840
27 changed files with 1217 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
Ext.application({
requires: [
'Ext.container.Viewport',
'Ext.layout.container.Border',
'BackupApp.Service',
'BackupApp.Utility',
'BackupApp.ListUtility',
'BackupApp.view.AboutWindow'
],
name: 'BackupApp',
appFolder: 'backup-app',
controllers: [
'Schedules',
'StatuswindowHeader',
'StatuswindowFooter'
],
//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 BackupApp.Service();
//Make the service globally accesible
BackupApp.service = this.service;
BackupApp.instance = this;
BackupApp.utility = this.utility;
//Pretend the app can fire the update event, this helps with initialization in controllers
this.service.on('current-state-updated', function(e) {
BackupApp.instance.fireEvent('current-state-updated', e)
});
this.service.on('count-down-pause-timer', function(e) {
BackupApp.instance.fireEvent('count-down-pause-timer', e)
});
//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'
}]
});
}
});
@@ -0,0 +1,84 @@
Ext.define('BackupApp.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 BackupApp.Utility.formatSizeString(size); },
timeAgo: function(date) { return BackupApp.Utility.timeAgo(date); },
formatBackupScheduleDetails: function(values)
{
var metadata = '';
if (values.MetadataLookup != null)
{
var metadataItems = [];
var mt = values.MetadataLookup;
if (mt['source-file-size'] != null)
metadataItems.push('<span class="backup-schedule-detail-source-size">Source files: ' + this.formatSizeString(mt['source-file-size']) + '</span>');
if (mt['total-backup-size'] != null && parseInt(mt['total-backup-size']) > 0)
metadataItems.push('<span class="backup-schedule-detail-backup-size">Backup size: ' + this.formatSizeString(mt['total-backup-size']) + '</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="BackupApp.service.runBackup(' + values.ID + ', false)">run a backup</a> to obtain data</span>'
return metadata;
}
}
})
@@ -0,0 +1,228 @@
Ext.define('BackupApp.Service', {
mixins: {
observable: 'Ext.util.Observable'
},
controlUrl: '/control.cgi',
pauseCountDownTimer: null,
updateRequestParams: {
action: 'get-current-state',
longpoll: true,
lasteventid: '-1',
duration: '5m',
},
updateRequestRunning: false,
//Bugfix for extjs
hasListeners: {},
//Cache for server responses
serverdata: {
'current-state': null,
'installed-backends': null,
'installed-compression-modules': null,
'installed-encryption-modules': null,
'installed-generic-modules': null
},
constructor: function(config) {
this.addEvents('current-state-updated', 'count-down-pause-timer');
},
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;
Ext.Ajax.request({
url: this.controlUrl,
params: params,
timeout: timeout,
scope: this,
success: function(response) {
var res = eval('(' + response.responseText + ')');
if (res != null)
{
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.startPauseCountDownTimer();
}
},
failure: function(response, opts) {
//TODO: Grey out or similar
//Register next request
if (!force)
{
Ext.log('Failure in longpoll: ' + response.status + ', timeout: ' + response.timedout);
this.updateRequestRunning = false;
this.updateCurrentState();
}
}
});
},
startPauseCountDownTimer: function() {
if (this.pauseCountDownTimer == null)
this.pauseCountDownTimer = window.setTimeout('BackupApp.service.onPauseCountDownTimer()', 500);
},
onPauseCountDownTimer: function() {
this.pauseCountDownTimer = null;
var pauseEnd = this.getExpectedPauseEnd();
if (pauseEnd == null) {
this.fireEvent('count-down-pause-timer', 0);
} else {
this.fireEvent('count-down-pause-timer', parseInt((pauseEnd.getTime() - new Date().getTime()) / 1000));
this.startPauseCountDownTimer();
}
},
getExpectedPauseEnd: function() {
var pauseEnd = this.serverdata['current-state'].EstimatedPauseEnd;
if (pauseEnd != null) {
pauseEnd = BackupApp.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) {
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) {
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']);
}
},
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) {
}
});
}
});
@@ -0,0 +1,147 @@
Ext.define('BackupApp.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('BackupApp.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;
}
}
});
@@ -0,0 +1,36 @@
Ext.define('BackupApp.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');
}
});
@@ -0,0 +1,16 @@
Ext.define('BackupApp.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');
}
});
@@ -0,0 +1,60 @@
Ext.define('BackupApp.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 = BackupApp.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 = BackupApp.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');
}
});
@@ -0,0 +1,4 @@
Ext.define('BackupApp.model.Schedule', {
extend: 'Ext.data.Model',
fields: ['ID', 'Name', 'Path', 'MetadataLookup', 'NextScheduledTime', 'When', 'ExistsInDb', 'AllowedWeekdays', 'Repeat', 'Weekdays']
});
@@ -0,0 +1,95 @@
Ext.define('BackupApp.store.Schedules', {
extend: 'Ext.data.Store',
model: 'BackupApp.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 = BackupApp.service.getSchedulePosition(a.get("ID"));
var schedulePosB = BackupApp.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 = BackupApp.Utility.parseJsonDate(a.get('NextScheduledTime'));
var nextTimeB = BackupApp.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 = BackupApp.Utility.parseJsonDate(a.data.MetadataLookup['last-backup-completed-time']);
var lastTimeB = BackupApp.Utility.parseJsonDate(b.data.MetadataLookup['last-backup-completed-time']);
if (lastTimeA == null)
lastTimeA = BackupApp.Utility.parseJsonDate(a.data.MetadataLookup['last-backup-date']);
if (lastTimeB == null)
lastTimeB = BackupApp.Utility.parseJsonDate(b.data.MetadataLookup['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'
}
}
});
@@ -0,0 +1,32 @@
Ext.define('BackupApp.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() {
BackupApp.view.AboutWindow.dialogWindow.hide();
}
}]
});
this.dialogWindow.show();
}
}
});
@@ -0,0 +1,37 @@
Ext.define('BackupApp.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&currency_code=EUR&bn=PP%2dDonationsBF&charset=UTF%2d8') },
margin: 10
}],
initComponent: function() {
this.callParent(arguments);
}
});
@@ -0,0 +1,101 @@
Ext.define('BackupApp.view.StatuswindowHeader' ,{
extend: 'Ext.panel.Panel',
alias : 'widget.statuswindowheader',
title : 'Main App controls',
header: false,
border: 0,
layout: 'column',
cls: 'header-panel',
items: [{
xtype: 'splitbutton',
cls: 'main-button',
text: 'Duplicati',
id: 'status-window-header-main-button',
margin: 10,
handler: function() { this.showMenu(); },
menu: [{
id: 'status-window-header-pause-menu',
text: 'Pause Duplicati',
handler: function() { BackupApp.service.togglePause(); }
},{
text: 'Pause for x minutes',
menu: [{
text: 'Pause for 5 minutes',
handler: function() { BackupApp.service.pause('5m'); }
},{
text: 'Pause for 15 minutes',
handler: function() { BackupApp.service.pause('15m'); }
},{
text: 'Pause for 30 minutes',
handler: function() { BackupApp.service.pause('30m'); }
},{
text: 'Pause for 60 minutes',
handler: function() { BackupApp.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() { BackupApp.view.AboutWindow.show(); }
}]
},{
xtype: 'label',
html: '&nbsp;',
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',
margin: 10
},{
xtype: 'button',
id: 'status-window-header-pause-button',
cls: 'pause-backup-button',
iconCls: 'backups-paused',
text: 'Pause',
margin: 10,
handler: function() { BackupApp.service.togglePause(); }
}],
initComponent: function() {
this.callParent(arguments);
}
});
@@ -0,0 +1,95 @@
Ext.define('BackupApp.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="BackupApp.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 */
'{[BackupApp.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 ' + BackupApp.ListUtility.currentScheduleId);
BackupApp.service.runBackup(BackupApp.ListUtility.currentScheduleId, false);
}
},{
text: 'Run a full backup now',
handler: function() {
Ext.log('Running full backup ' + BackupApp.ListUtility.currentScheduleId);
BackupApp.service.runBackup(BackupApp.ListUtility.currentScheduleId, true);
}
},{
text: '*** Cancel'
},{
xtype: 'menuseparator'
},{
text: '*** Edit settings'
},{
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 = BackupApp.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="BackupApp.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 = BackupApp.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>' + BackupApp.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 BackupApp.Utility.formatSizeString(v); } });
this.callParent(arguments);
}
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 717 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 943 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

@@ -0,0 +1 @@
/* Placeholder for customization */
@@ -0,0 +1,21 @@
<!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">
<title>Status Window</title>
<link rel="stylesheet" type="text/css" href="extjs-4.1.0/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="extjs-4.1.0/ext-debug.js"></script>
<script type="text/javascript" src="app.js"></script>
<script type="text/javascript" src="scripts/customized.js"></script>
</head>
<body>
</body>
</html>
@@ -0,0 +1 @@
/* Placeholder for customization */
@@ -0,0 +1 @@
/* Placeholder for customization */
@@ -0,0 +1,193 @@
.footer-panel .x-panel-body
{
background-color: #ccffcc;
}
.header-panel .x-panel-body
{
background-color: #ccffcc;
}
.main-button .x-btn-split, .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;
}