2014-11-26 13:39:59 +01:00
|
|
|
angular.module('syncthing.core')
|
2018-12-25 14:26:46 +01:00
|
|
|
.config(function ($locationProvider) {
|
|
|
|
|
$locationProvider.html5Mode({ enabled: true, requireBase: false }).hashPrefix('!');
|
2015-03-22 15:35:08 +00:00
|
|
|
})
|
2018-03-18 01:42:31 +01:00
|
|
|
.controller('SyncthingController', function ($scope, $http, $location, LocaleService, Events, $filter, $q, $compile, $timeout, $rootScope, $translate) {
|
2014-11-26 13:39:59 +01:00
|
|
|
'use strict';
|
|
|
|
|
|
2014-11-28 19:39:33 +01:00
|
|
|
// private/helper definitions
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var prevDate = 0;
|
|
|
|
|
var navigatingAway = false;
|
|
|
|
|
var online = false;
|
|
|
|
|
var restarting = false;
|
|
|
|
|
|
2014-11-28 19:39:33 +01:00
|
|
|
function initController() {
|
|
|
|
|
LocaleService.autoConfigLocale();
|
|
|
|
|
setInterval($scope.refresh, 10000);
|
2015-06-14 17:17:48 +03:00
|
|
|
Events.start();
|
2014-11-28 19:39:33 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
// public/scope definitions
|
2014-11-28 19:39:33 +01:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.completion = {};
|
|
|
|
|
$scope.config = {};
|
|
|
|
|
$scope.configInSync = true;
|
|
|
|
|
$scope.connections = {};
|
|
|
|
|
$scope.errors = [];
|
|
|
|
|
$scope.model = {};
|
|
|
|
|
$scope.myID = '';
|
|
|
|
|
$scope.devices = [];
|
2016-06-26 10:47:23 +00:00
|
|
|
$scope.discoveryCache = {};
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.protocolChanged = false;
|
|
|
|
|
$scope.reportData = {};
|
2017-10-15 07:45:15 +00:00
|
|
|
$scope.reportDataPreview = '';
|
2017-10-12 06:16:46 +00:00
|
|
|
$scope.reportDataPreviewVersion = '';
|
2017-10-15 07:45:15 +00:00
|
|
|
$scope.reportDataPreviewDiff = false;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.reportPreview = false;
|
|
|
|
|
$scope.folders = {};
|
|
|
|
|
$scope.seenError = '';
|
|
|
|
|
$scope.upgradeInfo = null;
|
2014-12-07 20:21:12 +00:00
|
|
|
$scope.deviceStats = {};
|
|
|
|
|
$scope.folderStats = {};
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.progress = {};
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.version = {};
|
2015-04-25 22:53:44 +01:00
|
|
|
$scope.needed = [];
|
|
|
|
|
$scope.neededCurrentPage = 1;
|
|
|
|
|
$scope.neededPageSize = 10;
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.failed = {};
|
2018-12-11 09:59:04 +01:00
|
|
|
$scope.localChanged = {};
|
2015-08-26 23:49:06 +01:00
|
|
|
$scope.scanProgress = {};
|
2016-06-07 07:46:45 +00:00
|
|
|
$scope.themes = [];
|
2016-12-21 16:35:20 +00:00
|
|
|
$scope.globalChangeEvents = {};
|
2017-03-31 06:32:54 +00:00
|
|
|
$scope.metricRates = false;
|
2017-05-23 19:54:56 +00:00
|
|
|
$scope.folderPathErrors = {};
|
2018-03-25 22:05:47 +02:00
|
|
|
$scope.currentFolder = {};
|
2017-12-15 20:01:56 +00:00
|
|
|
resetRemoteNeed();
|
2017-03-31 06:32:54 +00:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
$scope.metricRates = (window.localStorage["metricRates"] == "true");
|
|
|
|
|
} catch (exception) { }
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2017-04-01 09:58:06 +00:00
|
|
|
$scope.folderDefaults = {
|
2017-05-23 19:54:56 +00:00
|
|
|
selectedDevices: {},
|
2018-05-13 09:58:00 +02:00
|
|
|
type: "sendreceive",
|
2018-03-25 22:05:47 +02:00
|
|
|
rescanIntervalS: 3600,
|
2017-10-20 14:52:55 +00:00
|
|
|
fsWatcherDelayS: 10,
|
2018-03-25 22:05:47 +02:00
|
|
|
fsWatcherEnabled: true,
|
2018-12-25 14:26:46 +01:00
|
|
|
minDiskFree: { value: 1, unit: "%" },
|
2017-05-23 19:54:56 +00:00
|
|
|
maxConflicts: 10,
|
|
|
|
|
fsync: true,
|
|
|
|
|
order: "random",
|
|
|
|
|
fileVersioningSelector: "none",
|
|
|
|
|
trashcanClean: 0,
|
|
|
|
|
simpleKeep: 5,
|
|
|
|
|
staggeredMaxAge: 365,
|
|
|
|
|
staggeredCleanInterval: 3600,
|
|
|
|
|
staggeredVersionsPath: "",
|
|
|
|
|
externalCommand: "",
|
2017-07-20 13:16:54 +00:00
|
|
|
autoNormalize: true,
|
2019-02-02 13:06:01 +01:00
|
|
|
path: "",
|
|
|
|
|
useLargeBlocks: true,
|
2017-04-01 09:58:06 +00:00
|
|
|
};
|
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.localStateTotal = {
|
|
|
|
|
bytes: 0,
|
2017-06-07 10:34:45 +00:00
|
|
|
directories: 0,
|
2015-07-20 14:48:03 +02:00
|
|
|
files: 0
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$(window).bind('beforeunload', function () {
|
|
|
|
|
navigatingAway = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$scope.$on("$locationChangeSuccess", function () {
|
2014-11-28 19:39:33 +01:00
|
|
|
LocaleService.useLocale($location.search().lang);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$scope.needActions = {
|
|
|
|
|
'rm': 'Del',
|
|
|
|
|
'rmdir': 'Del (dir)',
|
|
|
|
|
'sync': 'Sync',
|
|
|
|
|
'touch': 'Update'
|
|
|
|
|
};
|
|
|
|
|
$scope.needIcons = {
|
2018-05-24 20:59:32 +02:00
|
|
|
'rm': 'far fa-fw fa-trash-alt',
|
|
|
|
|
'rmdir': 'far fa-fw fa-trash-alt',
|
|
|
|
|
'sync': 'far fa-fw arrow-alt-circle-down',
|
|
|
|
|
'touch': 'fas fa-fw fa-asterisk'
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.ONLINE, function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (online && !restarting) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('UIOnline');
|
2015-01-12 12:14:24 +01:00
|
|
|
|
|
|
|
|
refreshSystem();
|
2016-06-26 10:47:23 +00:00
|
|
|
refreshDiscoveryCache();
|
2015-01-12 12:14:24 +01:00
|
|
|
refreshConfig();
|
|
|
|
|
refreshConnectionStats();
|
|
|
|
|
refreshDeviceStats();
|
|
|
|
|
refreshFolderStats();
|
2016-12-30 01:33:27 +00:00
|
|
|
refreshGlobalChanges();
|
2016-06-07 07:46:45 +00:00
|
|
|
refreshThemes();
|
2015-01-12 12:14:24 +01:00
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/version').success(function (data) {
|
2019-01-15 08:44:46 +01:00
|
|
|
console.log("version", data);
|
2016-03-11 09:48:46 +00:00
|
|
|
if ($scope.version.version && $scope.version.version !== data.version) {
|
2015-08-25 15:38:28 +02:00
|
|
|
// We already have a version response, but it differs from
|
|
|
|
|
// the new one. Reload the full GUI in case it's changed.
|
|
|
|
|
document.location.reload(true);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.version = data;
|
2015-01-12 12:14:24 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
|
2015-04-07 13:32:33 +02:00
|
|
|
$http.get(urlbase + '/svc/report').success(function (data) {
|
2017-10-15 07:45:15 +00:00
|
|
|
$scope.reportData = data;
|
2017-10-21 18:46:07 +00:00
|
|
|
if ($scope.system && $scope.config.options.urAccepted > -1 && $scope.config.options.urSeen < $scope.system.urVersionMax && $scope.config.options.urAccepted < $scope.system.urVersionMax) {
|
2017-10-12 06:16:46 +00:00
|
|
|
// Usage reporting format has changed, prompt the user to re-accept.
|
|
|
|
|
$('#ur').modal();
|
|
|
|
|
}
|
2015-01-12 12:14:24 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/upgrade').success(function (data) {
|
2015-01-12 12:14:24 +01:00
|
|
|
$scope.upgradeInfo = data;
|
|
|
|
|
}).error(function () {
|
|
|
|
|
$scope.upgradeInfo = null;
|
|
|
|
|
});
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
online = true;
|
|
|
|
|
restarting = false;
|
|
|
|
|
$('#networkError').modal('hide');
|
|
|
|
|
$('#restarting').modal('hide');
|
|
|
|
|
$('#shutdown').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.OFFLINE, function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (navigatingAway || !online) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('UIOffline');
|
|
|
|
|
online = false;
|
|
|
|
|
if (!restarting) {
|
|
|
|
|
$('#networkError').modal();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2015-01-05 15:42:27 +01:00
|
|
|
$scope.$on('HTTPError', function (event, arg) {
|
|
|
|
|
// Emitted when a HTTP call fails. We use the status code to try
|
|
|
|
|
// to figure out what's wrong.
|
|
|
|
|
|
|
|
|
|
if (navigatingAway || !online) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('HTTPError', arg);
|
|
|
|
|
online = false;
|
|
|
|
|
if (!restarting) {
|
|
|
|
|
if (arg.status === 0) {
|
|
|
|
|
// A network error, not an HTTP error
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$emit(Events.OFFLINE);
|
2015-01-05 15:42:27 +01:00
|
|
|
} else if (arg.status >= 400 && arg.status <= 599) {
|
|
|
|
|
// A genuine HTTP error
|
|
|
|
|
$('#networkError').modal('hide');
|
|
|
|
|
$('#restarting').modal('hide');
|
|
|
|
|
$('#shutdown').modal('hide');
|
|
|
|
|
$('#httpError').modal();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.STATE_CHANGED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var data = arg.data;
|
|
|
|
|
if ($scope.model[data.folder]) {
|
|
|
|
|
$scope.model[data.folder].state = data.to;
|
2015-06-13 19:10:11 +01:00
|
|
|
$scope.model[data.folder].error = data.error;
|
2015-06-26 14:22:52 +02:00
|
|
|
|
2015-08-26 23:49:06 +01:00
|
|
|
// If a folder has started scanning, then any scan progress is
|
|
|
|
|
// also obsolete.
|
|
|
|
|
if (data.to === 'scanning') {
|
|
|
|
|
delete $scope.scanProgress[data.folder];
|
|
|
|
|
}
|
2016-06-02 19:26:52 +00:00
|
|
|
|
|
|
|
|
// If a folder finished scanning, then refresh folder stats
|
|
|
|
|
// to update last scan time.
|
2018-12-25 14:26:46 +01:00
|
|
|
if (data.from === 'scanning' && data.to === 'idle') {
|
2016-06-02 19:26:52 +00:00
|
|
|
refreshFolderStats();
|
|
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.LOCAL_INDEX_UPDATED, function (event, arg) {
|
2014-12-07 20:21:12 +00:00
|
|
|
refreshFolderStats();
|
2016-12-21 16:35:20 +00:00
|
|
|
refreshGlobalChanges();
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.DEVICE_DISCONNECTED, function (event, arg) {
|
2015-08-23 21:56:10 +02:00
|
|
|
$scope.connections[arg.data.id].connected = false;
|
2014-11-26 13:39:59 +01:00
|
|
|
refreshDeviceStats();
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.DEVICE_CONNECTED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!$scope.connections[arg.data.id]) {
|
|
|
|
|
$scope.connections[arg.data.id] = {
|
|
|
|
|
inbps: 0,
|
|
|
|
|
outbps: 0,
|
2015-03-10 23:45:43 +01:00
|
|
|
inBytesTotal: 0,
|
|
|
|
|
outBytesTotal: 0,
|
2015-07-17 21:22:07 +01:00
|
|
|
type: arg.data.type,
|
2015-03-10 23:45:43 +01:00
|
|
|
address: arg.data.addr
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
$scope.completion[arg.data.id] = {
|
2017-07-05 09:19:29 +00:00
|
|
|
_total: 100,
|
2017-12-15 20:01:56 +00:00
|
|
|
_needBytes: 0,
|
|
|
|
|
_needItems: 0
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on('ConfigLoaded', function () {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.options.urAccepted === 0) {
|
2014-11-26 13:39:59 +01:00
|
|
|
// If usage reporting has been neither accepted nor declined,
|
|
|
|
|
// we want to ask the user to make a choice. But we don't want
|
|
|
|
|
// to bug them during initial setup, so we set a cookie with
|
|
|
|
|
// the time of the first visit. When that cookie is present
|
|
|
|
|
// and the time is more than four hours ago, we ask the
|
|
|
|
|
// question.
|
|
|
|
|
|
|
|
|
|
var firstVisit = document.cookie.replace(/(?:(?:^|.*;\s*)firstVisit\s*\=\s*([^;]*).*$)|^.*$/, "$1");
|
|
|
|
|
if (!firstVisit) {
|
|
|
|
|
document.cookie = "firstVisit=" + Date.now() + ";max-age=" + 30 * 24 * 3600;
|
|
|
|
|
} else {
|
|
|
|
|
if (+firstVisit < Date.now() - 4 * 3600 * 1000) {
|
2016-06-17 06:44:55 +00:00
|
|
|
$('#ur').modal();
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.CONFIG_SAVED, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
updateLocalConfig(arg.data);
|
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config/insync').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = data.configInSync;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.DOWNLOAD_PROGRESS, function (event, arg) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var stats = arg.data;
|
|
|
|
|
var progress = {};
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var folder in stats) {
|
2014-11-26 13:39:59 +01:00
|
|
|
progress[folder] = {};
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var file in stats[folder]) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var s = stats[folder][file];
|
2015-03-10 23:45:43 +01:00
|
|
|
var reused = 100 * s.reused / s.total;
|
|
|
|
|
var copiedFromOrigin = 100 * s.copiedFromOrigin / s.total;
|
|
|
|
|
var copiedFromElsewhere = 100 * s.copiedFromElsewhere / s.total;
|
|
|
|
|
var pulled = 100 * s.pulled / s.total;
|
|
|
|
|
var pulling = 100 * s.pulling / s.total;
|
2015-04-28 18:34:55 +03:00
|
|
|
// We try to round up pulling to at least a percent so that it would be at least a bit visible.
|
2014-12-03 23:44:39 +00:00
|
|
|
if (pulling < 1 && pulled + copiedFromElsewhere + copiedFromOrigin + reused <= 99) {
|
2014-11-26 13:39:59 +01:00
|
|
|
pulling = 1;
|
|
|
|
|
}
|
|
|
|
|
progress[folder][file] = {
|
2015-03-10 23:45:43 +01:00
|
|
|
reused: reused,
|
|
|
|
|
copiedFromOrigin: copiedFromOrigin,
|
|
|
|
|
copiedFromElsewhere: copiedFromElsewhere,
|
|
|
|
|
pulled: pulled,
|
|
|
|
|
pulling: pulling,
|
|
|
|
|
bytesTotal: s.bytesTotal,
|
|
|
|
|
bytesDone: s.bytesDone,
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-11-28 19:39:33 +01:00
|
|
|
for (var folder in $scope.progress) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!(folder in progress)) {
|
2016-03-11 09:48:46 +00:00
|
|
|
if ($scope.neededFolder === folder) {
|
2014-11-26 13:39:59 +01:00
|
|
|
refreshNeed(folder);
|
|
|
|
|
}
|
2016-03-11 09:48:46 +00:00
|
|
|
} else if ($scope.neededFolder === folder) {
|
2014-11-28 19:39:33 +01:00
|
|
|
for (file in $scope.progress[folder]) {
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!(file in progress[folder])) {
|
|
|
|
|
refreshNeed(folder);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$scope.progress = progress;
|
|
|
|
|
console.log("DownloadProgress", $scope.progress);
|
|
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.FOLDER_SUMMARY, function (event, arg) {
|
2015-03-26 23:26:51 +01:00
|
|
|
var data = arg.data;
|
|
|
|
|
$scope.model[data.folder] = data.summary;
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2015-03-26 23:26:51 +01:00
|
|
|
});
|
|
|
|
|
|
2015-06-14 17:17:48 +03:00
|
|
|
$scope.$on(Events.FOLDER_COMPLETION, function (event, arg) {
|
2015-03-26 23:26:51 +01:00
|
|
|
var data = arg.data;
|
|
|
|
|
if (!$scope.completion[data.device]) {
|
|
|
|
|
$scope.completion[data.device] = {};
|
|
|
|
|
}
|
2016-08-12 06:41:43 +00:00
|
|
|
$scope.completion[data.device][data.folder] = data;
|
|
|
|
|
recalcCompletion(data.device);
|
2015-03-26 23:26:51 +01:00
|
|
|
});
|
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.$on(Events.FOLDER_ERRORS, function (event, arg) {
|
2018-01-14 17:01:06 +00:00
|
|
|
$scope.model[arg.data.folder].pullErrors = arg.data.errors.length;
|
2015-06-26 14:22:52 +02:00
|
|
|
});
|
|
|
|
|
|
2015-08-26 23:49:06 +01:00
|
|
|
$scope.$on(Events.FOLDER_SCAN_PROGRESS, function (event, arg) {
|
|
|
|
|
var data = arg.data;
|
|
|
|
|
$scope.scanProgress[data.folder] = {
|
|
|
|
|
current: data.current,
|
2015-11-17 21:08:36 +01:00
|
|
|
total: data.total,
|
2016-03-11 09:48:46 +00:00
|
|
|
rate: data.rate
|
2015-08-26 23:49:06 +01:00
|
|
|
};
|
|
|
|
|
console.log("FolderScanProgress", data);
|
|
|
|
|
});
|
|
|
|
|
|
2015-01-05 15:42:27 +01:00
|
|
|
$scope.emitHTTPError = function (data, status, headers, config) {
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.$emit('HTTPError', { data: data, status: status, headers: headers, config: config });
|
2015-01-05 15:42:27 +01:00
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var debouncedFuncs = {};
|
|
|
|
|
|
|
|
|
|
function refreshFolder(folder) {
|
|
|
|
|
var key = "refreshFolder" + folder;
|
|
|
|
|
if (!debouncedFuncs[key]) {
|
|
|
|
|
debouncedFuncs[key] = debounce(function () {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/db/status?folder=' + encodeURIComponent(folder)).success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.model[folder] = data;
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshFolder", folder, data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}, 1000, true);
|
|
|
|
|
}
|
|
|
|
|
debouncedFuncs[key]();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function updateLocalConfig(config) {
|
|
|
|
|
var hasConfig = !isEmptyObject($scope.config);
|
|
|
|
|
|
|
|
|
|
$scope.config = config;
|
2016-05-04 19:38:12 +00:00
|
|
|
$scope.config.options._listenAddressesStr = $scope.config.options.listenAddresses.join(', ');
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.config.options._globalAnnounceServersStr = $scope.config.options.globalAnnounceServers.join(', ');
|
2017-10-12 06:16:46 +00:00
|
|
|
$scope.config.options._urAcceptedStr = "" + $scope.config.options.urAccepted;
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.devices = $scope.config.devices;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.devices.forEach(function (deviceCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.completion[deviceCfg.deviceID] = {
|
2017-07-05 09:19:29 +00:00
|
|
|
_total: 100,
|
2017-12-15 20:01:56 +00:00
|
|
|
_needBytes: 0,
|
|
|
|
|
_needItems: 0
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
$scope.devices.sort(deviceCompare);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders = folderMap($scope.config.folders);
|
2014-11-26 13:39:59 +01:00
|
|
|
Object.keys($scope.folders).forEach(function (folder) {
|
|
|
|
|
refreshFolder(folder);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folder].devices.forEach(function (deviceCfg) {
|
|
|
|
|
refreshCompletion(deviceCfg.deviceID, folder);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2015-11-16 21:33:55 +01:00
|
|
|
// If we're not listening on localhost, and there is no
|
|
|
|
|
// authentication configured, and the magic setting to silence the
|
|
|
|
|
// warning isn't set, then yell at the user.
|
|
|
|
|
var guiCfg = $scope.config.gui;
|
2016-03-11 09:48:46 +00:00
|
|
|
$scope.openNoAuth = guiCfg.address.substr(0, 4) !== "127."
|
|
|
|
|
&& guiCfg.address.substr(0, 6) !== "[::1]:"
|
2015-11-16 21:33:55 +01:00
|
|
|
&& (!guiCfg.user || !guiCfg.password)
|
2018-09-15 07:12:00 +01:00
|
|
|
&& guiCfg.authMode !== 'ldap'
|
2015-11-16 21:33:55 +01:00
|
|
|
&& !guiCfg.insecureAdminAccess;
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
if (!hasConfig) {
|
|
|
|
|
$scope.$emit('ConfigLoaded');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshSystem() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/status').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.myID = data.myID;
|
|
|
|
|
$scope.system = data;
|
2015-07-17 21:22:07 +01:00
|
|
|
|
2017-10-12 06:16:46 +00:00
|
|
|
if ($scope.reportDataPreviewVersion === '') {
|
|
|
|
|
$scope.reportDataPreviewVersion = $scope.system.urVersionMax;
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-04 19:38:12 +00:00
|
|
|
var listenersFailed = [];
|
|
|
|
|
for (var address in data.connectionServiceStatus) {
|
|
|
|
|
if (data.connectionServiceStatus[address].error) {
|
|
|
|
|
listenersFailed.push(address + ": " + data.connectionServiceStatus[address].error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$scope.listenersFailed = listenersFailed;
|
2017-12-15 20:01:56 +00:00
|
|
|
$scope.listenersTotal = $scope.sizeOf(data.connectionServiceStatus);
|
2016-05-04 19:38:12 +00:00
|
|
|
|
2015-09-20 15:30:25 +02:00
|
|
|
$scope.discoveryTotal = data.discoveryMethods;
|
|
|
|
|
var discoveryFailed = [];
|
|
|
|
|
for (var disco in data.discoveryErrors) {
|
|
|
|
|
if (data.discoveryErrors[disco]) {
|
|
|
|
|
discoveryFailed.push(disco + ": " + data.discoveryErrors[disco]);
|
2015-07-17 21:22:07 +01:00
|
|
|
}
|
|
|
|
|
}
|
2015-09-20 15:30:25 +02:00
|
|
|
$scope.discoveryFailed = discoveryFailed;
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshSystem", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-26 10:47:23 +00:00
|
|
|
function refreshDiscoveryCache() {
|
|
|
|
|
$http.get(urlbase + '/system/discovery').success(function (data) {
|
|
|
|
|
for (var device in data) {
|
|
|
|
|
for (var i = 0; i < data[device].addresses.length; i++) {
|
|
|
|
|
// Relay addresses are URLs with
|
|
|
|
|
// .../?foo=barlongstuff that we strip away here. We
|
|
|
|
|
// remove the final slash as well for symmetry with
|
|
|
|
|
// tcp://192.0.2.42:1234 type addresses.
|
|
|
|
|
data[device].addresses[i] = data[device].addresses[i].replace(/\/\?.*/, '');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$scope.discoveryCache = data;
|
|
|
|
|
console.log("refreshDiscoveryCache", data);
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
function recalcLocalStateTotal() {
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.localStateTotal = {
|
|
|
|
|
bytes: 0,
|
2017-06-07 10:34:45 +00:00
|
|
|
directories: 0,
|
2015-07-20 14:48:03 +02:00
|
|
|
files: 0
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (var f in $scope.model) {
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.localStateTotal.bytes += $scope.model[f].localBytes;
|
|
|
|
|
$scope.localStateTotal.files += $scope.model[f].localFiles;
|
|
|
|
|
$scope.localStateTotal.directories += $scope.model[f].localDirectories;
|
2015-07-20 14:48:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-12 06:41:43 +00:00
|
|
|
function recalcCompletion(device) {
|
2017-12-15 20:01:56 +00:00
|
|
|
var total = 0, needed = 0, deletes = 0, items = 0;
|
2016-08-12 06:41:43 +00:00
|
|
|
for (var folder in $scope.completion[device]) {
|
2017-12-15 20:01:56 +00:00
|
|
|
if (folder === "_total" || folder === '_needBytes' || folder === '_needItems') {
|
2016-08-12 06:41:43 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
total += $scope.completion[device][folder].globalBytes;
|
|
|
|
|
needed += $scope.completion[device][folder].needBytes;
|
2017-12-15 20:01:56 +00:00
|
|
|
items += $scope.completion[device][folder].needItems;
|
2016-09-02 06:45:46 +00:00
|
|
|
deletes += $scope.completion[device][folder].needDeletes;
|
2016-08-12 06:41:43 +00:00
|
|
|
}
|
2016-08-12 08:49:16 +02:00
|
|
|
if (total == 0) {
|
|
|
|
|
$scope.completion[device]._total = 100;
|
2017-07-05 09:19:29 +00:00
|
|
|
$scope.completion[device]._needBytes = 0;
|
2017-12-15 20:01:56 +00:00
|
|
|
$scope.completion[device]._needItems = 0;
|
2016-08-12 08:49:16 +02:00
|
|
|
} else {
|
2017-08-24 04:26:12 +00:00
|
|
|
$scope.completion[device]._total = Math.floor(100 * (1 - needed / total));
|
2017-07-05 09:19:29 +00:00
|
|
|
$scope.completion[device]._needBytes = needed
|
2018-01-14 12:08:40 +00:00
|
|
|
$scope.completion[device]._needItems = items + deletes;
|
2016-08-12 08:49:16 +02:00
|
|
|
}
|
2016-08-12 06:41:43 +00:00
|
|
|
|
2016-09-02 06:45:46 +00:00
|
|
|
if (needed == 0 && deletes > 0) {
|
|
|
|
|
// We don't need any data, but we have deletes that we need
|
|
|
|
|
// to do. Drop down the completion percentage to indicate
|
|
|
|
|
// that we have stuff to do.
|
|
|
|
|
$scope.completion[device]._total = 95;
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-12 06:41:43 +00:00
|
|
|
console.log("recalcCompletion", device, $scope.completion[device]);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
function refreshCompletion(device, folder) {
|
|
|
|
|
if (device === $scope.myID) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/db/completion?device=' + device + '&folder=' + encodeURIComponent(folder)).success(function (data) {
|
2015-03-26 23:26:51 +01:00
|
|
|
if (!$scope.completion[device]) {
|
|
|
|
|
$scope.completion[device] = {};
|
|
|
|
|
}
|
2016-08-12 06:41:43 +00:00
|
|
|
$scope.completion[device][folder] = data;
|
|
|
|
|
recalcCompletion(device);
|
2015-03-26 23:26:51 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshConnectionStats() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/connections').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var now = Date.now(),
|
|
|
|
|
td = (now - prevDate) / 1000,
|
|
|
|
|
id;
|
|
|
|
|
|
|
|
|
|
prevDate = now;
|
2015-04-09 11:14:15 +01:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
data.total.inbps = Math.max(0, (data.total.inBytesTotal - $scope.connectionsTotal.inBytesTotal) / td);
|
|
|
|
|
data.total.outbps = Math.max(0, (data.total.outBytesTotal - $scope.connectionsTotal.outBytesTotal) / td);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
data.total.inbps = 0;
|
|
|
|
|
data.total.outbps = 0;
|
|
|
|
|
}
|
|
|
|
|
$scope.connectionsTotal = data.total;
|
|
|
|
|
|
2015-04-07 13:57:26 +01:00
|
|
|
data = data.connections;
|
2014-11-26 13:39:59 +01:00
|
|
|
for (id in data) {
|
|
|
|
|
if (!data.hasOwnProperty(id)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
try {
|
2015-03-10 23:45:43 +01:00
|
|
|
data[id].inbps = Math.max(0, (data[id].inBytesTotal - $scope.connections[id].inBytesTotal) / td);
|
|
|
|
|
data[id].outbps = Math.max(0, (data[id].outBytesTotal - $scope.connections[id].outBytesTotal) / td);
|
2014-11-26 13:39:59 +01:00
|
|
|
} catch (e) {
|
|
|
|
|
data[id].inbps = 0;
|
|
|
|
|
data[id].outbps = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$scope.connections = data;
|
|
|
|
|
console.log("refreshConnections", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshErrors() {
|
2015-04-07 13:46:39 +02:00
|
|
|
$http.get(urlbase + '/system/error').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.errors = data.errors;
|
|
|
|
|
console.log("refreshErrors", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshConfig() {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
updateLocalConfig(data);
|
|
|
|
|
console.log("refreshConfig", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/config/insync').success(function (data) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = data.configInSync;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function refreshNeed(folder) {
|
2015-04-25 22:53:44 +01:00
|
|
|
var url = urlbase + "/db/need?folder=" + encodeURIComponent(folder);
|
|
|
|
|
url += "&page=" + $scope.neededCurrentPage;
|
|
|
|
|
url += "&perpage=" + $scope.neededPageSize;
|
|
|
|
|
$http.get(url).success(function (data) {
|
2016-03-11 09:48:46 +00:00
|
|
|
if ($scope.neededFolder === folder) {
|
2014-11-26 13:39:59 +01:00
|
|
|
console.log("refreshNeed", folder, data);
|
2015-04-25 22:53:44 +01:00
|
|
|
parseNeeded(data);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
2015-04-25 22:53:44 +01:00
|
|
|
function needAction(file) {
|
|
|
|
|
var fDelete = 4096;
|
|
|
|
|
var fDirectory = 16384;
|
|
|
|
|
|
|
|
|
|
if ((file.flags & (fDelete + fDirectory)) === fDelete + fDirectory) {
|
|
|
|
|
return 'rmdir';
|
|
|
|
|
} else if ((file.flags & fDelete) === fDelete) {
|
|
|
|
|
return 'rm';
|
|
|
|
|
} else if ((file.flags & fDirectory) === fDirectory) {
|
|
|
|
|
return 'touch';
|
|
|
|
|
} else {
|
|
|
|
|
return 'sync';
|
|
|
|
|
}
|
2015-06-14 17:17:48 +03:00
|
|
|
}
|
2015-04-25 22:53:44 +01:00
|
|
|
|
|
|
|
|
function parseNeeded(data) {
|
|
|
|
|
var merged = [];
|
|
|
|
|
data.progress.forEach(function (item) {
|
|
|
|
|
item.type = "progress";
|
|
|
|
|
item.action = needAction(item);
|
|
|
|
|
merged.push(item);
|
|
|
|
|
});
|
|
|
|
|
data.queued.forEach(function (item) {
|
|
|
|
|
item.type = "queued";
|
|
|
|
|
item.action = needAction(item);
|
|
|
|
|
merged.push(item);
|
|
|
|
|
});
|
|
|
|
|
data.rest.forEach(function (item) {
|
|
|
|
|
item.type = "rest";
|
|
|
|
|
item.action = needAction(item);
|
|
|
|
|
merged.push(item);
|
|
|
|
|
});
|
|
|
|
|
$scope.needed = merged;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-20 13:16:54 +00:00
|
|
|
function pathJoin(base, name) {
|
|
|
|
|
base = expandTilde(base);
|
|
|
|
|
if (base[base.length - 1] !== $scope.system.pathSeparator) {
|
|
|
|
|
return base + $scope.system.pathSeparator + name;
|
|
|
|
|
}
|
|
|
|
|
return base + name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function expandTilde(path) {
|
|
|
|
|
if (path && path.trim().charAt(0) === '~') {
|
|
|
|
|
return $scope.system.tilde + path.trim().substring(1);
|
|
|
|
|
}
|
|
|
|
|
return path;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-10 15:31:25 +00:00
|
|
|
function shouldSetDefaultFolderPath() {
|
|
|
|
|
return $scope.config.options && $scope.config.options.defaultFolderPath && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-15 20:01:56 +00:00
|
|
|
function resetRemoteNeed() {
|
|
|
|
|
$scope.remoteNeed = {};
|
|
|
|
|
$scope.remoteNeedFolders = [];
|
|
|
|
|
$scope.remoteNeedDevice = undefined;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-17 13:50:43 +01:00
|
|
|
function saveIgnores(ignores, cb) {
|
|
|
|
|
$http.post(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id), {
|
|
|
|
|
ignore: ignores
|
|
|
|
|
}).success(function () {
|
|
|
|
|
if (cb) {
|
|
|
|
|
cb();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-04-25 22:53:44 +01:00
|
|
|
$scope.neededPageChanged = function (page) {
|
|
|
|
|
$scope.neededCurrentPage = page;
|
|
|
|
|
refreshNeed($scope.neededFolder);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.neededChangePageSize = function (perpage) {
|
|
|
|
|
$scope.neededPageSize = perpage;
|
|
|
|
|
refreshNeed($scope.neededFolder);
|
2015-06-14 17:17:48 +03:00
|
|
|
};
|
2015-04-25 22:53:44 +01:00
|
|
|
|
2018-01-14 17:01:06 +00:00
|
|
|
$scope.refreshFailed = function (page, perpage) {
|
|
|
|
|
var url = urlbase + '/folder/pullerrors?folder=' + encodeURIComponent($scope.failed.folder);
|
|
|
|
|
url += "&page=" + page + "&perpage=" + perpage;
|
|
|
|
|
$http.get(url).success(function (data) {
|
|
|
|
|
$scope.failed = data;
|
|
|
|
|
}).error($scope.emitHTTPError);
|
2015-06-26 14:22:52 +02:00
|
|
|
};
|
|
|
|
|
|
2017-12-15 20:01:56 +00:00
|
|
|
$scope.refreshRemoteNeed = function (folder, page, perpage) {
|
|
|
|
|
var url = urlbase + '/db/remoteneed?device=' + $scope.remoteNeedDevice.deviceID;
|
|
|
|
|
url += '&folder=' + encodeURIComponent(folder);
|
|
|
|
|
url += "&page=" + page + "&perpage=" + perpage;
|
|
|
|
|
$http.get(url).success(function (data) {
|
|
|
|
|
if ($scope.remoteNeedDevice !== '') {
|
|
|
|
|
$scope.remoteNeed[folder] = data;
|
|
|
|
|
}
|
|
|
|
|
}).error(function (err) {
|
|
|
|
|
$scope.remoteNeed[folder] = undefined;
|
|
|
|
|
$scope.emitHTTPError(err);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-11 09:59:04 +01:00
|
|
|
$scope.refreshLocalChanged = function (page, perpage) {
|
|
|
|
|
var url = urlbase + '/db/localchanged?folder=';
|
|
|
|
|
url += encodeURIComponent($scope.localChanged.folder);
|
|
|
|
|
url += "&page=" + page + "&perpage=" + perpage;
|
|
|
|
|
$http.get(url).success(function (data) {
|
|
|
|
|
$scope.localChanged = data;
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
var refreshDeviceStats = debounce(function () {
|
|
|
|
|
$http.get(urlbase + "/stats/device").success(function (data) {
|
2014-12-07 20:21:12 +00:00
|
|
|
$scope.deviceStats = data;
|
|
|
|
|
for (var device in $scope.deviceStats) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.deviceStats[device].lastSeen = new Date($scope.deviceStats[device].lastSeen);
|
|
|
|
|
$scope.deviceStats[device].lastSeenDays = (new Date() - $scope.deviceStats[device].lastSeen) / 1000 / 86400;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
console.log("refreshDeviceStats", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2015-03-26 23:26:51 +01:00
|
|
|
}, 2500);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2014-12-07 20:21:12 +00:00
|
|
|
var refreshFolderStats = debounce(function () {
|
|
|
|
|
$http.get(urlbase + "/stats/folder").success(function (data) {
|
|
|
|
|
$scope.folderStats = data;
|
|
|
|
|
for (var folder in $scope.folderStats) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.folderStats[folder].lastFile) {
|
|
|
|
|
$scope.folderStats[folder].lastFile.at = new Date($scope.folderStats[folder].lastFile.at);
|
2014-12-07 20:21:12 +00:00
|
|
|
}
|
2016-06-02 19:26:52 +00:00
|
|
|
|
|
|
|
|
$scope.folderStats[folder].lastScan = new Date($scope.folderStats[folder].lastScan);
|
|
|
|
|
$scope.folderStats[folder].lastScanDays = (new Date() - $scope.folderStats[folder].lastScan) / 1000 / 86400;
|
2014-12-07 20:21:12 +00:00
|
|
|
}
|
|
|
|
|
console.log("refreshfolderStats", data);
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2015-03-26 23:26:51 +01:00
|
|
|
}, 2500);
|
2014-12-07 20:21:12 +00:00
|
|
|
|
2016-06-07 07:46:45 +00:00
|
|
|
var refreshThemes = debounce(function () {
|
|
|
|
|
$http.get("themes.json").success(function (data) { // no urlbase here as this is served by the asset handler
|
|
|
|
|
$scope.themes = data.themes;
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
}, 2500);
|
|
|
|
|
|
2016-12-21 16:35:20 +00:00
|
|
|
var refreshGlobalChanges = debounce(function () {
|
2016-12-30 01:33:27 +00:00
|
|
|
$http.get(urlbase + "/events/disk?limit=25").success(function (data) {
|
2016-12-21 16:35:20 +00:00
|
|
|
data = data.reverse();
|
|
|
|
|
$scope.globalChangeEvents = data;
|
|
|
|
|
console.log("refreshGlobalChanges", data);
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
}, 2500);
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.refresh = function () {
|
|
|
|
|
refreshSystem();
|
2016-06-26 10:47:23 +00:00
|
|
|
refreshDiscoveryCache();
|
2014-11-26 13:39:59 +01:00
|
|
|
refreshConnectionStats();
|
|
|
|
|
refreshErrors();
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-29 09:18:52 +01:00
|
|
|
$scope.folderStatus = function (folderCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if (typeof $scope.model[folderCfg.id] === 'undefined') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-21 18:41:25 +00:00
|
|
|
if (folderCfg.paused) {
|
|
|
|
|
return 'paused';
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
// after restart syncthing process state may be empty
|
|
|
|
|
if (!$scope.model[folderCfg.id].state) {
|
|
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var state = '' + $scope.model[folderCfg.id].state;
|
|
|
|
|
if (state === 'error') {
|
2015-04-13 05:12:01 +09:00
|
|
|
return 'stopped'; // legacy, the state is called "stopped" in the GUI
|
|
|
|
|
}
|
2018-12-11 09:59:04 +01:00
|
|
|
if (state === 'idle' && $scope.model[folderCfg.id].needTotalItems > 0) {
|
2015-06-26 14:22:52 +02:00
|
|
|
return 'outofsync';
|
2015-06-10 16:48:16 +03:00
|
|
|
}
|
2016-06-02 00:17:48 +00:00
|
|
|
if (state === 'scanning') {
|
2016-06-07 07:46:45 +00:00
|
|
|
return state;
|
2016-06-02 00:17:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (folderCfg.devices.length <= 1) {
|
|
|
|
|
return 'unshared';
|
|
|
|
|
}
|
2015-04-13 05:12:01 +09:00
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
return state;
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2014-11-29 09:18:52 +01:00
|
|
|
$scope.folderClass = function (folderCfg) {
|
2015-06-26 14:22:52 +02:00
|
|
|
var status = $scope.folderStatus(folderCfg);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2016-03-11 09:48:46 +00:00
|
|
|
if (status === 'idle') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'success';
|
|
|
|
|
}
|
2016-12-21 18:41:25 +00:00
|
|
|
if (status == 'paused') {
|
|
|
|
|
return 'default';
|
|
|
|
|
}
|
2016-03-11 09:48:46 +00:00
|
|
|
if (status === 'syncing' || status === 'scanning') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'primary';
|
|
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
if (status === 'unknown') {
|
|
|
|
|
return 'info';
|
|
|
|
|
}
|
|
|
|
|
if (status === 'stopped' || status === 'outofsync' || status === 'error') {
|
2015-04-13 05:12:01 +09:00
|
|
|
return 'danger';
|
|
|
|
|
}
|
2018-12-05 07:40:05 +00:00
|
|
|
if (status === 'unshared' || status === 'scan-waiting') {
|
2016-06-02 18:34:25 +00:00
|
|
|
return 'warning';
|
|
|
|
|
}
|
2015-06-26 14:22:52 +02:00
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'info';
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.syncPercentage = function (folder) {
|
|
|
|
|
if (typeof $scope.model[folder] === 'undefined') {
|
|
|
|
|
return 100;
|
|
|
|
|
}
|
|
|
|
|
if ($scope.model[folder].globalBytes === 0) {
|
|
|
|
|
return 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var pct = 100 * $scope.model[folder].inSyncBytes / $scope.model[folder].globalBytes;
|
|
|
|
|
return Math.floor(pct);
|
|
|
|
|
};
|
|
|
|
|
|
2017-01-26 09:39:48 +00:00
|
|
|
$scope.syncRemaining = function (folder) {
|
|
|
|
|
// Remaining sync bytes
|
|
|
|
|
if (typeof $scope.model[folder] === 'undefined') {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if ($scope.model[folder].globalBytes === 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var bytes = $scope.model[folder].globalBytes - $scope.model[folder].inSyncBytes;
|
|
|
|
|
if (isNaN(bytes) || bytes < 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return bytes;
|
|
|
|
|
};
|
|
|
|
|
|
2015-08-26 23:49:06 +01:00
|
|
|
$scope.scanPercentage = function (folder) {
|
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
var pct = 100 * $scope.scanProgress[folder].current / $scope.scanProgress[folder].total;
|
|
|
|
|
return Math.floor(pct);
|
2016-03-11 09:48:46 +00:00
|
|
|
};
|
2015-08-26 23:49:06 +01:00
|
|
|
|
2015-11-17 21:08:36 +01:00
|
|
|
$scope.scanRate = function (folder) {
|
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return $scope.scanProgress[folder].rate;
|
2016-03-11 09:48:46 +00:00
|
|
|
};
|
2015-11-17 21:08:36 +01:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
$scope.scanRemaining = function (folder) {
|
|
|
|
|
// Formats the remaining scan time as a string. Includes days and
|
|
|
|
|
// hours only when relevant, resulting in time stamps like:
|
|
|
|
|
// 00m 40s
|
|
|
|
|
// 32m 40s
|
|
|
|
|
// 2h 32m
|
|
|
|
|
// 4d 2h
|
|
|
|
|
|
|
|
|
|
if (!$scope.scanProgress[folder]) {
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
// Calculate remaining bytes and seconds based on our current
|
|
|
|
|
// rate.
|
2016-03-11 09:48:46 +00:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
var remainingBytes = $scope.scanProgress[folder].total - $scope.scanProgress[folder].current;
|
|
|
|
|
var seconds = remainingBytes / $scope.scanProgress[folder].rate;
|
|
|
|
|
// Round up to closest ten seconds to avoid flapping too much to
|
|
|
|
|
// and fro.
|
2016-03-11 09:48:46 +00:00
|
|
|
|
2015-11-18 10:57:11 +01:00
|
|
|
seconds = Math.ceil(seconds / 10) * 10;
|
|
|
|
|
|
|
|
|
|
// Separate out the number of days.
|
|
|
|
|
var days = 0;
|
2016-03-11 09:48:46 +00:00
|
|
|
var res = [];
|
2015-11-18 10:57:11 +01:00
|
|
|
if (seconds >= 86400) {
|
|
|
|
|
days = Math.floor(seconds / 86400);
|
|
|
|
|
res.push('' + days + 'd')
|
|
|
|
|
seconds = seconds % 86400;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Separate out the number of hours.
|
|
|
|
|
var hours = 0;
|
|
|
|
|
if (seconds > 3600) {
|
|
|
|
|
hours = Math.floor(seconds / 3600);
|
|
|
|
|
res.push('' + hours + 'h')
|
|
|
|
|
seconds = seconds % 3600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var d = new Date(1970, 0, 1).setSeconds(seconds);
|
|
|
|
|
|
2016-03-11 09:48:46 +00:00
|
|
|
if (days === 0) {
|
2015-11-18 10:57:11 +01:00
|
|
|
// Format minutes only if we're within a day of completion.
|
|
|
|
|
var f = $filter('date')(d, "m'm'");
|
|
|
|
|
res.push(f);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-11 09:48:46 +00:00
|
|
|
if (days === 0 && hours === 0) {
|
2015-11-18 10:57:11 +01:00
|
|
|
// Format seconds only when we're within an hour of completion.
|
|
|
|
|
var f = $filter('date')(d, "ss's'");
|
|
|
|
|
res.push(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return res.join(' ');
|
2016-03-11 09:48:46 +00:00
|
|
|
};
|
2015-11-18 10:57:11 +01:00
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
$scope.deviceStatus = function (deviceCfg) {
|
|
|
|
|
if ($scope.deviceFolders(deviceCfg).length === 0) {
|
|
|
|
|
return 'unused';
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
|
|
|
|
|
return 'unknown';
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-21 18:41:25 +00:00
|
|
|
if (deviceCfg.paused) {
|
2015-08-23 21:56:10 +02:00
|
|
|
return 'paused';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
|
2014-11-29 09:42:16 +01:00
|
|
|
return 'insync';
|
|
|
|
|
} else {
|
|
|
|
|
return 'syncing';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Disconnected
|
|
|
|
|
return 'disconnected';
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceClass = function (deviceCfg) {
|
2014-11-29 09:42:16 +01:00
|
|
|
if ($scope.deviceFolders(deviceCfg).length === 0) {
|
|
|
|
|
// Unused
|
|
|
|
|
return 'warning';
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-23 21:56:10 +02:00
|
|
|
if (typeof $scope.connections[deviceCfg.deviceID] === 'undefined') {
|
|
|
|
|
return 'info';
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-21 18:41:25 +00:00
|
|
|
if (deviceCfg.paused) {
|
2015-08-23 21:56:10 +02:00
|
|
|
return 'default';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($scope.connections[deviceCfg.deviceID].connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.completion[deviceCfg.deviceID] && $scope.completion[deviceCfg.deviceID]._total === 100) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'success';
|
|
|
|
|
} else {
|
|
|
|
|
return 'primary';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
// Disconnected
|
2014-11-26 13:39:59 +01:00
|
|
|
return 'info';
|
|
|
|
|
};
|
|
|
|
|
|
2016-06-01 19:06:36 +00:00
|
|
|
$scope.syncthingStatus = function () {
|
|
|
|
|
var syncCount = 0;
|
|
|
|
|
var notifyCount = 0;
|
|
|
|
|
var pauseCount = 0;
|
2016-06-01 20:24:43 +00:00
|
|
|
|
2016-06-01 19:06:36 +00:00
|
|
|
// loop through all folders
|
|
|
|
|
var folderListCache = $scope.folderList();
|
|
|
|
|
for (var i = 0; i < folderListCache.length; i++) {
|
|
|
|
|
var status = $scope.folderStatus(folderListCache[i]);
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 'syncing':
|
|
|
|
|
syncCount++;
|
|
|
|
|
break;
|
|
|
|
|
case 'stopped':
|
|
|
|
|
case 'unknown':
|
|
|
|
|
case 'outofsync':
|
|
|
|
|
case 'error':
|
|
|
|
|
notifyCount++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// loop through all devices
|
|
|
|
|
var deviceCount = $scope.devices.length;
|
2018-08-25 11:36:10 +01:00
|
|
|
var pendingFolders = 0;
|
2016-06-01 19:06:36 +00:00
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
|
var status = $scope.deviceStatus({
|
2018-12-25 14:26:46 +01:00
|
|
|
deviceID: $scope.devices[i].deviceID
|
2016-06-01 19:06:36 +00:00
|
|
|
});
|
|
|
|
|
switch (status) {
|
|
|
|
|
case 'unknown':
|
|
|
|
|
notifyCount++;
|
|
|
|
|
break;
|
|
|
|
|
case 'paused':
|
|
|
|
|
pauseCount++;
|
|
|
|
|
break;
|
|
|
|
|
case 'unused':
|
|
|
|
|
deviceCount--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2018-08-25 11:36:10 +01:00
|
|
|
pendingFolders += $scope.devices[i].pendingFolders.length;
|
2016-06-01 19:06:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// enumerate notifications
|
2018-08-25 11:36:10 +01:00
|
|
|
if ($scope.openNoAuth || !$scope.configInSync || $scope.errorList().length > 0 || !online || (
|
|
|
|
|
!isEmptyObject($scope.config) && ($scope.config.pendingDevices.length > 0 || pendingFolders > 0)
|
|
|
|
|
)) {
|
2016-06-01 19:06:36 +00:00
|
|
|
notifyCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// at least one folder is syncing
|
|
|
|
|
if (syncCount > 0) {
|
|
|
|
|
return 'sync';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// a device is unknown or a folder is stopped/unknown/outofsync/error or some other notification is open or gui offline
|
|
|
|
|
if (notifyCount > 0) {
|
|
|
|
|
return 'notify';
|
|
|
|
|
}
|
2016-06-01 20:24:43 +00:00
|
|
|
|
2016-06-01 19:06:36 +00:00
|
|
|
// all used devices are paused except (this) one
|
2018-12-25 14:26:46 +01:00
|
|
|
if (pauseCount === deviceCount - 1) {
|
2016-06-01 19:06:36 +00:00
|
|
|
return 'pause';
|
|
|
|
|
}
|
2016-06-01 20:24:43 +00:00
|
|
|
|
2016-06-01 19:06:36 +00:00
|
|
|
return 'default';
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceAddr = function (deviceCfg) {
|
2015-03-10 23:45:43 +01:00
|
|
|
var conn = $scope.connections[deviceCfg.deviceID];
|
2015-08-23 21:56:10 +02:00
|
|
|
if (conn && conn.connected) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return conn.address;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
return '?';
|
|
|
|
|
};
|
|
|
|
|
|
2016-12-21 16:35:20 +00:00
|
|
|
$scope.friendlyNameFromShort = function (shortID) {
|
|
|
|
|
var matches = $scope.devices.filter(function (n) {
|
|
|
|
|
return n.deviceID.substr(0, 7) === shortID;
|
|
|
|
|
});
|
|
|
|
|
if (matches.length !== 1) {
|
|
|
|
|
return shortID;
|
|
|
|
|
}
|
|
|
|
|
return matches[0].name;
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.friendlyNameFromID = function (deviceID) {
|
|
|
|
|
var match = $scope.findDevice(deviceID);
|
|
|
|
|
if (match) {
|
|
|
|
|
return $scope.deviceName(match);
|
|
|
|
|
}
|
|
|
|
|
return deviceID.substr(0, 6);
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.findDevice = function (deviceID) {
|
|
|
|
|
var matches = $scope.devices.filter(function (n) {
|
2016-03-11 09:48:46 +00:00
|
|
|
return n.deviceID === deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2016-03-11 09:48:46 +00:00
|
|
|
if (matches.length !== 1) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
return matches[0];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.deviceName = function (deviceCfg) {
|
2016-04-05 06:36:53 +00:00
|
|
|
if (typeof deviceCfg === 'undefined' || typeof deviceCfg.deviceID === 'undefined') {
|
2014-11-26 13:39:59 +01:00
|
|
|
return "";
|
|
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
if (deviceCfg.name) {
|
|
|
|
|
return deviceCfg.name;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
return deviceCfg.deviceID.substr(0, 6);
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.thisDeviceName = function () {
|
|
|
|
|
var device = $scope.thisDevice();
|
|
|
|
|
if (typeof device === 'undefined') {
|
|
|
|
|
return "(unknown device)";
|
|
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
if (device.name) {
|
|
|
|
|
return device.name;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
return device.deviceID.substr(0, 6);
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2016-12-21 18:41:25 +00:00
|
|
|
$scope.setDevicePause = function (device, pause) {
|
|
|
|
|
$scope.devices.forEach(function (cfg) {
|
|
|
|
|
if (cfg.deviceID == device) {
|
|
|
|
|
cfg.paused = pause;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$scope.config.devices = $scope.devices;
|
|
|
|
|
$scope.saveConfig();
|
2015-08-23 21:56:10 +02:00
|
|
|
};
|
|
|
|
|
|
2016-12-21 18:41:25 +00:00
|
|
|
$scope.setFolderPause = function (folder, pause) {
|
|
|
|
|
var cfg = $scope.folders[folder];
|
|
|
|
|
if (cfg) {
|
|
|
|
|
cfg.paused = pause;
|
|
|
|
|
$scope.config.folders = folderList($scope.folders);
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
}
|
2015-08-23 21:56:10 +02:00
|
|
|
};
|
|
|
|
|
|
2017-03-20 13:55:08 +00:00
|
|
|
$scope.showDiscoveryFailures = function () {
|
|
|
|
|
$('#discovery-failures').modal();
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-24 22:26:05 +00:00
|
|
|
$scope.logging = {
|
|
|
|
|
facilities: {},
|
2018-12-25 14:26:46 +01:00
|
|
|
refreshFacilities: function () {
|
2017-12-24 22:26:05 +00:00
|
|
|
$http.get(urlbase + '/system/debug').success(function (data) {
|
|
|
|
|
var facilities = {};
|
|
|
|
|
data.enabled = data.enabled || [];
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each(data.facilities, function (key, value) {
|
2017-12-24 22:26:05 +00:00
|
|
|
facilities[key] = {
|
|
|
|
|
description: value,
|
|
|
|
|
enabled: data.enabled.indexOf(key) > -1
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
$scope.logging.facilities = facilities;
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
show: function () {
|
2017-12-24 22:26:05 +00:00
|
|
|
$scope.logging.refreshFacilities();
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.logging.timer = $timeout($scope.logging.fetch);
|
2018-06-18 12:27:54 +01:00
|
|
|
var textArea = $('#logViewerText');
|
|
|
|
|
textArea.on("scroll", $scope.logging.onScroll);
|
2018-12-25 14:26:46 +01:00
|
|
|
$('#logViewer').modal().one('shown.bs.modal', function () {
|
2018-06-18 12:27:54 +01:00
|
|
|
// Scroll to bottom.
|
|
|
|
|
textArea.scrollTop(textArea[0].scrollHeight);
|
2018-07-29 20:15:24 +01:00
|
|
|
}).one('hidden.bs.modal', function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
$timeout.cancel($scope.logging.timer);
|
2018-06-18 12:27:54 +01:00
|
|
|
textArea.off("scroll", $scope.logging.onScroll);
|
2017-12-24 22:26:05 +00:00
|
|
|
$scope.logging.timer = null;
|
|
|
|
|
$scope.logging.entries = [];
|
|
|
|
|
});
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
onFacilityChange: function (facility) {
|
2017-12-24 22:26:05 +00:00
|
|
|
var enabled = $scope.logging.facilities[facility].enabled;
|
|
|
|
|
// Disable checkboxes while we're in flight.
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.logging.facilities, function (key) {
|
2017-12-24 22:26:05 +00:00
|
|
|
$scope.logging.facilities[key].enabled = null;
|
|
|
|
|
})
|
2018-12-25 14:26:46 +01:00
|
|
|
$http.post(urlbase + '/system/debug?' + (enabled ? 'enable=' : 'disable=') + facility)
|
2017-12-24 22:26:05 +00:00
|
|
|
.success($scope.logging.refreshFacilities)
|
|
|
|
|
.error($scope.emitHTTPError);
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
onScroll: function () {
|
2018-06-18 12:27:54 +01:00
|
|
|
var textArea = $('#logViewerText');
|
|
|
|
|
var scrollTop = textArea.prop('scrollTop');
|
|
|
|
|
var scrollHeight = textArea.prop('scrollHeight');
|
|
|
|
|
$scope.logging.paused = scrollHeight > (scrollTop + textArea.outerHeight());
|
|
|
|
|
// Browser events do not cause redraw, trigger manually.
|
|
|
|
|
$scope.$apply();
|
|
|
|
|
},
|
2017-12-24 22:26:05 +00:00
|
|
|
timer: null,
|
|
|
|
|
entries: [],
|
|
|
|
|
paused: false,
|
2018-12-25 14:26:46 +01:00
|
|
|
content: function () {
|
2017-12-24 22:26:05 +00:00
|
|
|
var content = "";
|
|
|
|
|
$.each($scope.logging.entries, function (idx, entry) {
|
|
|
|
|
content += entry.when.split('.')[0].replace('T', ' ') + ' ' + entry.message + "\n";
|
|
|
|
|
});
|
|
|
|
|
return content;
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
fetch: function () {
|
2017-12-24 22:26:05 +00:00
|
|
|
var textArea = $('#logViewerText');
|
2018-06-18 12:27:54 +01:00
|
|
|
if ($scope.logging.paused) {
|
2017-12-24 22:26:05 +00:00
|
|
|
if (!$scope.logging.timer) return;
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.logging.timer = $timeout($scope.logging.fetch, 500);
|
2017-12-24 22:26:05 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var last = null;
|
|
|
|
|
if ($scope.logging.entries.length > 0) {
|
2018-12-25 14:26:46 +01:00
|
|
|
last = $scope.logging.entries[$scope.logging.entries.length - 1].when;
|
2017-12-24 22:26:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$http.get(urlbase + '/system/log' + (last ? '?since=' + encodeURIComponent(last) : '')).success(function (data) {
|
|
|
|
|
if (!$scope.logging.timer) return;
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.logging.timer = $timeout($scope.logging.fetch, 2000);
|
2018-06-18 12:27:54 +01:00
|
|
|
if (!$scope.logging.paused) {
|
2017-12-24 22:26:05 +00:00
|
|
|
if (data.messages) {
|
|
|
|
|
$scope.logging.entries.push.apply($scope.logging.entries, data.messages);
|
2018-06-18 12:27:54 +01:00
|
|
|
// Wait for the text area to be redrawn, adding new lines, and then scroll to bottom.
|
2018-12-25 14:26:46 +01:00
|
|
|
$timeout(function () {
|
2018-06-18 12:27:54 +01:00
|
|
|
textArea.scrollTop(textArea[0].scrollHeight);
|
|
|
|
|
});
|
2017-12-24 22:26:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.editSettings = function () {
|
|
|
|
|
// Make a working copy
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.tmpOptions = angular.copy($scope.config.options);
|
|
|
|
|
$scope.tmpOptions.deviceName = $scope.thisDevice().name;
|
2017-01-27 12:17:06 +00:00
|
|
|
$scope.tmpOptions.upgrades = "none";
|
|
|
|
|
if ($scope.tmpOptions.autoUpgradeIntervalH > 0) {
|
|
|
|
|
$scope.tmpOptions.upgrades = "stable";
|
|
|
|
|
}
|
|
|
|
|
if ($scope.tmpOptions.upgradeToPreReleases) {
|
|
|
|
|
$scope.tmpOptions.upgrades = "candidate";
|
|
|
|
|
}
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.tmpGUI = angular.copy($scope.config.gui);
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.tmpRemoteIgnoredDevices = angular.copy($scope.config.remoteIgnoredDevices);
|
|
|
|
|
$scope.tmpDevices = angular.copy($scope.config.devices);
|
|
|
|
|
var settingsModal = $('#settings').modal();
|
|
|
|
|
settingsModal.one('hidden.bs.modal', function () {
|
|
|
|
|
$('.nav-tabs a[href="#settings-general"]').tab('show');
|
2018-03-17 13:50:43 +01:00
|
|
|
window.location.hash = "";
|
2018-08-25 11:36:10 +01:00
|
|
|
settingsModal.off('hide.bs.modal');
|
|
|
|
|
}).on('hide.bs.modal', function (e) {
|
|
|
|
|
if ($scope.settingsModified()) {
|
2018-12-25 14:26:46 +01:00
|
|
|
$("#discard-changes-confirmation").modal().one('hidden.bs.modal', function () {
|
2018-08-25 11:36:10 +01:00
|
|
|
if (!$scope.settingsModified()) {
|
|
|
|
|
settingsModal.modal('hide');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
e.stopImmediatePropagation();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-03-17 13:50:43 +01:00
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2017-04-24 08:51:08 +09:00
|
|
|
$scope.saveConfig = function (cb) {
|
2014-11-26 13:39:59 +01:00
|
|
|
var cfg = JSON.stringify($scope.config);
|
|
|
|
|
var opts = {
|
|
|
|
|
headers: {
|
|
|
|
|
'Content-Type': 'application/json'
|
|
|
|
|
}
|
|
|
|
|
};
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/config', cfg, opts).success(function () {
|
2019-01-13 23:28:17 +00:00
|
|
|
refreshConfig();
|
|
|
|
|
if (cb) {
|
|
|
|
|
cb();
|
|
|
|
|
}
|
|
|
|
|
}).error(function (data, status, headers, config) {
|
|
|
|
|
refreshConfig();
|
|
|
|
|
$scope.emitHTTPError(data, status, headers, config);
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.urVersions = function () {
|
2017-10-12 06:16:46 +00:00
|
|
|
var result = [];
|
|
|
|
|
if ($scope.system) {
|
|
|
|
|
for (var i = $scope.system.urVersionMax; i >= 2; i--) {
|
|
|
|
|
result.push("" + i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.settingsModified = function () {
|
|
|
|
|
// Options has artificial properties injected into the temp config.
|
|
|
|
|
// Need to recompute them before we can check equality
|
|
|
|
|
var options = angular.copy($scope.config.options);
|
|
|
|
|
options.deviceName = $scope.thisDevice().name;
|
|
|
|
|
options.upgrades = "none";
|
|
|
|
|
if (options.autoUpgradeIntervalH > 0) {
|
|
|
|
|
options.upgrades = "stable";
|
|
|
|
|
}
|
|
|
|
|
if (options.upgradeToPreReleases) {
|
|
|
|
|
options.upgrades = "candidate";
|
|
|
|
|
}
|
|
|
|
|
var optionsEqual = angular.equals(options, $scope.tmpOptions);
|
|
|
|
|
var guiEquals = angular.equals($scope.config.gui, $scope.tmpGUI);
|
|
|
|
|
var ignoredDevicesEquals = angular.equals($scope.config.remoteIgnoredDevices, $scope.tmpRemoteIgnoredDevices);
|
|
|
|
|
var ignoredFoldersEquals = angular.equals($scope.config.devices, $scope.tmpDevices);
|
|
|
|
|
console.log("settings equals - options: " + optionsEqual + " gui: " + guiEquals + " ignDev: " + ignoredDevicesEquals + " ignFol: " + ignoredFoldersEquals);
|
|
|
|
|
return !optionsEqual || !guiEquals || !ignoredDevicesEquals || !ignoredFoldersEquals;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveSettings = function () {
|
|
|
|
|
// Make sure something changed
|
2018-08-25 11:36:10 +01:00
|
|
|
if ($scope.settingsModified()) {
|
|
|
|
|
var themeChanged = $scope.config.gui.theme !== $scope.tmpGUI.theme;
|
2017-10-12 06:16:46 +00:00
|
|
|
// Angular has issues with selects with numeric values, so we handle strings here.
|
|
|
|
|
$scope.tmpOptions.urAccepted = parseInt($scope.tmpOptions._urAcceptedStr);
|
2017-01-27 12:17:06 +00:00
|
|
|
// Check if auto-upgrade has been enabled or disabled. This
|
|
|
|
|
// also has an effect on usage reporting, so do the check
|
|
|
|
|
// for that later.
|
|
|
|
|
if ($scope.tmpOptions.upgrades == "candidate") {
|
|
|
|
|
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
|
|
|
|
|
$scope.tmpOptions.upgradeToPreReleases = true;
|
2017-10-12 06:16:46 +00:00
|
|
|
$scope.tmpOptions.urAccepted = $scope.system.urVersionMax;
|
|
|
|
|
$scope.tmpOptions.urSeen = $scope.system.urVersionMax;
|
2017-01-27 12:17:06 +00:00
|
|
|
} else if ($scope.tmpOptions.upgrades == "stable") {
|
|
|
|
|
$scope.tmpOptions.autoUpgradeIntervalH = $scope.tmpOptions.autoUpgradeIntervalH || 12;
|
|
|
|
|
$scope.tmpOptions.upgradeToPreReleases = false;
|
|
|
|
|
} else {
|
|
|
|
|
$scope.tmpOptions.autoUpgradeIntervalH = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
// Check if protocol will need to be changed on restart
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.gui.useTLS !== $scope.tmpGUI.useTLS) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.protocolChanged = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Apply new settings locally
|
2018-10-10 20:14:24 +02:00
|
|
|
$scope.thisDeviceIn($scope.tmpDevices).name = $scope.tmpOptions.deviceName;
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.options = angular.copy($scope.tmpOptions);
|
|
|
|
|
$scope.config.gui = angular.copy($scope.tmpGUI);
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.config.remoteIgnoredDevices = angular.copy($scope.tmpRemoteIgnoredDevices);
|
|
|
|
|
$scope.config.devices = angular.copy($scope.tmpDevices);
|
2018-10-10 20:14:24 +02:00
|
|
|
// $scope.devices is updated by updateLocalConfig based on
|
|
|
|
|
// the config changed event, but settingsModified will look
|
|
|
|
|
// at it before that and conclude that the settings are
|
|
|
|
|
// modified (even though we just saved) unless we update
|
|
|
|
|
// here as well...
|
|
|
|
|
$scope.devices = $scope.config.devices;
|
2014-11-18 22:57:21 +00:00
|
|
|
|
2016-05-04 19:38:12 +00:00
|
|
|
['listenAddresses', 'globalAnnounceServers'].forEach(function (key) {
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.config.options[key] = $scope.config.options["_" + key + "Str"].split(/[ ,]+/).map(function (x) {
|
2014-11-18 22:57:21 +00:00
|
|
|
return x.trim();
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
2017-05-03 19:34:24 +02:00
|
|
|
$scope.saveConfig(function () {
|
|
|
|
|
if (themeChanged) {
|
|
|
|
|
document.location.reload(true);
|
|
|
|
|
}
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$('#settings').modal("hide");
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.saveAdvanced = function () {
|
2015-07-09 21:58:58 +01:00
|
|
|
$scope.config = $scope.advancedConfig;
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.saveConfig();
|
|
|
|
|
$('#advanced').modal("hide");
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.restart = function () {
|
|
|
|
|
restarting = true;
|
|
|
|
|
$('#restarting').modal();
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/restart');
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = true;
|
|
|
|
|
|
|
|
|
|
// Switch webpage protocol if needed
|
|
|
|
|
if ($scope.protocolChanged) {
|
|
|
|
|
var protocol = 'http';
|
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.config.gui.useTLS) {
|
2014-11-26 13:39:59 +01:00
|
|
|
protocol = 'https';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setTimeout(function () {
|
|
|
|
|
window.location.protocol = protocol;
|
|
|
|
|
}, 2500);
|
|
|
|
|
|
|
|
|
|
$scope.protocolChanged = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.upgrade = function () {
|
|
|
|
|
restarting = true;
|
2015-04-22 21:41:08 +09:00
|
|
|
$('#majorUpgrade').modal('hide');
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#upgrading').modal();
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/upgrade').success(function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#restarting').modal();
|
|
|
|
|
$('#upgrading').modal('hide');
|
|
|
|
|
}).error(function () {
|
|
|
|
|
$('#upgrading').modal('hide');
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.shutdown = function () {
|
|
|
|
|
restarting = true;
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/shutdown').success(function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#shutdown').modal();
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.configInSync = true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.editDevice = function (deviceCfg) {
|
|
|
|
|
$scope.currentDevice = $.extend({}, deviceCfg);
|
|
|
|
|
$scope.editingExisting = true;
|
2016-12-02 21:07:02 +00:00
|
|
|
$scope.willBeReintroducedBy = undefined;
|
2018-12-25 14:26:46 +01:00
|
|
|
if (deviceCfg.introducedBy) {
|
2016-12-02 21:07:02 +00:00
|
|
|
var introducerDevice = $scope.findDevice(deviceCfg.introducedBy);
|
|
|
|
|
if (introducerDevice && introducerDevice.introducer) {
|
|
|
|
|
$scope.willBeReintroducedBy = $scope.deviceName(introducerDevice);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.currentDevice._addressesStr = deviceCfg.addresses.join(', ');
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.currentDevice.selectedFolders = {};
|
|
|
|
|
$scope.deviceFolders($scope.currentDevice).forEach(function (folder) {
|
|
|
|
|
$scope.currentDevice.selectedFolders[folder] = true;
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.deviceEditor.$setPristine();
|
|
|
|
|
$('#editDevice').modal();
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-05 12:03:47 +01:00
|
|
|
$scope.selectAllFolders = function () {
|
|
|
|
|
angular.forEach($scope.folders, function (id) {
|
2018-11-07 11:44:52 +03:00
|
|
|
$scope.currentDevice.selectedFolders[id] = true;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-05 12:03:47 +01:00
|
|
|
$scope.deSelectAllFolders = function () {
|
|
|
|
|
angular.forEach($scope.folders, function (id) {
|
2018-11-07 11:44:52 +03:00
|
|
|
$scope.currentDevice.selectedFolders[id] = false;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2016-03-25 20:29:07 +00:00
|
|
|
$scope.addDevice = function (deviceID, name) {
|
2015-12-18 15:33:11 -05:00
|
|
|
return $http.get(urlbase + '/system/discovery')
|
2014-11-26 13:39:59 +01:00
|
|
|
.success(function (registry) {
|
2017-06-16 15:27:09 +00:00
|
|
|
$scope.discovery = [];
|
2017-06-29 13:47:42 +00:00
|
|
|
outer:
|
2017-06-16 15:27:09 +00:00
|
|
|
for (var id in registry) {
|
|
|
|
|
if ($scope.discovery.length === 5) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-06-29 13:47:42 +00:00
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
|
if ($scope.devices[i].deviceID === id) {
|
|
|
|
|
continue outer;
|
|
|
|
|
}
|
2017-06-16 15:27:09 +00:00
|
|
|
}
|
2017-06-29 13:47:42 +00:00
|
|
|
$scope.discovery.push(id);
|
2017-06-16 15:27:09 +00:00
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
})
|
|
|
|
|
.then(function () {
|
|
|
|
|
$scope.currentDevice = {
|
2016-03-25 20:29:07 +00:00
|
|
|
name: name,
|
2015-12-18 15:33:11 -05:00
|
|
|
deviceID: deviceID,
|
2015-07-03 14:07:38 +02:00
|
|
|
_addressesStr: 'dynamic',
|
2015-03-10 23:45:43 +01:00
|
|
|
compression: 'metadata',
|
|
|
|
|
introducer: false,
|
2018-09-13 21:52:16 +01:00
|
|
|
selectedFolders: {},
|
|
|
|
|
pendingFolders: [],
|
|
|
|
|
ignoredFolders: []
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
$scope.editingExisting = false;
|
|
|
|
|
$scope.deviceEditor.$setPristine();
|
|
|
|
|
$('#editDevice').modal();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.deleteDevice = function () {
|
|
|
|
|
$('#editDevice').modal('hide');
|
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.devices = $scope.devices.filter(function (n) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return n.deviceID !== $scope.currentDevice.deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.devices = $scope.devices;
|
2014-11-26 13:39:59 +01:00
|
|
|
|
|
|
|
|
for (var id in $scope.folders) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
|
|
|
|
return n.deviceID !== $scope.currentDevice.deviceID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.saveDevice = function () {
|
|
|
|
|
$('#editDevice').modal('hide');
|
2014-12-27 23:12:12 +00:00
|
|
|
$scope.saveDeviceConfig($scope.currentDevice);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.saveDeviceConfig = function (deviceCfg) {
|
2015-07-03 14:07:38 +02:00
|
|
|
deviceCfg.addresses = deviceCfg._addressesStr.split(',').map(function (x) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return x.trim();
|
|
|
|
|
});
|
|
|
|
|
|
2016-03-11 09:48:46 +00:00
|
|
|
var done = false;
|
|
|
|
|
for (var i = 0; i < $scope.devices.length && !done; i++) {
|
2015-03-10 23:45:43 +01:00
|
|
|
if ($scope.devices[i].deviceID === deviceCfg.deviceID) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.devices[i] = deviceCfg;
|
|
|
|
|
done = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!done) {
|
|
|
|
|
$scope.devices.push(deviceCfg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.devices.sort(deviceCompare);
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.devices = $scope.devices;
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
for (var id in deviceCfg.selectedFolders) {
|
|
|
|
|
if (deviceCfg.selectedFolders[id]) {
|
|
|
|
|
var found = false;
|
|
|
|
|
for (i = 0; i < $scope.folders[id].devices.length; i++) {
|
2016-03-11 09:48:46 +00:00
|
|
|
if ($scope.folders[id].devices[i].deviceID === deviceCfg.deviceID) {
|
2015-07-20 14:48:03 +02:00
|
|
|
found = true;
|
|
|
|
|
break;
|
2014-12-05 00:22:16 +00:00
|
|
|
}
|
2015-07-20 14:48:03 +02:00
|
|
|
}
|
2014-12-05 00:22:16 +00:00
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
if (!found) {
|
|
|
|
|
$scope.folders[id].devices.push({
|
|
|
|
|
deviceID: deviceCfg.deviceID
|
2014-12-05 00:22:16 +00:00
|
|
|
});
|
|
|
|
|
}
|
2015-07-20 14:48:03 +02:00
|
|
|
} else {
|
|
|
|
|
$scope.folders[id].devices = $scope.folders[id].devices.filter(function (n) {
|
2016-03-11 09:48:46 +00:00
|
|
|
return n.deviceID !== deviceCfg.deviceID;
|
2015-07-20 14:48:03 +02:00
|
|
|
});
|
2014-12-05 00:22:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.ignoreDevice = function (pendingDevice) {
|
|
|
|
|
pendingDevice = angular.copy(pendingDevice);
|
|
|
|
|
// Bump time
|
|
|
|
|
pendingDevice.time = (new Date()).toISOString();
|
|
|
|
|
$scope.config.remoteIgnoredDevices.push(pendingDevice);
|
|
|
|
|
$scope.saveConfig();
|
2014-12-27 23:12:12 +00:00
|
|
|
};
|
|
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.unignoreDeviceFromTemporaryConfig = function (ignoredDevice) {
|
|
|
|
|
$scope.tmpRemoteIgnoredDevices = $scope.tmpRemoteIgnoredDevices.filter(function (existingIgnoredDevice) {
|
|
|
|
|
return ignoredDevice.deviceID !== existingIgnoredDevice.deviceID;
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.ignoredFoldersCountTmpConfig = function () {
|
|
|
|
|
var count = 0;
|
|
|
|
|
($scope.tmpDevices || []).forEach(function (deviceCfg) {
|
|
|
|
|
count += deviceCfg.ignoredFolders.length;
|
|
|
|
|
});
|
|
|
|
|
return count;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.unignoreFolderFromTemporaryConfig = function (device, ignoredFolderID) {
|
|
|
|
|
for (var i = 0; i < $scope.tmpDevices.length; i++) {
|
|
|
|
|
if ($scope.tmpDevices[i].deviceID == device) {
|
|
|
|
|
$scope.tmpDevices[i].ignoredFolders = $scope.tmpDevices[i].ignoredFolders.filter(function (existingIgnoredFolder) {
|
|
|
|
|
return existingIgnoredFolder.id !== ignoredFolderID;
|
|
|
|
|
});
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-12-27 23:12:12 +00:00
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.otherDevices = function () {
|
|
|
|
|
return $scope.devices.filter(function (n) {
|
2015-03-10 23:45:43 +01:00
|
|
|
return n.deviceID !== $scope.myID;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.thisDevice = function () {
|
2018-10-10 20:14:24 +02:00
|
|
|
return $scope.thisDeviceIn($scope.devices);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.thisDeviceIn = function (l) {
|
|
|
|
|
for (var i = 0; i < l.length; i++) {
|
|
|
|
|
var n = l[i];
|
2015-03-10 23:45:43 +01:00
|
|
|
if (n.deviceID === $scope.myID) {
|
2014-11-26 13:39:59 +01:00
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.allDevices = function () {
|
|
|
|
|
var devices = $scope.otherDevices();
|
|
|
|
|
devices.push($scope.thisDevice());
|
|
|
|
|
return devices;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.errorList = function () {
|
2015-10-12 10:12:57 +09:00
|
|
|
if (!$scope.errors) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
return $scope.errors.filter(function (e) {
|
2015-10-12 14:18:40 +09:00
|
|
|
return e.when > $scope.seenError;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.clearErrors = function () {
|
2015-10-12 14:18:40 +09:00
|
|
|
$scope.seenError = $scope.errors[$scope.errors.length - 1].when;
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + '/system/error/clear');
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2018-06-11 15:47:54 +02:00
|
|
|
$scope.fsWatcherErrorMap = function () {
|
|
|
|
|
var errs = {}
|
|
|
|
|
$.each($scope.folders, function (id, cfg) {
|
|
|
|
|
if (cfg.fsWatcherEnabled && $scope.model[cfg.id] && $scope.model[id].watchError && !cfg.paused && $scope.folderStatus(cfg) !== 'stopped') {
|
|
|
|
|
errs[id] = $scope.model[id].watchError;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return errs;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.friendlyDevices = function (str) {
|
|
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
|
var cfg = $scope.devices[i];
|
2015-03-10 23:45:43 +01:00
|
|
|
str = str.replace(cfg.deviceID, $scope.deviceName(cfg));
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
return str;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.folderList = function () {
|
|
|
|
|
return folderList($scope.folders);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.directoryList = [];
|
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.$watch('currentFolder.path', function (newvalue) {
|
2017-07-20 13:16:54 +00:00
|
|
|
if (!newvalue) {
|
|
|
|
|
return;
|
2015-04-10 15:34:24 +08:00
|
|
|
}
|
2017-07-20 13:16:54 +00:00
|
|
|
$scope.currentFolder.path = expandTilde(newvalue);
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.get(urlbase + '/system/browse', {
|
2014-11-26 13:39:59 +01:00
|
|
|
params: { current: newvalue }
|
|
|
|
|
}).success(function (data) {
|
|
|
|
|
$scope.directoryList = data;
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
|
2017-07-20 13:16:54 +00:00
|
|
|
$scope.$watch('currentFolder.label', function (newvalue) {
|
2017-08-10 15:31:25 +00:00
|
|
|
if (!newvalue || !shouldSetDefaultFolderPath()) {
|
2017-07-20 13:16:54 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$scope.$watch('currentFolder.id', function (newvalue) {
|
2017-08-10 15:31:25 +00:00
|
|
|
if (!newvalue || !shouldSetDefaultFolderPath() || $scope.currentFolder.label) {
|
2017-07-20 13:16:54 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$scope.currentFolder.path = pathJoin($scope.config.options.defaultFolderPath, newvalue);
|
|
|
|
|
});
|
|
|
|
|
|
2018-05-17 09:21:10 +02:00
|
|
|
$scope.fsWatcherToggled = function () {
|
|
|
|
|
if ($scope.currentFolder.fsWatcherEnabled) {
|
2018-03-25 22:05:47 +02:00
|
|
|
$scope.currentFolder.rescanIntervalS = 3600;
|
|
|
|
|
} else {
|
|
|
|
|
$scope.currentFolder.rescanIntervalS = 60;
|
|
|
|
|
}
|
2018-05-17 09:21:10 +02:00
|
|
|
};
|
2018-03-25 22:05:47 +02:00
|
|
|
|
2016-06-12 14:06:48 +00:00
|
|
|
$scope.loadFormIntoScope = function (form) {
|
2018-12-25 14:26:46 +01:00
|
|
|
console.log('loadFormIntoScope', form.$name);
|
2016-06-12 14:06:48 +00:00
|
|
|
switch (form.$name) {
|
|
|
|
|
case 'deviceEditor':
|
|
|
|
|
$scope.deviceEditor = form;
|
|
|
|
|
break;
|
|
|
|
|
case 'folderEditor':
|
|
|
|
|
$scope.folderEditor = form;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2016-12-21 16:35:20 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.globalChanges = function () {
|
|
|
|
|
$('#globalChanges').modal();
|
|
|
|
|
};
|
2016-06-12 14:06:48 +00:00
|
|
|
|
2017-06-12 08:54:08 +02:00
|
|
|
$scope.editFolderModal = function () {
|
2017-06-09 05:41:19 +00:00
|
|
|
$scope.folderPathErrors = {};
|
|
|
|
|
$scope.folderEditor.$setPristine();
|
2018-07-29 20:15:24 +01:00
|
|
|
$('#editFolder').modal().one('shown.bs.tab', function (e) {
|
|
|
|
|
if (e.target.attributes.href.value === "#folder-ignores") {
|
|
|
|
|
$('#folder-ignores textarea').focus();
|
2018-03-17 13:50:43 +01:00
|
|
|
}
|
2018-07-29 20:15:24 +01:00
|
|
|
}).one('hidden.bs.modal', function () {
|
2018-08-25 11:36:10 +01:00
|
|
|
$('.nav-tabs a[href="#folder-general"]').tab('show');
|
2018-07-29 20:15:24 +01:00
|
|
|
window.location.hash = "";
|
2018-03-17 13:50:43 +01:00
|
|
|
});
|
2017-06-09 05:41:19 +00:00
|
|
|
};
|
|
|
|
|
|
2014-12-27 23:12:12 +00:00
|
|
|
$scope.editFolder = function (folderCfg) {
|
2017-07-20 13:16:54 +00:00
|
|
|
$scope.editingExisting = true;
|
2014-12-27 23:12:12 +00:00
|
|
|
$scope.currentFolder = angular.copy(folderCfg);
|
2018-06-05 23:47:47 +02:00
|
|
|
if ($scope.currentFolder.path.length > 1 && $scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
|
2014-12-14 23:12:12 +00:00
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.selectedDevices = {};
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.devices.forEach(function (n) {
|
|
|
|
|
$scope.currentFolder.selectedDevices[n.deviceID] = true;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
2015-06-12 13:04:00 +02:00
|
|
|
if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "trashcan") {
|
|
|
|
|
$scope.currentFolder.trashcanFileVersioning = true;
|
|
|
|
|
$scope.currentFolder.fileVersioningSelector = "trashcan";
|
|
|
|
|
$scope.currentFolder.trashcanClean = +$scope.currentFolder.versioning.params.cleanoutDays;
|
|
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "simple") {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.simpleFileVersioning = true;
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "simple";
|
|
|
|
|
$scope.currentFolder.simpleKeep = +$scope.currentFolder.versioning.params.keep;
|
|
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "staggered") {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.staggeredFileVersioning = true;
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "staggered";
|
|
|
|
|
$scope.currentFolder.staggeredMaxAge = Math.floor(+$scope.currentFolder.versioning.params.maxAge / 86400);
|
|
|
|
|
$scope.currentFolder.staggeredCleanInterval = +$scope.currentFolder.versioning.params.cleanInterval;
|
|
|
|
|
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.versioning.params.versionsPath;
|
2015-03-19 11:31:21 +01:00
|
|
|
} else if ($scope.currentFolder.versioning && $scope.currentFolder.versioning.type === "external") {
|
|
|
|
|
$scope.currentFolder.externalFileVersioning = true;
|
|
|
|
|
$scope.currentFolder.fileVersioningSelector = "external";
|
|
|
|
|
$scope.currentFolder.externalCommand = $scope.currentFolder.versioning.params.command;
|
2014-11-26 13:39:59 +01:00
|
|
|
} else {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.currentFolder.fileVersioningSelector = "none";
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2015-06-12 13:04:00 +02:00
|
|
|
$scope.currentFolder.trashcanClean = $scope.currentFolder.trashcanClean || 0; // weeds out nulls and undefineds
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.currentFolder.simpleKeep = $scope.currentFolder.simpleKeep || 5;
|
|
|
|
|
$scope.currentFolder.staggeredCleanInterval = $scope.currentFolder.staggeredCleanInterval || 3600;
|
|
|
|
|
$scope.currentFolder.staggeredVersionsPath = $scope.currentFolder.staggeredVersionsPath || "";
|
|
|
|
|
|
|
|
|
|
// staggeredMaxAge can validly be zero, which we should not replace
|
|
|
|
|
// with the default value of 365. So only set the default if it's
|
|
|
|
|
// actually undefined.
|
|
|
|
|
if (typeof $scope.currentFolder.staggeredMaxAge === 'undefined') {
|
|
|
|
|
$scope.currentFolder.staggeredMaxAge = 365;
|
|
|
|
|
}
|
2015-03-19 11:31:21 +01:00
|
|
|
$scope.currentFolder.externalCommand = $scope.currentFolder.externalCommand || "";
|
2015-03-21 15:33:31 +01:00
|
|
|
|
2018-03-18 01:42:31 +01:00
|
|
|
$('#folder-ignores textarea').val($translate.instant("Loading..."));
|
2018-03-17 13:50:43 +01:00
|
|
|
$('#folder-ignores textarea').attr('disabled', 'disabled');
|
|
|
|
|
$http.get(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id))
|
|
|
|
|
.success(function (data) {
|
|
|
|
|
$scope.currentFolder.ignores = data.ignore || [];
|
|
|
|
|
$('#folder-ignores textarea').val($scope.currentFolder.ignores.join('\n'));
|
|
|
|
|
$('#folder-ignores textarea').removeAttr('disabled');
|
2018-03-18 01:42:31 +01:00
|
|
|
})
|
|
|
|
|
.error(function (err) {
|
|
|
|
|
$('#folder-ignores textarea').val($translate.instant("Failed to load ignore patterns."));
|
|
|
|
|
$scope.emitHTTPError(err);
|
2018-03-17 13:50:43 +01:00
|
|
|
});
|
|
|
|
|
|
2017-06-09 05:41:19 +00:00
|
|
|
$scope.editFolderModal();
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.selectAllDevices = function () {
|
2018-11-07 11:44:52 +03:00
|
|
|
var devices = $scope.otherDevices();
|
2018-12-25 14:26:46 +01:00
|
|
|
for (var i = 0; i < devices.length; i++) {
|
2018-11-07 11:44:52 +03:00
|
|
|
$scope.currentFolder.selectedDevices[devices[i].deviceID] = true;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.deSelectAllDevices = function () {
|
2018-11-07 11:44:52 +03:00
|
|
|
var devices = $scope.otherDevices();
|
2018-12-25 14:26:46 +01:00
|
|
|
for (var i = 0; i < devices.length; i++) {
|
2018-11-07 11:44:52 +03:00
|
|
|
$scope.currentFolder.selectedDevices[devices[i].deviceID] = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.addFolder = function () {
|
2016-05-26 07:25:34 +00:00
|
|
|
$http.get(urlbase + '/svc/random/string?length=10').success(function (data) {
|
2017-07-20 13:16:54 +00:00
|
|
|
$scope.editingExisting = false;
|
2017-06-09 05:41:19 +00:00
|
|
|
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
2016-06-27 09:39:25 +00:00
|
|
|
$scope.currentFolder.id = (data.random.substr(0, 5) + '-' + data.random.substr(5, 5)).toLowerCase();
|
2018-03-17 13:50:43 +01:00
|
|
|
$('#folder-ignores textarea').val("");
|
|
|
|
|
$('#folder-ignores textarea').removeAttr('disabled');
|
2017-06-09 05:41:19 +00:00
|
|
|
$scope.editFolderModal();
|
2016-05-26 07:25:34 +00:00
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2016-03-11 09:48:46 +00:00
|
|
|
$scope.addFolderAndShare = function (folder, folderLabel, device) {
|
2017-07-20 13:16:54 +00:00
|
|
|
$scope.editingExisting = false;
|
2017-04-01 09:58:06 +00:00
|
|
|
$scope.currentFolder = angular.copy($scope.folderDefaults);
|
|
|
|
|
$scope.currentFolder.id = folder;
|
|
|
|
|
$scope.currentFolder.label = folderLabel;
|
|
|
|
|
$scope.currentFolder.viewFlags = {
|
|
|
|
|
importFromOtherDevice: true
|
2014-12-27 23:12:12 +00:00
|
|
|
};
|
|
|
|
|
$scope.currentFolder.selectedDevices[device] = true;
|
2018-03-17 13:50:43 +01:00
|
|
|
$('#folder-ignores textarea').val("");
|
|
|
|
|
$('#folder-ignores textarea').removeAttr('disabled');
|
2017-06-09 05:41:19 +00:00
|
|
|
$scope.editFolderModal();
|
2014-12-27 23:12:12 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.shareFolderWithDevice = function (folder, device) {
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folder].devices.push({
|
|
|
|
|
deviceID: device
|
2014-12-27 23:12:12 +00:00
|
|
|
});
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2014-12-27 23:12:12 +00:00
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveFolder = function () {
|
|
|
|
|
$('#editFolder').modal('hide');
|
2016-03-11 09:48:46 +00:00
|
|
|
var folderCfg = $scope.currentFolder;
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices = [];
|
2014-11-26 13:39:59 +01:00
|
|
|
folderCfg.selectedDevices[$scope.myID] = true;
|
|
|
|
|
for (var deviceID in folderCfg.selectedDevices) {
|
|
|
|
|
if (folderCfg.selectedDevices[deviceID] === true) {
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices.push({
|
|
|
|
|
deviceID: deviceID
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
delete folderCfg.selectedDevices;
|
|
|
|
|
|
2015-06-12 13:04:00 +02:00
|
|
|
if (folderCfg.fileVersioningSelector === "trashcan") {
|
|
|
|
|
folderCfg.versioning = {
|
|
|
|
|
'Type': 'trashcan',
|
|
|
|
|
'Params': {
|
|
|
|
|
'cleanoutDays': '' + folderCfg.trashcanClean
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
delete folderCfg.trashcanFileVersioning;
|
|
|
|
|
delete folderCfg.trashcanClean;
|
|
|
|
|
} else if (folderCfg.fileVersioningSelector === "simple") {
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.versioning = {
|
2014-11-26 13:39:59 +01:00
|
|
|
'Type': 'simple',
|
|
|
|
|
'Params': {
|
|
|
|
|
'keep': '' + folderCfg.simpleKeep
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
delete folderCfg.simpleFileVersioning;
|
|
|
|
|
delete folderCfg.simpleKeep;
|
2015-03-10 23:45:43 +01:00
|
|
|
} else if (folderCfg.fileVersioningSelector === "staggered") {
|
|
|
|
|
folderCfg.versioning = {
|
|
|
|
|
'type': 'staggered',
|
|
|
|
|
'params': {
|
2014-11-26 13:39:59 +01:00
|
|
|
'maxAge': '' + (folderCfg.staggeredMaxAge * 86400),
|
|
|
|
|
'cleanInterval': '' + folderCfg.staggeredCleanInterval,
|
|
|
|
|
'versionsPath': '' + folderCfg.staggeredVersionsPath
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
delete folderCfg.staggeredFileVersioning;
|
|
|
|
|
delete folderCfg.staggeredMaxAge;
|
|
|
|
|
delete folderCfg.staggeredCleanInterval;
|
|
|
|
|
delete folderCfg.staggeredVersionsPath;
|
|
|
|
|
|
2015-03-19 11:31:21 +01:00
|
|
|
} else if (folderCfg.fileVersioningSelector === "external") {
|
|
|
|
|
folderCfg.versioning = {
|
|
|
|
|
'Type': 'external',
|
|
|
|
|
'Params': {
|
|
|
|
|
'command': '' + folderCfg.externalCommand
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
delete folderCfg.externalFileVersioning;
|
|
|
|
|
delete folderCfg.externalCommand;
|
2014-11-26 13:39:59 +01:00
|
|
|
} else {
|
2015-03-10 23:45:43 +01:00
|
|
|
delete folderCfg.versioning;
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
|
|
|
|
|
2018-05-08 22:39:17 +01:00
|
|
|
var ignoresLoaded = !$('#folder-ignores textarea').is(':disabled');
|
2018-03-17 13:50:43 +01:00
|
|
|
var ignores = $('#folder-ignores textarea').val().split('\n');
|
|
|
|
|
// Split always returns a minimum 1-length array even for no patterns
|
|
|
|
|
if (ignores.length === 1 && ignores[0] === "") {
|
|
|
|
|
ignores = [];
|
|
|
|
|
}
|
|
|
|
|
if (!$scope.editingExisting && ignores.length) {
|
2017-04-01 09:58:06 +00:00
|
|
|
folderCfg.paused = true;
|
|
|
|
|
};
|
|
|
|
|
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.folders[folderCfg.id] = folderCfg;
|
|
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2018-05-08 22:39:17 +01:00
|
|
|
if (ignoresLoaded && $scope.editingExisting && ignores !== folderCfg.ignores) {
|
2018-03-17 13:50:43 +01:00
|
|
|
saveIgnores(ignores);
|
|
|
|
|
};
|
|
|
|
|
|
2017-04-24 08:51:08 +09:00
|
|
|
$scope.saveConfig(function () {
|
2018-03-17 13:50:43 +01:00
|
|
|
if (!$scope.editingExisting && ignores.length) {
|
|
|
|
|
saveIgnores(ignores, function () {
|
2017-04-24 08:51:08 +09:00
|
|
|
$scope.setFolderPause(folderCfg.id, false);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
2014-12-27 23:12:12 +00:00
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
$scope.ignoreFolder = function (device, pendingFolder) {
|
|
|
|
|
pendingFolder = angular.copy(pendingFolder);
|
|
|
|
|
// Bump time
|
|
|
|
|
pendingFolder.time = (new Date()).toISOString();
|
2014-11-26 13:39:59 +01:00
|
|
|
|
2018-08-25 11:36:10 +01:00
|
|
|
for (var i = 0; i < $scope.devices.length; i++) {
|
|
|
|
|
if ($scope.devices[i].deviceID == device) {
|
|
|
|
|
$scope.devices[i].ignoredFolders.push(pendingFolder);
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-31 18:04:00 +00:00
|
|
|
};
|
|
|
|
|
|
2014-12-01 20:50:27 +01:00
|
|
|
$scope.sharesFolder = function (folderCfg) {
|
|
|
|
|
var names = [];
|
2015-03-10 23:45:43 +01:00
|
|
|
folderCfg.devices.forEach(function (device) {
|
2016-03-11 09:48:46 +00:00
|
|
|
if (device.deviceID !== $scope.myID) {
|
2015-03-10 23:45:43 +01:00
|
|
|
names.push($scope.deviceName($scope.findDevice(device.deviceID)));
|
2014-12-01 20:50:27 +01:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
names.sort();
|
|
|
|
|
return names.join(", ");
|
2015-06-14 17:17:48 +03:00
|
|
|
};
|
2014-12-01 20:50:27 +01:00
|
|
|
|
2014-11-29 09:42:16 +01:00
|
|
|
$scope.deviceFolders = function (deviceCfg) {
|
|
|
|
|
var folders = [];
|
2017-12-15 20:01:56 +00:00
|
|
|
$scope.folderList().forEach(function (folder) {
|
|
|
|
|
for (var i = 0; i < folder.devices.length; i++) {
|
|
|
|
|
if (folder.devices[i].deviceID === deviceCfg.deviceID) {
|
|
|
|
|
folders.push(folder.id);
|
2014-12-04 21:11:30 +01:00
|
|
|
break;
|
2014-11-29 09:42:16 +01:00
|
|
|
}
|
2014-11-26 13:39:59 +01:00
|
|
|
}
|
2017-12-15 20:01:56 +00:00
|
|
|
});
|
2014-11-29 09:42:16 +01:00
|
|
|
return folders;
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2016-06-01 20:24:43 +00:00
|
|
|
$scope.folderLabel = function (folderID) {
|
2018-05-23 12:41:04 +02:00
|
|
|
if (!$scope.folders[folderID]) {
|
|
|
|
|
return folderID;
|
|
|
|
|
}
|
2016-06-01 20:24:43 +00:00
|
|
|
var label = $scope.folders[folderID].label;
|
2018-08-25 11:36:10 +01:00
|
|
|
return label && label.length > 0 ? label : folderID;
|
2016-06-01 20:24:43 +00:00
|
|
|
}
|
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
$scope.deleteFolder = function (id) {
|
2014-11-26 13:39:59 +01:00
|
|
|
$('#editFolder').modal('hide');
|
|
|
|
|
if (!$scope.editingExisting) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-20 14:48:03 +02:00
|
|
|
delete $scope.folders[id];
|
|
|
|
|
delete $scope.model[id];
|
2015-03-10 23:45:43 +01:00
|
|
|
$scope.config.folders = folderList($scope.folders);
|
2015-07-20 14:48:03 +02:00
|
|
|
recalcLocalStateTotal();
|
2014-11-26 13:39:59 +01:00
|
|
|
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
2018-01-01 14:39:23 +00:00
|
|
|
function resetRestoreVersions() {
|
|
|
|
|
$scope.restoreVersions = {
|
|
|
|
|
folder: null,
|
|
|
|
|
selections: {},
|
|
|
|
|
versions: null,
|
|
|
|
|
tree: null,
|
|
|
|
|
errors: null,
|
|
|
|
|
filters: {},
|
|
|
|
|
massAction: function (name, action) {
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.restoreVersions.versions, function (key) {
|
2018-01-01 14:39:23 +00:00
|
|
|
if (key.startsWith(name + '/') && (!$scope.restoreVersions.filters.text || key.indexOf($scope.restoreVersions.filters.text) > -1)) {
|
|
|
|
|
if (action == 'unset') {
|
|
|
|
|
delete $scope.restoreVersions.selections[key];
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var availableVersions = [];
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.restoreVersions.filterVersions($scope.restoreVersions.versions[key]), function (idx, version) {
|
2018-01-01 14:39:23 +00:00
|
|
|
availableVersions.push(version.versionTime);
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if (availableVersions.length) {
|
|
|
|
|
availableVersions.sort(function (a, b) { return a - b; });
|
|
|
|
|
if (action == 'latest') {
|
|
|
|
|
$scope.restoreVersions.selections[key] = availableVersions.pop();
|
|
|
|
|
} else if (action == 'oldest') {
|
|
|
|
|
$scope.restoreVersions.selections[key] = availableVersions.shift();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
filterVersions: function (versions) {
|
|
|
|
|
var filteredVersions = [];
|
2018-01-01 14:39:23 +00:00
|
|
|
$.each(versions, function (idx, version) {
|
|
|
|
|
if (moment(version.versionTime).isBetween($scope.restoreVersions.filters['start'], $scope.restoreVersions.filters['end'], null, '[]')) {
|
|
|
|
|
filteredVersions.push(version);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return filteredVersions;
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
selectionCount: function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
var count = 0;
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.restoreVersions.selections, function (key, value) {
|
2018-01-01 14:39:23 +00:00
|
|
|
if (value) {
|
|
|
|
|
count++;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return count;
|
|
|
|
|
},
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
restore: function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.restoreVersions.tree.clear();
|
|
|
|
|
$scope.restoreVersions.tree = null;
|
|
|
|
|
$scope.restoreVersions.versions = null;
|
|
|
|
|
var selections = {};
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.restoreVersions.selections, function (key, value) {
|
2018-01-01 14:39:23 +00:00
|
|
|
if (value) {
|
|
|
|
|
selections[key] = value;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
$scope.restoreVersions.selections = {};
|
|
|
|
|
|
|
|
|
|
$http.post(urlbase + '/folder/versions?folder=' + encodeURIComponent($scope.restoreVersions.folder), selections).success(function (data) {
|
|
|
|
|
if (Object.keys(data).length == 0) {
|
|
|
|
|
$('#restoreVersions').modal('hide');
|
|
|
|
|
} else {
|
|
|
|
|
$scope.restoreVersions.errors = data;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
2018-12-25 14:26:46 +01:00
|
|
|
show: function (folder) {
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.restoreVersions.folder = folder;
|
|
|
|
|
|
|
|
|
|
var closed = false;
|
|
|
|
|
var modalShown = $q.defer();
|
2018-07-29 20:15:24 +01:00
|
|
|
$('#restoreVersions').modal().one('hidden.bs.modal', function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
closed = true;
|
|
|
|
|
resetRestoreVersions();
|
2018-12-25 14:26:46 +01:00
|
|
|
}).one('shown.bs.modal', function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
modalShown.resolve();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var dataReceived = $http.get(urlbase + '/folder/versions?folder=' + encodeURIComponent($scope.restoreVersions.folder))
|
|
|
|
|
.success(function (data) {
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each(data, function (key, values) {
|
|
|
|
|
$.each(values, function (idx, value) {
|
2018-01-01 14:39:23 +00:00
|
|
|
value.modTime = new Date(value.modTime);
|
|
|
|
|
value.versionTime = new Date(value.versionTime);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
if (closed) return;
|
|
|
|
|
$scope.restoreVersions.versions = data;
|
|
|
|
|
});
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$q.all([dataReceived, modalShown.promise]).then(function () {
|
|
|
|
|
$timeout(function () {
|
2018-11-11 13:42:53 +01:00
|
|
|
if (closed) {
|
|
|
|
|
resetRestoreVersions();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-01-01 14:39:23 +00:00
|
|
|
|
2018-11-11 13:42:53 +01:00
|
|
|
$scope.restoreVersions.tree = $("#restoreTree").fancytree({
|
|
|
|
|
extensions: ["table", "filter"],
|
|
|
|
|
quicksearch: true,
|
|
|
|
|
filter: {
|
|
|
|
|
autoApply: true,
|
|
|
|
|
counter: true,
|
|
|
|
|
hideExpandedCounter: true,
|
|
|
|
|
hideExpanders: true,
|
|
|
|
|
highlight: true,
|
|
|
|
|
leavesOnly: false,
|
|
|
|
|
nodata: true,
|
|
|
|
|
mode: "hide"
|
|
|
|
|
},
|
|
|
|
|
table: {
|
|
|
|
|
indentation: 20,
|
|
|
|
|
nodeColumnIdx: 0,
|
|
|
|
|
},
|
|
|
|
|
debugLevel: 2,
|
|
|
|
|
source: buildTree($scope.restoreVersions.versions),
|
2018-12-25 14:26:46 +01:00
|
|
|
renderColumns: function (event, data) {
|
2018-11-11 13:42:53 +01:00
|
|
|
var node = data.node,
|
|
|
|
|
$tdList = $(node.tr).find(">td"),
|
|
|
|
|
template;
|
|
|
|
|
if (node.folder) {
|
|
|
|
|
template = '<div ng-include="\'syncthing/folder/restoreVersionsMassActions.html\'" class="pull-right"/>';
|
|
|
|
|
} else {
|
|
|
|
|
template = '<div ng-include="\'syncthing/folder/restoreVersionsVersionSelector.html\'" class="pull-right"/>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var scope = $rootScope.$new(true);
|
|
|
|
|
scope.key = node.key;
|
|
|
|
|
scope.restoreVersions = $scope.restoreVersions;
|
|
|
|
|
|
|
|
|
|
$tdList.eq(1).html(
|
|
|
|
|
$compile(template)(scope)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Force angular to redraw.
|
2018-12-25 14:26:46 +01:00
|
|
|
$timeout(function () {
|
2018-11-11 13:42:53 +01:00
|
|
|
$scope.$apply();
|
|
|
|
|
});
|
2018-01-01 14:39:23 +00:00
|
|
|
}
|
2018-11-11 13:42:53 +01:00
|
|
|
}).fancytree("getTree");
|
|
|
|
|
|
|
|
|
|
var minDate = moment(),
|
|
|
|
|
maxDate = moment(0, 'X'),
|
|
|
|
|
date;
|
|
|
|
|
|
|
|
|
|
// Find version window.
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each($scope.restoreVersions.versions, function (key) {
|
|
|
|
|
$.each($scope.restoreVersions.versions[key], function (idx, version) {
|
2018-11-11 13:42:53 +01:00
|
|
|
date = moment(version.versionTime);
|
|
|
|
|
if (date.isBefore(minDate)) {
|
|
|
|
|
minDate = date;
|
|
|
|
|
}
|
|
|
|
|
if (date.isAfter(maxDate)) {
|
|
|
|
|
maxDate = date;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2018-01-01 14:39:23 +00:00
|
|
|
|
2018-11-11 13:42:53 +01:00
|
|
|
$scope.restoreVersions.filters['start'] = minDate;
|
|
|
|
|
$scope.restoreVersions.filters['end'] = maxDate;
|
|
|
|
|
|
|
|
|
|
var ranges = {
|
2018-12-25 14:26:46 +01:00
|
|
|
'All time': [minDate, maxDate],
|
|
|
|
|
'Today': [moment(), moment()],
|
|
|
|
|
'Yesterday': [moment().subtract(1, 'days'), moment().subtract(1, 'days')],
|
|
|
|
|
'Last 7 Days': [moment().subtract(6, 'days'), moment()],
|
|
|
|
|
'Last 30 Days': [moment().subtract(29, 'days'), moment()],
|
|
|
|
|
'This Month': [moment().startOf('month'), moment().endOf('month')],
|
|
|
|
|
'Last Month': [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')]
|
2018-11-11 13:42:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Filter out invalid ranges.
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each(ranges, function (key, range) {
|
2018-11-11 13:42:53 +01:00
|
|
|
if (!range[0].isBetween(minDate, maxDate, null, '[]') && !range[1].isBetween(minDate, maxDate, null, '[]')) {
|
|
|
|
|
delete ranges[key];
|
|
|
|
|
}
|
|
|
|
|
});
|
2018-01-01 14:39:23 +00:00
|
|
|
|
2018-11-11 13:42:53 +01:00
|
|
|
$("#restoreVersionDateRange").daterangepicker({
|
|
|
|
|
timePicker: true,
|
|
|
|
|
timePicker24Hour: true,
|
|
|
|
|
timePickerSeconds: true,
|
|
|
|
|
autoUpdateInput: true,
|
|
|
|
|
opens: "left",
|
|
|
|
|
drops: "up",
|
|
|
|
|
startDate: minDate,
|
|
|
|
|
endDate: maxDate,
|
|
|
|
|
minDate: minDate,
|
|
|
|
|
maxDate: maxDate,
|
|
|
|
|
ranges: ranges,
|
|
|
|
|
locale: {
|
|
|
|
|
format: 'YYYY/MM/DD HH:mm:ss',
|
|
|
|
|
}
|
2018-12-25 14:26:46 +01:00
|
|
|
}).on('apply.daterangepicker', function (ev, picker) {
|
2018-11-11 13:42:53 +01:00
|
|
|
$scope.restoreVersions.filters['start'] = picker.startDate;
|
|
|
|
|
$scope.restoreVersions.filters['end'] = picker.endDate;
|
|
|
|
|
// Events for this UI element are not managed by angular.
|
|
|
|
|
// Force angular to wake up.
|
2018-12-25 14:26:46 +01:00
|
|
|
$timeout(function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
$scope.$apply();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
resetRestoreVersions();
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.$watchCollection('restoreVersions.filters', function () {
|
2018-01-01 14:39:23 +00:00
|
|
|
if (!$scope.restoreVersions.tree) return;
|
|
|
|
|
|
|
|
|
|
$scope.restoreVersions.tree.filterNodes(function (node) {
|
|
|
|
|
if (node.folder) return false;
|
|
|
|
|
if ($scope.restoreVersions.filters.text && node.key.indexOf($scope.restoreVersions.filters.text) < 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if ($scope.restoreVersions.filterVersions(node.data.versions).length == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.setAPIKey = function (cfg) {
|
2016-05-26 07:25:34 +00:00
|
|
|
$http.get(urlbase + '/svc/random/string?length=32').success(function (data) {
|
|
|
|
|
cfg.apiKey = data.random;
|
|
|
|
|
});
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.acceptUR = function () {
|
2017-10-12 06:16:46 +00:00
|
|
|
$scope.config.options.urAccepted = $scope.system.urVersionMax;
|
|
|
|
|
$scope.config.options.urSeen = $scope.system.urVersionMax;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
|
$('#ur').modal('hide');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.declineUR = function () {
|
2017-10-12 06:16:46 +00:00
|
|
|
if ($scope.config.options.urAccepted === 0) {
|
|
|
|
|
$scope.config.options.urAccepted = -1;
|
|
|
|
|
}
|
|
|
|
|
$scope.config.options.urSeen = $scope.system.urVersionMax;
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.saveConfig();
|
|
|
|
|
$('#ur').modal('hide');
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.showNeed = function (folder) {
|
|
|
|
|
$scope.neededFolder = folder;
|
|
|
|
|
refreshNeed(folder);
|
2018-07-29 20:15:24 +01:00
|
|
|
$('#needed').modal().one('hidden.bs.modal', function () {
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.neededFolder = undefined;
|
|
|
|
|
$scope.needed = undefined;
|
2015-04-25 22:53:44 +01:00
|
|
|
$scope.neededCurrentPage = 1;
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2017-12-15 20:01:56 +00:00
|
|
|
$scope.showRemoteNeed = function (device) {
|
|
|
|
|
resetRemoteNeed();
|
|
|
|
|
$scope.remoteNeedDevice = device;
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.deviceFolders(device).forEach(function (folder) {
|
2018-01-14 12:08:40 +00:00
|
|
|
var comp = $scope.completion[device.deviceID][folder];
|
|
|
|
|
if (comp !== undefined && comp.needItems + comp.needDeletes === 0) {
|
2017-12-15 20:01:56 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
$scope.remoteNeedFolders.push(folder);
|
|
|
|
|
$scope.refreshRemoteNeed(folder, 1, 10);
|
|
|
|
|
});
|
2018-07-29 20:15:24 +01:00
|
|
|
$('#remoteNeed').modal().one('hidden.bs.modal', function () {
|
2017-12-15 20:01:56 +00:00
|
|
|
resetRemoteNeed();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2015-06-26 14:22:52 +02:00
|
|
|
$scope.showFailed = function (folder) {
|
2018-01-14 17:01:06 +00:00
|
|
|
$scope.failed.folder = folder;
|
|
|
|
|
$scope.failed = $scope.refreshFailed(1, 10);
|
2018-07-29 20:15:24 +01:00
|
|
|
$('#failed').modal().one('hidden.bs.modal', function () {
|
2018-01-14 17:01:06 +00:00
|
|
|
$scope.failed = {};
|
2015-06-26 14:22:52 +02:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.hasFailedFiles = function (folder) {
|
2018-01-14 17:01:06 +00:00
|
|
|
if (!$scope.model[folder]) {
|
2015-06-26 14:22:52 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2018-01-14 17:01:06 +00:00
|
|
|
return $scope.model[folder].pullErrors !== 0;
|
2015-06-26 14:22:52 +02:00
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.override = function (folder) {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/override?folder=" + encodeURIComponent(folder));
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2018-12-11 09:59:04 +01:00
|
|
|
$scope.showLocalChanged = function (folder) {
|
|
|
|
|
$scope.localChanged.folder = folder;
|
|
|
|
|
$scope.localChanged = $scope.refreshLocalChanged(1, 10);
|
|
|
|
|
$('#localChanged').modal().one('hidden.bs.modal', function () {
|
|
|
|
|
$scope.localChanged = {};
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-12 11:15:57 +03:00
|
|
|
$scope.revert = function (folder) {
|
|
|
|
|
$http.post(urlbase + "/db/revert?folder=" + encodeURIComponent(folder));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.canRevert = function (folder) {
|
|
|
|
|
var f = $scope.model[folder];
|
|
|
|
|
if (!f) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2018-12-11 09:59:04 +01:00
|
|
|
return $scope.model[folder].receiveOnlyTotalItems > 0;
|
2018-07-12 11:15:57 +03:00
|
|
|
};
|
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.advanced = function () {
|
2015-07-09 21:58:58 +01:00
|
|
|
$scope.advancedConfig = angular.copy($scope.config);
|
2015-07-03 14:07:38 +02:00
|
|
|
$('#advanced').modal('show');
|
|
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.showReportPreview = function () {
|
|
|
|
|
$scope.reportPreview = true;
|
|
|
|
|
};
|
|
|
|
|
|
2017-10-12 06:16:46 +00:00
|
|
|
$scope.refreshReportDataPreview = function () {
|
|
|
|
|
$scope.reportDataPreview = '';
|
2017-10-15 07:45:15 +00:00
|
|
|
if (!$scope.reportDataPreviewVersion) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
var version = parseInt($scope.reportDataPreviewVersion);
|
|
|
|
|
if ($scope.reportDataPreviewDiff && version > 2) {
|
|
|
|
|
$q.all([
|
|
|
|
|
$http.get(urlbase + '/svc/report?version=' + version),
|
2018-12-25 14:26:46 +01:00
|
|
|
$http.get(urlbase + '/svc/report?version=' + (version - 1)),
|
2017-10-15 07:45:15 +00:00
|
|
|
]).then(function (responses) {
|
|
|
|
|
var newReport = responses[0].data;
|
|
|
|
|
var oldReport = responses[1].data;
|
2018-12-25 14:26:46 +01:00
|
|
|
angular.forEach(oldReport, function (_, key) {
|
2017-10-15 07:45:15 +00:00
|
|
|
delete newReport[key];
|
|
|
|
|
});
|
|
|
|
|
$scope.reportDataPreview = newReport;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
$http.get(urlbase + '/svc/report?version=' + version).success(function (data) {
|
|
|
|
|
$scope.reportDataPreview = data;
|
|
|
|
|
}).error($scope.emitHTTPError);
|
|
|
|
|
}
|
2017-10-12 06:16:46 +00:00
|
|
|
};
|
|
|
|
|
|
2015-02-11 19:52:59 +01:00
|
|
|
$scope.rescanAllFolders = function () {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/scan");
|
2015-02-11 19:52:59 +01:00
|
|
|
};
|
|
|
|
|
|
2014-11-26 13:39:59 +01:00
|
|
|
$scope.rescanFolder = function (folder) {
|
2015-04-06 10:23:27 +02:00
|
|
|
$http.post(urlbase + "/db/scan?folder=" + encodeURIComponent(folder));
|
2014-11-26 13:39:59 +01:00
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.setAllFoldersPause = function (pause) {
|
2017-02-09 20:31:23 +00:00
|
|
|
var folderListCache = $scope.folderList();
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < folderListCache.length; i++) {
|
|
|
|
|
folderListCache[i].paused = pause;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$scope.config.folders = folderList(folderListCache);
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.isAtleastOneFolderPausedStateSetTo = function (pause) {
|
2017-02-09 20:31:23 +00:00
|
|
|
var folderListCache = $scope.folderList();
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < folderListCache.length; i++) {
|
|
|
|
|
if (folderListCache[i].paused == pause) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$scope.activateAllFsWatchers = function () {
|
2018-03-25 22:05:47 +02:00
|
|
|
var folders = $scope.folderList();
|
|
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
$.each(folders, function (i) {
|
2018-03-25 22:05:47 +02:00
|
|
|
if (folders[i].fsWatcherEnabled) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
folders[i].fsWatcherEnabled = true;
|
|
|
|
|
if (folders[i].rescanIntervalS === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Delay full scans, but scan at least once per day
|
|
|
|
|
folders[i].rescanIntervalS *= 60;
|
|
|
|
|
if (folders[i].rescanIntervalS > 86400) {
|
|
|
|
|
folders[i].rescanIntervalS = 86400;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
$scope.config.folders = folders;
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
};
|
|
|
|
|
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 19:23:06 +00:00
|
|
|
$scope.bumpFile = function (folder, file) {
|
2015-04-25 22:53:44 +01:00
|
|
|
var url = urlbase + "/db/prio?folder=" + encodeURIComponent(folder) + "&file=" + encodeURIComponent(file);
|
|
|
|
|
// In order to get the right view of data in the response.
|
|
|
|
|
url += "&page=" + $scope.neededCurrentPage;
|
|
|
|
|
url += "&perpage=" + $scope.neededPageSize;
|
|
|
|
|
$http.post(url).success(function (data) {
|
2016-03-11 09:48:46 +00:00
|
|
|
if ($scope.neededFolder === folder) {
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 19:23:06 +00:00
|
|
|
console.log("bumpFile", folder, data);
|
2015-04-25 22:53:44 +01:00
|
|
|
parseNeeded(data);
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 19:23:06 +00:00
|
|
|
}
|
2015-01-05 15:42:27 +01:00
|
|
|
}).error($scope.emitHTTPError);
|
Add job queue (fixes #629)
Request to terminate currently ongoing downloads and jump to the bumped file
incoming in 3, 2, 1.
Also, has a slightly strange effect where we pop a job off the queue, but
the copyChannel is still busy and blocks, though it gets moved to the
progress slice in the jobqueue, and looks like it's in progress which it isn't
as it's waiting to be picked up from the copyChan.
As a result, the progress emitter doesn't register on the task, and hence the file
doesn't have a progress bar, but cannot be replaced by a bump.
I guess I can fix progress bar issue by moving the progressEmiter.Register just
before passing the file to the copyChan, but then we are back to the initial
problem of a file with a progress bar, but no progress happening as it's stuck
on write to copyChan
I checked if there is a way to check for channel writeability (before popping)
but got struck by lightning just for bringing the idea up in #go-nuts.
My ideal scenario would be to check if copyChan is writeable, pop job from the
queue and shove it down handleFile. This way jobs would stay in the queue while
they cannot be handled, meaning that the `Bump` could bring your file up higher.
2014-12-01 19:23:06 +00:00
|
|
|
};
|
|
|
|
|
|
2015-04-09 11:32:54 +02:00
|
|
|
$scope.versionString = function () {
|
|
|
|
|
if (!$scope.version.version) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var os = {
|
2018-01-27 09:07:19 +00:00
|
|
|
'darwin': 'macOS',
|
2015-04-09 11:32:54 +02:00
|
|
|
'dragonfly': 'DragonFly BSD',
|
|
|
|
|
'freebsd': 'FreeBSD',
|
|
|
|
|
'openbsd': 'OpenBSD',
|
|
|
|
|
'netbsd': 'NetBSD',
|
|
|
|
|
'linux': 'Linux',
|
|
|
|
|
'windows': 'Windows',
|
2016-03-11 09:48:46 +00:00
|
|
|
'solaris': 'Solaris'
|
2015-04-10 12:45:47 +02:00
|
|
|
}[$scope.version.os] || $scope.version.os;
|
2015-04-09 11:32:54 +02:00
|
|
|
|
2018-12-25 14:26:46 +01:00
|
|
|
var arch = {
|
2015-04-09 11:32:54 +02:00
|
|
|
'386': '32 bit',
|
|
|
|
|
'amd64': '64 bit',
|
2015-04-10 12:45:47 +02:00
|
|
|
'arm': 'ARM',
|
2016-01-01 21:19:15 +01:00
|
|
|
'arm64': 'AArch64',
|
|
|
|
|
'ppc64': 'PowerPC',
|
2016-03-11 09:48:46 +00:00
|
|
|
'ppc64le': 'PowerPC (LE)'
|
2015-04-10 12:45:47 +02:00
|
|
|
}[$scope.version.arch] || $scope.version.arch;
|
2015-04-09 11:32:54 +02:00
|
|
|
|
|
|
|
|
return $scope.version.version + ', ' + os + ' (' + arch + ')';
|
|
|
|
|
};
|
|
|
|
|
|
2015-07-03 14:07:38 +02:00
|
|
|
$scope.inputTypeFor = function (key, value) {
|
|
|
|
|
if (key.substr(0, 1) === '_') {
|
|
|
|
|
return 'skip';
|
|
|
|
|
}
|
2016-01-27 22:41:39 +08:00
|
|
|
if (value === null) {
|
|
|
|
|
return 'null';
|
|
|
|
|
}
|
2015-07-03 14:07:38 +02:00
|
|
|
if (typeof value === 'number') {
|
|
|
|
|
return 'number';
|
|
|
|
|
}
|
|
|
|
|
if (typeof value === 'boolean') {
|
|
|
|
|
return 'checkbox';
|
|
|
|
|
}
|
2017-03-30 14:32:58 +00:00
|
|
|
if (value instanceof Array) {
|
|
|
|
|
return 'list';
|
|
|
|
|
}
|
2015-07-03 14:07:38 +02:00
|
|
|
if (typeof value === 'object') {
|
|
|
|
|
return 'skip';
|
|
|
|
|
}
|
|
|
|
|
return 'text';
|
|
|
|
|
};
|
|
|
|
|
|
2016-03-27 06:40:50 +00:00
|
|
|
$scope.themeName = function (theme) {
|
|
|
|
|
return theme.replace('-', ' ').replace(/(?:^|\s)\S/g, function (a) {
|
|
|
|
|
return a.toUpperCase();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2016-06-17 06:44:55 +00:00
|
|
|
$scope.modalLoaded = function () {
|
|
|
|
|
// once all modal elements have been processed
|
|
|
|
|
if ($('modal').length === 0) {
|
|
|
|
|
|
|
|
|
|
// pseudo main. called on all definitions assigned
|
|
|
|
|
initController();
|
|
|
|
|
}
|
2017-12-15 20:01:56 +00:00
|
|
|
};
|
2016-06-17 06:44:55 +00:00
|
|
|
|
2017-03-31 06:32:54 +00:00
|
|
|
$scope.toggleUnits = function () {
|
|
|
|
|
$scope.metricRates = !$scope.metricRates;
|
|
|
|
|
try {
|
|
|
|
|
window.localStorage["metricRates"] = $scope.metricRates;
|
|
|
|
|
} catch (exception) { }
|
2017-12-15 20:01:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
$scope.sizeOf = function (dict) {
|
2018-06-11 15:47:54 +02:00
|
|
|
if (dict === undefined) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2017-12-15 20:01:56 +00:00
|
|
|
return Object.keys(dict).length;
|
|
|
|
|
};
|
2018-05-13 07:44:19 +02:00
|
|
|
|
|
|
|
|
$scope.dismissNotification = function (id) {
|
|
|
|
|
var idx = $scope.config.options.unackedNotificationIDs.indexOf(id);
|
|
|
|
|
if (idx > -1) {
|
|
|
|
|
$scope.config.options.unackedNotificationIDs.splice(idx, 1);
|
|
|
|
|
$scope.saveConfig();
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-11-26 13:39:59 +01:00
|
|
|
});
|