duplicati/Duplicati/Server/webroot/ngax/scripts/directives/restoreFilePicker.js
Kenneth Skovhede 9f79025744 Removed HttpServer.
Re-implemented everything using ASP.NET.
Changed some requests to use JSON instead of FORM data.
Some work towards deleting the FIXMEGlobal instance.
Auth is missing, XSRF does not work correctly.
2024-06-07 15:56:43 +02:00

354 lines
13 KiB
JavaScript

backupApp.directive('restoreFilePicker', function() {
return {
restrict: 'E',
require: ['ngSources', 'ngFilters', 'ngBackupId', 'ngTimestamp', 'ngSelected'],
scope: {
ngSources: '=',
ngFilters: '=',
ngBackupId: '=',
ngTimestamp: '=',
treedata: '=',
ngSelected: '=',
ngSearchFilter: '=',
ngSearchMode: '='
},
templateUrl: 'templates/restorefilepicker.html',
controller: function($scope, $timeout, SystemInfo, AppService, AppUtils) {
var scope = $scope;
var dirsep = '/';
$scope.systeminfo = SystemInfo.watch($scope);
$scope.treedata = [];
$scope.AppUtils = AppUtils;
function compareablePath(path) {
return scope.systeminfo.CaseSensitiveFilesystem ? path : path.toLowerCase();
};
$scope.toggleExpanded = function(node) {
node.expanded = !node.expanded;
if (node.root || node.iconCls == 'x-tree-icon-leaf' || node.iconCls == 'x-tree-icon-locked')
return;
if (!node.children && !node.loading) {
node.loading = true;
// Prefix filter with "@" to prevent Duplicati from
// mistaking literal '*' and '?' characters in paths
// for glob wildcard characters
AppService.get('/backup/' + $scope.ngBackupId + '/files?prefix-only=false&folder-contents=true&time=' + encodeURIComponent($scope.ngTimestamp) + '&filter=' + encodeURIComponent('@' + node.id)).then(function(data) {
var children = []
for(var n in data.data.Files)
{
var disp = data.data.Files[n].Path.substr(node.id.length);
var leaf = true;
if (disp.substr(disp.length - 1, 1) == dirsep) {
disp = disp.substr(0, disp.length - 1);
leaf = false;
}
children.push({
text: disp,
id: data.data.Files[n].Path,
size: data.data.Files[n].Sizes[0],
iconCls: leaf ? 'x-tree-icon-leaf' : '',
entrytype: AppUtils.getEntryTypeFromIconCls(leaf ? 'x-tree-icon-leaf' : ''),
leaf: leaf
});
}
node.children = children;
node.loading = false;
updateCheckState(children);
}, function() {
node.loading = false;
node.expanded = false;
AppUtils.connectionError.apply(AppUtils, arguments);
});
}
};
$scope.toggleSelected = function(node) {
if (scope.selectednode != null)
scope.selectednode.selected = false;
scope.selectednode = node;
scope.selectednode.selected = true;
};
function forAllChildren(node, callback) {
var q = [node];
while(q.length > 0) {
var x = q.pop();
if (x.children != null)
for(var c in x.children) {
q.push(x.children[c]);
callback(x.children[c]);
}
}
};
function propagateCheckDown(node) {
forAllChildren(node, function(n) { n.include = node.include; });
};
function findParent(node) {
var e = [];
e.push.apply(e, $scope.treedata.children);
var p = compareablePath(node.id);
while(e.length > 0) {
var el = e.pop();
if (p.length > el.id.length && el.id.substr(el.id.length - 1, 1) == dirsep && el.children != null) {
if (p.indexOf(compareablePath(el.id)) == 0) {
for(var n in el.children) {
if (el.children[n] == node)
return el;
else
e.push(el.children[n]);
}
}
}
}
return null;
};
function propagateCheckUp(node) {
var p = findParent(node);
while(p != null) {
var all = true;
var any = false;
for(var i in p.children) {
if (p.children[i].include != '+')
all = false;
if (p.children[i].include == '+' || p.children[i].include == ' ')
any = true;
if (all == false && any == true)
break;
}
if (all) {
p.include = '+';
} else if (any) {
p.include = ' ';
} else {
p.include = '';
}
p = findParent(p);
}
};
function buildSelectedMap() {
var map = {};
for (var i = $scope.ngSelected.length - 1; i >= 0; i--)
map[compareablePath($scope.ngSelected[i])] = true;
return map;
};
function buildPartialMap() {
var map = {};
for (var i = $scope.ngSelected.length - 1; i >= 0; i--) {
var is_dir = $scope.ngSelected[i].substr($scope.ngSelected[i].length - 1, 1) == dirsep;
var parts = $scope.ngSelected[i].split(dirsep);
var cur = '';
for (var j = 0; j < parts.length; j++) {
cur += parts[j];
if (j != parts.length - 1 || is_dir)
cur += dirsep;
map[compareablePath(cur)] = true;
};
}
return map;
};
function removePathFromArray(arr, path) {
path = compareablePath(path);
for (var i = arr.length - 1; i >= 0; i--) {
var n = compareablePath(arr[i]);
if (n == path)
arr.splice(i, 1);
};
};
$scope.toggleCheck = function(node) {
if (node.include != '+') {
var p = findParent(node) || node;
var cur = node;
while(p != null) {
// Remove path and all sub-paths
var c = compareablePath(cur.id);
var is_dir = c.substr(c.length - 1, 1) == dirsep;
for (var i = $scope.ngSelected.length - 1; i >= 0; i--) {
var n = compareablePath($scope.ngSelected[i]);
if (n == c || (n.indexOf(c) == 0 && is_dir))
$scope.ngSelected.splice(i, 1);
};
var all = true;
var pp = compareablePath(p.id);
var map = buildSelectedMap();
map[c] = true;
for (var i = p.children.length - 1; i >= 0; i--)
if (!map[compareablePath(p.children[i].id)]) {
all = false;
break;
}
if (!all || p == node || $scope.ngSearchMode) {
$scope.ngSelected.push(cur.id);
break;
}
cur = p;
p = findParent(p);
if (p == null && all && !$scope.ngSearchMode)
$scope.ngSelected.push(cur.id);
}
} else {
var map = buildSelectedMap();
// This item is not included, include parents children
var backtrace = [];
var p = node;
while(p != null && !map[compareablePath(p.id)]) {
backtrace.push(p);
p = findParent(p);
}
removePathFromArray($scope.ngSelected, p.id);
while(backtrace.length > 0) {
var t = backtrace.pop();
for(var n in p.children)
if (t != p.children[n]) {
$scope.ngSelected.push(p.children[n].id);
}
p = t;
}
}
//updateCheckState();
};
var updateCheckState = function(nodes) {
var map = buildSelectedMap();
var partialmap = buildPartialMap();
nodes = nodes || $scope.treedata.children;
var w = [];
w.push.apply(w, $scope.treedata.children);
while(w.length > 0) {
var n = w.pop();
var cp = compareablePath(n.id);
if (map[cp]) {
n.include = '+';
propagateCheckDown(n);
} else if (partialmap[cp]) {
n.include = ' ';
if (n.children != null)
w.push.apply(w, n.children);
}
else {
n.include = '';
propagateCheckDown(n);
}
}
};
var buildnodes = function(items, parentpath) {
var res = [];
parentpath = parentpath || '';
for(var n in items) {
var txt = items[n].Path.substr(parentpath.length);
if (txt[txt.length] == dirsep)
txt.length--;
var rootnode = {
text: txt,
expanded: items[n].expanded,
iconCls: items[n].iconCls,
leaf: items[n].leaf,
id: items[n].Path,
entrytype: AppUtils.getEntryTypeFromIconCls(items[n].iconCls)
};
if (items[n].Children) {
rootnode.children = buildnodes(items[n].Children, items[n].Path);
delete items[n].Children;
}
res.push(rootnode);
}
return res;
};
var updateRoots = function()
{
if ($scope.ngSources == null || $scope.ngSources.length == 0)
dirsep = scope.systeminfo.DirectorySeparator || '/';
else
dirsep = $scope.ngSources[0].Path[0] == '/' ? '/' : '\\';
var roots = buildnodes($scope.ngSources);
$scope.treedata = $scope.treedata || {};
$scope.treedata.children = roots;
$scope.treedata.forAll = function(callback) { forAllChildren(this, callback); };
$scope.treedata.allSelected = function() {
var q = [];
var r = [];
q.push.apply(q, this.children);
while(q.length > 0) {
var n = q.pop();
if (n.include == '+')
r.push(n.id);
else if (n.include == ' ')
q.push.apply(q, n.children);
}
return r;
};
if (roots.length == 1)
$scope.toggleExpanded(roots[0]);
updateCheckState();
};
$scope.$watchCollection('ngSources', updateRoots);
updateRoots();
$scope.$watchCollection('ngSelected', updateCheckState)
}
};
});