2017-02-07 21:51:14 +01:00
backupApp . controller ( 'RestoreController' , function ( $rootScope , $scope , $routeParams , $location , AppService , AppUtils , SystemInfo , ServerStatus , DialogService , BackupList , gettextCatalog ) {
2015-09-25 09:45:41 +02:00
2015-11-16 23:45:03 +01:00
$scope . SystemInfo = SystemInfo . watch ( $scope );
2015-09-25 09:45:41 +02:00
$scope . AppUtils = AppUtils ;
2015-10-04 17:13:30 +02:00
$scope . restore_step = 0 ;
$scope . connecting = false ;
2015-11-17 00:29:57 +01:00
$scope . HideFolderBrowser = true ;
$scope . RestoreLocation = 'direct' ;
$scope . RestoreMode = 'overwrite' ;
2018-11-20 20:07:14 +01:00
$scope . passphrase = "" ;
2015-10-04 17:13:30 +02:00
var filesetsBuilt = {};
var filesetsRepaired = {};
var filesetStamps = {};
var inProgress = {};
2017-01-06 23:01:54 +01:00
var dirsep = $scope . SystemInfo . DirectorySeparator || '/' ;
2015-10-04 17:13:30 +02:00
2015-10-26 09:16:32 +01:00
$scope . filesetStamps = filesetStamps ;
$scope . treedata = {};
2015-10-27 09:11:44 +01:00
$scope . Selected = [];
2015-10-26 09:16:32 +01:00
2015-10-04 17:13:30 +02:00
function createGroupLabel ( dt ) {
var dateStamp = function ( a ) { return a . getFullYear () * 10000 + a . getMonth () * 100 + a . getDate (); }
2018-11-20 20:07:14 +01:00
2015-10-04 17:13:30 +02:00
var now = new Date ();
var today = dateStamp ( now );
var yesterday = dateStamp ( new Date ( new Date (). setDate ( now . getDate () - 1 )));
var week = dateStamp ( new Date ( new Date (). setDate ( now . getDate () - 7 )));
var thismonth = dateStamp ( new Date ( new Date (). setMonth ( now . getMonth () - 1 )));
var lastmonth = dateStamp ( new Date ( new Date (). setMonth ( now . getMonth () - 2 )));
var dateBuckets = [
2018-11-20 20:07:14 +01:00
{ text : gettextCatalog . getString ( 'Today' ), stamp : today },
2016-10-02 15:18:34 +02:00
{ text : gettextCatalog . getString ( 'Yesterday' ), stamp : yesterday },
{ text : gettextCatalog . getString ( 'This week' ), stamp : week },
{ text : gettextCatalog . getString ( 'This month' ), stamp : thismonth },
{ text : gettextCatalog . getString ( 'Last month' ), stamp : lastmonth }
2015-10-04 17:13:30 +02:00
];
var stamp = dateStamp ( dt );
for ( var t in dateBuckets )
if ( stamp >= dateBuckets [ t ]. stamp )
2015-11-15 22:16:20 +01:00
return dateBuckets [ t ]. text ;
2015-10-04 17:13:30 +02:00
return dt . getFullYear () + '' ;
};
2015-11-22 00:49:40 +01:00
$scope . parseBackupTimesData = function () {
for ( var n in filesetStamps )
delete filesetStamps [ n ];
2015-11-15 22:16:20 +01:00
2015-11-22 00:49:40 +01:00
for ( var n in $scope . Filesets ) {
var item = $scope . Filesets [ n ];
item . DisplayLabel = item . Version + ': ' + AppUtils . toDisplayDateAndTime ( AppUtils . parseDate ( item . Time ));
2019-08-05 20:14:05 -04:00
if ( item . IsFullBackup === 0 ) {
item . DisplayLabel = item . DisplayLabel + ' (partial)' ;
}
2016-10-02 15:18:34 +02:00
item . GroupLabel = n == 0 ? gettextCatalog . getString ( 'Latest' ) : createGroupLabel ( AppUtils . parseDate ( item . Time ));
2015-11-15 22:16:20 +01:00
2015-11-22 00:49:40 +01:00
filesetStamps [ item . Version + '' ] = item . Time ;
2015-11-15 22:16:20 +01:00
}
2015-11-22 00:49:40 +01:00
$scope . RestoreVersion = 0 ;
2015-11-15 22:16:20 +01:00
};
$scope . fetchBackupTimes = function () {
2015-11-22 00:49:40 +01:00
$scope . connecting = true ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Getting file versions …' );
2015-11-15 22:16:20 +01:00
2015-11-22 00:49:40 +01:00
var qp = '' ;
if ( $scope . IsBackupTemporary )
qp += '?from-remote-only=true' ;
2015-11-15 22:16:20 +01:00
2015-11-22 00:49:40 +01:00
AppService . get ( '/backup/' + $scope . BackupID + '/filesets' + qp ). then (
function ( resp ) {
2015-12-06 17:12:04 +01:00
$scope . connecting = false ;
2017-01-06 23:01:54 +01:00
$scope . ConnectionProgress = '' ;
2015-11-22 00:49:40 +01:00
$scope . Filesets = resp . data ;
$scope . parseBackupTimesData ();
2015-11-15 22:16:20 +01:00
$scope . fetchPathInformation ();
},
function ( resp ) {
2023-12-12 23:17:12 +01:00
var message = AppService . responseErrorMessage ( resp );
2015-11-15 22:16:20 +01:00
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to connect: {{message}}' , { message : message }));
2015-11-15 22:16:20 +01:00
}
);
};
$scope . fetchPathInformation = function () {
var version = $scope . RestoreVersion + '' ;
2015-12-06 17:12:04 +01:00
if ( $scope . connecting )
return ;
2015-11-22 00:49:40 +01:00
if ( inProgress [ version ] || $scope . restore_step != 0 )
2015-11-15 22:16:20 +01:00
return ;
2015-11-22 00:49:40 +01:00
if ( ! $scope . IsBackupTemporary && $scope . temporaryDB == null ) {
// TODO: Register a temporary db here
}
function handleError ( resp ) {
delete inProgress [ version ];
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2023-12-12 23:17:12 +01:00
var message = AppService . responseErrorMessage ( resp );
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to fetch path information: {{message}}' , { message : message }));
2015-11-22 00:49:40 +01:00
};
2015-11-15 22:16:20 +01:00
if ( filesetsBuilt [ version ] == null ) {
2015-12-06 17:12:04 +01:00
if ( $scope . IsBackupTemporary && filesetsRepaired [ version ] == null ) {
2015-11-15 22:16:20 +01:00
$scope . connecting = true ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Fetching path information …' );
2015-11-15 22:16:20 +01:00
inProgress [ version ] = true ;
AppService . post ( '/backup/' + $scope . BackupID + '/repairupdate' , { 'only-paths' : true , 'time' : filesetStamps [ version + '' ]}). then (
function ( resp ) {
var taskid = resp . data . ID ;
inProgress [ version ] = taskid ;
$scope . taskid = taskid ;
ServerStatus . callWhenTaskCompletes ( taskid , function () {
AppService . get ( '/task/' + taskid ). then ( function ( resp ) {
delete inProgress [ version ];
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
if ( resp . data . Status == 'Completed' )
{
filesetsRepaired [ version ] = true ;
$scope . fetchPathInformation ();
}
else
{
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to fetch path information: {{message}}' , { message : resp . data . ErrorMessage }));
2015-11-15 22:16:20 +01:00
}
2015-11-22 00:49:40 +01:00
}, handleError );
2015-11-15 22:16:20 +01:00
});
2015-11-22 00:49:40 +01:00
}, handleError );
2015-11-15 22:16:20 +01:00
} else {
var stamp = filesetStamps [ version ];
2015-11-29 00:40:09 +01:00
// In case the times are not loaded yet
if ( stamp == null )
return ;
2015-11-15 22:16:20 +01:00
$scope . connecting = true ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Fetching path information …' );
2015-11-15 22:16:20 +01:00
inProgress [ version ] = true ;
AppService . get ( '/backup/' + $scope . BackupID + '/files/*?prefix-only=true&folder-contents=false&time=' + encodeURIComponent ( stamp )). then (
function ( resp ) {
delete inProgress [ version ];
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
filesetsBuilt [ version ] = resp . data . Files ;
$scope . Paths = filesetsBuilt [ version ];
2017-01-06 23:01:54 +01:00
dirsep = resp . data . Files [ 0 ]. Path [ 0 ] == '/' ? '/' : '\\' ;
2015-11-22 00:49:40 +01:00
}, handleError );
2015-11-15 22:16:20 +01:00
}
} else {
$scope . Paths = filesetsBuilt [ version ];
}
};
$scope . $watch ( 'RestoreVersion' , function () { $scope . fetchPathInformation (); });
2015-11-17 00:29:57 +01:00
$scope . $watch ( 'RestorePath' , function () {
if (( $scope . RestorePath || '' ). trim (). length == 0 )
$scope . RestoreLocation = 'direct' ;
else
$scope . RestoreLocation = 'custom' ;
});
2015-11-15 22:16:20 +01:00
$scope . onClickNext = function () {
2015-11-17 00:29:57 +01:00
var results = $scope . Selected ;
2015-11-15 22:16:20 +01:00
if ( results . length == 0 ) {
2016-10-02 15:18:34 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'No items selected' ), gettextCatalog . getString ( 'No items to restore, please select one or more items' ));
2015-11-17 00:29:57 +01:00
} else {
2015-11-22 00:49:40 +01:00
$scope . restore_step = 1 ;
2015-11-15 22:16:20 +01:00
}
};
2015-11-22 00:49:40 +01:00
$scope . onClickBack = function () {
$location . path ( '/restoredirect' )
};
2015-11-16 23:45:03 +01:00
$scope . clearSearch = function () {
$scope . InSearchMode = false ;
$scope . fetchPathInformation ();
};
2015-11-15 22:16:20 +01:00
$scope . doSearch = function () {
2016-02-10 16:37:24 +01:00
if ( $scope . Searching || $scope . restore_step != 0 )
2015-11-16 23:45:03 +01:00
return ;
if (( $scope . SearchFilter || '' ). trim (). length == 0 ) {
$scope . clearSearch ();
return ;
}
$scope . Searching = true ;
var version = $scope . RestoreVersion + '' ;
var stamp = filesetStamps [ version ];
2015-11-15 22:16:20 +01:00
2016-02-10 16:37:10 +01:00
AppService . get ( '/backup/' + $scope . BackupID + '/files/*' + $scope . SearchFilter + '*?prefix-only=false&time=' + encodeURIComponent ( stamp ) + '&filter=*' + encodeURIComponent ( $scope . SearchFilter ) + '*' ). then (
2015-11-15 22:16:20 +01:00
function ( resp ) {
2015-11-16 23:45:03 +01:00
$scope . Searching = false ;
var searchNodes = [];
function compareablePath ( path ) {
return $scope . SystemInfo . CaseSensitiveFilesystem ? path : path . toLowerCase ();
};
2018-11-20 20:07:14 +01:00
2015-11-16 23:45:03 +01:00
for ( var i in filesetsBuilt [ version ])
searchNodes [ i ] = { Path : filesetsBuilt [ version ][ i ]. Path };
var files = resp . data . Files ;
for ( var i in files ) {
var p = files [ i ]. Path ;
var cp = compareablePath ( p );
var isdir = p [ p . length - 1 ] == dirsep ;
for ( var j in searchNodes ) {
var sn = searchNodes [ j ];
if ( cp . indexOf ( compareablePath ( sn . Path )) == 0 ) {
var curpath = sn . Path ;
var parts = p . substr ( sn . Path . length ). split ( dirsep );
2023-12-10 00:25:47 +01:00
// Remove empty part if path had dirsep at end
if ( parts [ parts . length - 1 ]. length == 0 ) {
parts . pop ();
}
2015-11-16 23:45:03 +01:00
var col = sn ;
for ( var k in parts ) {
var found = false ;
curpath += parts [ k ];
if ( isdir || k != parts . length - 1 )
curpath += dirsep ;
if ( ! col . Children )
col . Children = [];
for ( var m in col . Children ) {
if ( compareablePath ( col . Children [ m ]. Path ) == compareablePath ( curpath )) {
found = true ;
2018-11-20 20:07:14 +01:00
col = col . Children [ m ];
2023-12-10 00:25:47 +01:00
break ;
2015-11-16 23:45:03 +01:00
}
}
if ( ! found ) {
var n = { Path : curpath , expanded : true };
if ( ! isdir && k == parts . length - 1 ) {
n . iconCls = 'x-tree-icon-leaf' ;
n . leaf = true ;
}
col . Children . push ( n );
col = n ;
}
}
break ;
}
}
}
$scope . Paths = searchNodes ;
$scope . InSearchMode = true ;
2015-11-15 22:16:20 +01:00
},
function ( resp ) {
2015-11-16 23:45:03 +01:00
$scope . Searching = false ;
2023-12-12 23:17:12 +01:00
var message = AppService . responseErrorMessage ( resp );
2015-11-15 22:16:20 +01:00
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to connect: {{message}}' , { message : message }));
2015-11-15 22:16:20 +01:00
}
);
};
2015-10-26 09:16:32 +01:00
2015-11-22 00:49:40 +01:00
$scope . onStartRestore = function () {
2017-01-06 23:01:54 +01:00
if ( $scope . RestoreLocation == 'custom' && ( $scope . RestorePath || '' ). trim (). length == 0 )
{
DialogService . alert ( gettextCatalog . getString ( 'You have chosen to restore to a new location, but not entered one' ));
return ;
}
if ( $scope . RestoreLocation != 'custom' && dirsep != $scope . SystemInfo . DirectorySeparator )
{
DialogService . confirm ( gettextCatalog . getString ( 'This backup was created on another operating system. Restoring files without specifying a destination folder can cause files to be restored in unexpected places. Are you sure you want to continue without choosing a destination folder?' ), function ( ix ) {
if ( ix == 1 )
$scope . onStartRestoreProcess ();
});
}
else
{
$scope . onStartRestoreProcess ();
}
}
$scope . onStartRestoreProcess = function () {
2015-11-22 00:49:40 +01:00
var version = $scope . RestoreVersion + '' ;
var stamp = filesetStamps [ version ];
2017-02-07 21:51:14 +01:00
$scope . restore_step = 2 ;
2015-11-22 00:49:40 +01:00
function handleError ( resp ) {
2023-12-12 23:17:12 +01:00
var message = AppService . responseErrorMessage ( resp );
2015-11-22 00:49:40 +01:00
2017-02-07 21:51:14 +01:00
$scope . restore_step = 1 ;
2015-11-22 00:49:40 +01:00
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to connect: {{message}}' , { message : message }));
2015-11-22 00:49:40 +01:00
};
var p = {
'time' : stamp ,
'restore-path' : $scope . RestoreLocation == 'custom' ? $scope . RestorePath : null ,
'overwrite' : $scope . RestoreMode == 'overwrite' ,
2018-11-20 20:07:14 +01:00
'permissions' : $scope . RestorePermissions == null ? false : $scope . RestorePermissions ,
'passphrase' : $scope . passphrase
2015-11-22 00:49:40 +01:00
};
var paths = [];
for ( var n in $scope . Selected ) {
var item = $scope . Selected [ n ];
2020-09-04 16:54:49 -07:00
if ( item . substr ( item . length - 1 ) == dirsep ) {
// To support the possibility of encountering paths
// with literal wildcard characters, but also being
// able to add the globbing "*" suffix, use a regular
// expression filter
// Escape regular expression metacharacters
var itemRegex = item . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' );
// Add "globbing" suffix
paths . push ( '[' + itemRegex + '.*]' );
2020-09-03 22:59:24 -07:00
} else {
2020-09-04 16:54:49 -07:00
// To support the possibility of encountering paths
// with literal wildcard characters, create a literal
// filter
paths . push ( '@' + item );
2020-09-03 22:59:24 -07:00
}
2015-11-22 00:49:40 +01:00
}
if ( paths . length > 0 )
2016-08-31 19:37:38 +02:00
p . paths = JSON . stringify ( paths );
2015-11-22 00:49:40 +01:00
if ( $scope . IsBackupTemporary ) {
$scope . connecting = true ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Creating temporary backup …' );
2015-11-22 00:49:40 +01:00
AppService . post ( '/backup/' + $scope . BackupID + '/copytotemp' ). then ( function ( resp ) {
var backupid = resp . data . ID ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Building partial temporary database …' );
2015-11-22 00:49:40 +01:00
AppService . post ( '/backup/' + backupid + '/repair' , p ). then ( function ( resp ) {
var taskid = $scope . taskid = resp . data . ID ;
ServerStatus . callWhenTaskCompletes ( taskid , function () {
AppService . get ( '/task/' + taskid ). then ( function ( resp ) {
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Starting the restore process …' );
2015-11-22 00:49:40 +01:00
if ( resp . data . Status == 'Completed' )
{
AppService . post ( '/backup/' + backupid + '/restore' , p ). then ( function ( resp ) {
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Restoring files …' );
2015-11-22 00:49:40 +01:00
var t2 = $scope . taskid = resp . data . TaskID ;
ServerStatus . callWhenTaskCompletes ( t2 , function () { $scope . onRestoreComplete ( t2 ); });
}, handleError );
}
else
{
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to build temporary database: {{message}}' , { message : resp . data . ErrorMessage }));
2015-11-22 00:49:40 +01:00
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2017-02-07 21:51:14 +01:00
$scope . restore_step = 1 ;
2015-11-22 00:49:40 +01:00
}
}, handleError );
});
}, handleError );
}, handleError );
} else {
2016-08-31 17:37:28 +02:00
$scope . connecting = true ;
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Starting the restore process …' );
2015-11-22 00:49:40 +01:00
AppService . post ( '/backup/' + $scope . BackupID + '/restore' , p ). then ( function ( resp ) {
2019-09-02 16:45:22 +00:00
$scope . ConnectionProgress = gettextCatalog . getString ( 'Restoring files …' );
2015-11-22 00:49:40 +01:00
var t2 = $scope . taskid = resp . data . TaskID ;
ServerStatus . callWhenTaskCompletes ( t2 , function () { $scope . onRestoreComplete ( t2 ); });
}, handleError );
}
};
$scope . onRestoreComplete = function ( taskid ) {
AppService . get ( '/task/' + taskid ). then ( function ( resp ) {
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
if ( resp . data . Status == 'Completed' )
{
2017-02-07 21:51:14 +01:00
$scope . restore_step = 3 ;
2015-11-22 00:49:40 +01:00
}
else
{
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to restore files: {{message}}' , { message : resp . data . ErrorMessage }));
2015-11-22 00:49:40 +01:00
}
}, function ( resp ) {
2023-12-12 23:17:12 +01:00
var message = AppService . responseErrorMessage ( resp );
2015-11-22 00:49:40 +01:00
2017-02-07 21:51:14 +01:00
$scope . restore_step = 1 ;
2015-11-22 00:49:40 +01:00
$scope . connecting = false ;
$scope . ConnectionProgress = '' ;
2016-10-02 16:47:40 +02:00
DialogService . dialog ( gettextCatalog . getString ( 'Error' ), gettextCatalog . getString ( 'Failed to connect: {{message}}' , { message : message }));
2015-11-22 00:49:40 +01:00
});
};
$scope . onClickComplete = function () {
$location . path ( '/' );
2017-02-07 21:51:14 +01:00
};
$scope . trySetStep = function ( pg ) {
if ( $scope . restore_step < 2 )
$scope . restore_step = pg ;
};
2015-11-22 00:49:40 +01:00
2018-11-20 20:07:14 +01:00
$scope . showInputPassphrase = function () {
if ( ! this . Backup ) {
return false ;
}
2023-09-22 12:34:06 +02:00
if ( ! ( 'IsUnencryptedOrPassphraseStored' in this . Backup )) {
2018-11-20 20:07:14 +01:00
return false ;
}
2018-11-21 09:55:56 +01:00
return ! this . Backup [ 'IsUnencryptedOrPassphraseStored' ];
2018-11-20 20:07:14 +01:00
}
2015-11-22 00:49:40 +01:00
$scope . BackupID = $routeParams . backupid ;
$scope . IsBackupTemporary = parseInt ( $scope . BackupID ) != $scope . BackupID ;
2017-02-07 21:51:14 +01:00
if ( ! $scope . IsBackupTemporary ) {
$scope . $on ( 'backuplistchanged' , function () { $scope . Backup = BackupList . lookup [ $scope . BackupID ]; });
$scope . Backup = BackupList . lookup [ $scope . BackupID ];
}
2015-11-22 00:49:40 +01:00
// We pass in the filelist through a global variable
// ... bit ugly, but we do not want to do two remote queries,
// ... nor do we want to pass the information through the url
2015-11-29 00:40:09 +01:00
if ( $scope . IsBackupTemporary && $rootScope . filesets && $rootScope . filesets [ $scope . BackupID ]) {
2015-11-22 00:49:40 +01:00
$scope . Filesets = $rootScope . filesets [ $scope . BackupID ];
$scope . parseBackupTimesData ();
$scope . fetchPathInformation ();
} else {
$scope . fetchBackupTimes ();
}
2016-09-15 11:39:27 +02:00
});