@@ -58,6 +58,9 @@ angular.module('syncthing.core')
|
||||
text: '',
|
||||
error: null,
|
||||
disabled: false,
|
||||
originalLines: [],
|
||||
defaultLines: [],
|
||||
saved: false,
|
||||
};
|
||||
resetRemoteNeed();
|
||||
|
||||
@@ -409,8 +412,14 @@ angular.module('syncthing.core')
|
||||
console.log("FolderScanProgress", data);
|
||||
});
|
||||
|
||||
// May be called through .error with the presented arguments, or through
|
||||
// .catch with the http response object containing the same arguments.
|
||||
$scope.emitHTTPError = function (data, status, headers, config) {
|
||||
$scope.$emit('HTTPError', { data: data, status: status, headers: headers, config: config });
|
||||
var out = data;
|
||||
if (data && !data.data) {
|
||||
out = { data: data, status: status, headers: headers, config: config };
|
||||
}
|
||||
$scope.$emit('HTTPError', out);
|
||||
};
|
||||
|
||||
var debouncedFuncs = {};
|
||||
@@ -741,7 +750,7 @@ angular.module('syncthing.core')
|
||||
}
|
||||
|
||||
function shouldSetDefaultFolderPath() {
|
||||
return $scope.config.defaults.folder.path && !$scope.editingExisting && $scope.folderEditor.folderPath.$pristine && !$scope.editingDefaults;
|
||||
return $scope.config.defaults.folder.path && $scope.folderEditor.folderPath.$pristine && $scope.currentFolder._editing == "add";
|
||||
}
|
||||
|
||||
function resetRemoteNeed() {
|
||||
@@ -750,7 +759,6 @@ angular.module('syncthing.core')
|
||||
$scope.remoteNeedDevice = undefined;
|
||||
}
|
||||
|
||||
|
||||
function setDefaultTheme() {
|
||||
if (!document.getElementById("fallback-theme-css")) {
|
||||
|
||||
@@ -767,13 +775,9 @@ angular.module('syncthing.core')
|
||||
}
|
||||
}
|
||||
|
||||
function saveIgnores(ignores, cb) {
|
||||
$http.post(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id), {
|
||||
function saveIgnores(ignores) {
|
||||
return $http.post(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id), {
|
||||
ignore: ignores
|
||||
}).success(function () {
|
||||
if (cb) {
|
||||
cb();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -1268,8 +1272,9 @@ angular.module('syncthing.core')
|
||||
if (cfg) {
|
||||
cfg.paused = pause;
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
$scope.saveConfig();
|
||||
return $scope.saveConfig();
|
||||
}
|
||||
return $q.when();
|
||||
};
|
||||
|
||||
$scope.showListenerStatus = function () {
|
||||
@@ -1421,18 +1426,14 @@ angular.module('syncthing.core')
|
||||
});
|
||||
};
|
||||
|
||||
$scope.saveConfig = function (callback) {
|
||||
$scope.saveConfig = function () {
|
||||
var cfg = JSON.stringify($scope.config);
|
||||
var opts = {
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
};
|
||||
$http.put(urlbase + '/config', cfg, opts).finally(refreshConfig).then(function() {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}, $scope.emitHTTPError);
|
||||
return $http.put(urlbase + '/config', cfg, opts).finally(refreshConfig).catch($scope.emitHTTPError);
|
||||
};
|
||||
|
||||
$scope.urVersions = function () {
|
||||
@@ -1512,7 +1513,7 @@ angular.module('syncthing.core')
|
||||
// here as well...
|
||||
$scope.devices = deviceMap($scope.config.devices);
|
||||
|
||||
$scope.saveConfig(function () {
|
||||
$scope.saveConfig.then(function () {
|
||||
if (themeChanged) {
|
||||
document.location.reload(true);
|
||||
}
|
||||
@@ -1578,11 +1579,11 @@ angular.module('syncthing.core')
|
||||
}
|
||||
|
||||
$scope.editDeviceModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
if ($scope.editingDeviceDefaults()) {
|
||||
return $translate.instant("Edit Device Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
if ($scope.editingDeviceExisting()) {
|
||||
title += $translate.instant("Edit Device");
|
||||
} else {
|
||||
title += $translate.instant("Add Device");
|
||||
@@ -1595,16 +1596,23 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.editDeviceModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
if ($scope.has(["existing", "defaults"], $scope.currentDevice._editing)) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-desktop';
|
||||
};
|
||||
|
||||
$scope.editingDeviceDefaults = function() {
|
||||
return $scope.currentDevice._editing == 'defaults';
|
||||
}
|
||||
|
||||
$scope.editingDeviceExisting = function() {
|
||||
return $scope.currentDevice._editing == 'existing';
|
||||
}
|
||||
|
||||
$scope.editDeviceExisting = function (deviceCfg) {
|
||||
$scope.currentDevice = $.extend({}, deviceCfg);
|
||||
$scope.editingExisting = true;
|
||||
$scope.editingDefaults = false;
|
||||
$scope.currentDevice._editing = "existing";
|
||||
$scope.willBeReintroducedBy = undefined;
|
||||
if (deviceCfg.introducedBy) {
|
||||
var introducerDevice = $scope.devices[deviceCfg.introducedBy];
|
||||
@@ -1633,7 +1641,7 @@ angular.module('syncthing.core')
|
||||
$scope.editDeviceDefaults = function () {
|
||||
$http.get(urlbase + '/config/defaults/device').then(function (p) {
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.editingDefaults = true;
|
||||
$scope.currentDevice._editing = "defaults";
|
||||
editDeviceModal();
|
||||
}, $scope.emitHTTPError);
|
||||
};
|
||||
@@ -1671,8 +1679,7 @@ angular.module('syncthing.core')
|
||||
$scope.currentDevice = p.data;
|
||||
$scope.currentDevice.name = name;
|
||||
$scope.currentDevice.deviceID = deviceID;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
$scope.currentDevice._editing = "add";
|
||||
initShareEditing('device');
|
||||
$scope.currentSharing.unrelated = $scope.folderList();
|
||||
editDeviceModal();
|
||||
@@ -1682,7 +1689,7 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.deleteDevice = function () {
|
||||
$('#editDevice').modal('hide');
|
||||
if (!$scope.editingExisting) {
|
||||
if ($scope.currentDevice._editing != "existing") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1705,13 +1712,13 @@ angular.module('syncthing.core')
|
||||
return x.trim();
|
||||
});
|
||||
delete $scope.currentDevice._addressesStr;
|
||||
if ($scope.editingDefaults) {
|
||||
if ($scope.currentDevice._editing == "defaults") {
|
||||
$scope.config.defaults.device = $scope.currentDevice;
|
||||
} else {
|
||||
setDeviceConfig();
|
||||
}
|
||||
delete $scope.currentSharing;
|
||||
delete $scope.currentDevice;
|
||||
$scope.currentDevice = {};
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
@@ -1955,20 +1962,34 @@ angular.module('syncthing.core')
|
||||
$('#folder-ignores textarea').focus();
|
||||
}
|
||||
}).one('hidden.bs.modal', function () {
|
||||
window.location.hash = "";
|
||||
$scope.currentFolder = {};
|
||||
var p = $q.when();
|
||||
// If the modal was closed default patterns should still apply
|
||||
if ($scope.currentFolder._editing == "add-ignores" && !$scope.ignores.saved && $scope.ignores.defaultLines) {
|
||||
p = saveFolderAddIgnores($scope.currentFolder.id, true);
|
||||
}
|
||||
p.then(function () {
|
||||
window.location.hash = "";
|
||||
$scope.currentFolder = {};
|
||||
$scope.ignores = {};
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
$scope.editFolderModalTitle = function() {
|
||||
if ($scope.editingDefaults) {
|
||||
if ($scope.editingFolderDefaults()) {
|
||||
return $translate.instant("Edit Folder Defaults");
|
||||
}
|
||||
var title = '';
|
||||
if ($scope.editingExisting) {
|
||||
title += $translate.instant("Edit Folder");
|
||||
} else {
|
||||
title += $translate.instant("Add Folder");
|
||||
switch ($scope.currentFolder._editing) {
|
||||
case "existing":
|
||||
title = $translate.instant("Edit Folder");
|
||||
break;
|
||||
case "add":
|
||||
title = $translate.instant("Add Folder");
|
||||
break;
|
||||
case "add-ignores":
|
||||
title = $translate.instant("Set Ignores on Added Folder");
|
||||
break;
|
||||
}
|
||||
if ($scope.currentFolder.id !== '') {
|
||||
title += ' (' + $scope.folderLabel($scope.currentFolder.id) + ')';
|
||||
@@ -1977,12 +1998,20 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.editFolderModalIcon = function() {
|
||||
if ($scope.editingDefaults || $scope.editingExisting) {
|
||||
if ($scope.has(["existing", "defaults"], $scope.currentFolder._editing)) {
|
||||
return 'fas fa-pencil-alt';
|
||||
}
|
||||
return 'fas fa-folder';
|
||||
};
|
||||
|
||||
$scope.editingFolderDefaults = function() {
|
||||
return $scope.currentFolder._editing == 'defaults';
|
||||
}
|
||||
|
||||
$scope.editingFolderExisting = function() {
|
||||
return $scope.currentFolder._editing == 'existing';
|
||||
}
|
||||
|
||||
function editFolder(initialTab) {
|
||||
if ($scope.currentFolder.path.length > 1 && $scope.currentFolder.path.slice(-1) === $scope.system.pathSeparator) {
|
||||
$scope.currentFolder.path = $scope.currentFolder.path.slice(0, -1);
|
||||
@@ -2033,39 +2062,60 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.editFolderExisting = function(folderCfg, initialTab) {
|
||||
$scope.editingExisting = true;
|
||||
$scope.editingDefaults = false;
|
||||
$scope.currentFolder = angular.copy(folderCfg);
|
||||
|
||||
$scope.ignores.text = 'Loading...';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = true;
|
||||
$http.get(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id))
|
||||
.success(function (data) {
|
||||
$scope.currentFolder.ignores = data.ignore || [];
|
||||
$scope.ignores.text = $scope.currentFolder.ignores.join('\n');
|
||||
$scope.ignores.error = data.error;
|
||||
$scope.ignores.disabled = false;
|
||||
})
|
||||
.error(function (err) {
|
||||
$scope.ignores.text = $translate.instant("Failed to load ignore patterns.");
|
||||
$scope.emitHTTPError(err);
|
||||
});
|
||||
|
||||
$scope.currentFolder._editing = "existing";
|
||||
editFolderLoadIgnores();
|
||||
editFolder(initialTab);
|
||||
};
|
||||
|
||||
$scope.editFolderDefaults = function() {
|
||||
$http.get(urlbase + '/config/defaults/folder')
|
||||
.success(function (data) {
|
||||
$scope.currentFolder = data;
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = true;
|
||||
editFolder();
|
||||
})
|
||||
.error($scope.emitHTTPError);
|
||||
function editFolderLoadingIgnores() {
|
||||
$scope.ignores.text = 'Loading...';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = true;
|
||||
}
|
||||
|
||||
function editFolderGetIgnores() {
|
||||
return $http.get(urlbase + '/db/ignores?folder=' + encodeURIComponent($scope.currentFolder.id))
|
||||
.then(function(r) {
|
||||
return r.data;
|
||||
}, function (response) {
|
||||
$scope.ignores.text = $translate.instant("Failed to load ignore patterns.");
|
||||
return $q.reject(response);
|
||||
});
|
||||
};
|
||||
|
||||
function editFolderLoadIgnores() {
|
||||
editFolderLoadingIgnores();
|
||||
return editFolderGetIgnores().then(editFolderInitIgnores, $scope.emitHTTPError);
|
||||
}
|
||||
|
||||
$scope.editFolderDefaults = function() {
|
||||
$q.all([
|
||||
$http.get(urlbase + '/config/defaults/folder').then(function (response) {
|
||||
$scope.currentFolder = response.data;
|
||||
$scope.currentFolder._editing = "defaults";
|
||||
}),
|
||||
getDefaultIgnores().then(editFolderInitIgnores),
|
||||
]).then(editFolder, $scope.emitHTTPError);
|
||||
};
|
||||
|
||||
function getDefaultIgnores() {
|
||||
return $http.get(urlbase + '/config/defaults/ignores').then(function (r) {
|
||||
return r.data.lines;
|
||||
});
|
||||
}
|
||||
|
||||
function editFolderInitIgnores(data) {
|
||||
$scope.ignores.originalLines = data.ignore || [];
|
||||
setIgnoresText(data.ignore);
|
||||
$scope.ignores.error = data.error;
|
||||
$scope.ignores.disabled = false;
|
||||
}
|
||||
|
||||
function setIgnoresText(lines) {
|
||||
$scope.ignores.text = lines ? lines.join('\n') : "";
|
||||
}
|
||||
|
||||
$scope.selectAllSharedDevices = function (state) {
|
||||
var devices = $scope.currentSharing.shared;
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
@@ -2093,9 +2143,6 @@ angular.module('syncthing.core')
|
||||
|
||||
$scope.addFolderAndShare = function (folderID, pendingFolder, device) {
|
||||
addFolderInit(folderID).then(function() {
|
||||
$scope.currentFolder.viewFlags = {
|
||||
importFromOtherDevice: true
|
||||
};
|
||||
$scope.currentSharing.selected[device] = true;
|
||||
$scope.currentFolder.label = pendingFolder.offeredBy[device].label;
|
||||
for (var k in pendingFolder.offeredBy) {
|
||||
@@ -2110,19 +2157,16 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
function addFolderInit(folderID) {
|
||||
$scope.editingExisting = false;
|
||||
$scope.editingDefaults = false;
|
||||
return $http.get(urlbase + '/config/defaults/folder').then(function(p) {
|
||||
$scope.currentFolder = p.data;
|
||||
return $http.get(urlbase + '/config/defaults/folder').then(function (response) {
|
||||
$scope.currentFolder = response.data;
|
||||
$scope.currentFolder._editing = "add";
|
||||
$scope.currentFolder.id = folderID;
|
||||
|
||||
initShareEditing('folder');
|
||||
$scope.currentSharing.unrelated = $scope.currentSharing.unrelated.concat($scope.currentSharing.shared);
|
||||
$scope.currentSharing.shared = [];
|
||||
|
||||
$scope.ignores.text = '';
|
||||
$scope.ignores.error = null;
|
||||
$scope.ignores.disabled = false;
|
||||
// Ignores don't need to be initialized here, as that happens in
|
||||
// a second step if the user indicates in the creation modal
|
||||
// that they want to set ignores
|
||||
}, $scope.emitHTTPError);
|
||||
}
|
||||
|
||||
@@ -2142,7 +2186,14 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.saveFolder = function () {
|
||||
$('#editFolder').modal('hide');
|
||||
if ($scope.currentFolder._editing == "add-ignores") {
|
||||
// On modal being hidden without clicking save, the defaults will be saved.
|
||||
$scope.ignores.saved = true;
|
||||
saveFolderAddIgnores($scope.currentFolder.id);
|
||||
hideFolderModal();
|
||||
return;
|
||||
}
|
||||
|
||||
var folderCfg = angular.copy($scope.currentFolder);
|
||||
$scope.currentSharing.selected[$scope.myID] = true;
|
||||
var newDevices = [];
|
||||
@@ -2191,44 +2242,88 @@ angular.module('syncthing.core')
|
||||
}
|
||||
delete folderCfg._guiVersioning;
|
||||
|
||||
if ($scope.editingDefaults) {
|
||||
if ($scope.currentFolder._editing == "defaults") {
|
||||
hideFolderModal();
|
||||
$scope.config.defaults.ignores.lines = ignoresArray();
|
||||
$scope.config.defaults.folder = folderCfg;
|
||||
$scope.saveConfig();
|
||||
} else {
|
||||
saveFolderExisting(folderCfg);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is a new folder where ignores should apply before it first starts.
|
||||
if ($scope.currentFolder._addIgnores) {
|
||||
folderCfg.paused = true;
|
||||
}
|
||||
$scope.folders[folderCfg.id] = folderCfg;
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
|
||||
if ($scope.currentFolder._editing == "existing") {
|
||||
hideFolderModal();
|
||||
saveFolderIgnoresExisting();
|
||||
$scope.saveConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
// No ignores to be set on the new folder, save directly.
|
||||
if (!$scope.currentFolder._addIgnores) {
|
||||
hideFolderModal();
|
||||
$scope.saveConfig();
|
||||
return;
|
||||
}
|
||||
|
||||
// Add folder (paused), load existing ignores and if there are none,
|
||||
// load default ignores, then let the user edit them.
|
||||
$scope.saveConfig().then(function() {
|
||||
editFolderLoadingIgnores();
|
||||
$scope.currentFolder._editing = "add-ignores";
|
||||
$('.nav-tabs a[href="#folder-ignores"]').tab('show');
|
||||
return editFolderGetIgnores();
|
||||
}).then(function(data) {
|
||||
// Error getting ignores -> leave error message.
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
if ((data.ignore && data.ignore.length > 0) || data.error) {
|
||||
editFolderInitIgnores(data);
|
||||
} else {
|
||||
getDefaultIgnores().then(function(lines) {
|
||||
setIgnoresText(lines);
|
||||
$scope.ignores.defaultLines = lines;
|
||||
$scope.ignores.disabled = false;
|
||||
});
|
||||
}
|
||||
}, $scope.emitHTTPError);
|
||||
};
|
||||
|
||||
function saveFolderExisting(folderCfg) {
|
||||
var ignoresLoaded = !$scope.ignores.disabled;
|
||||
function saveFolderIgnoresExisting() {
|
||||
if ($scope.ignores.disabled) {
|
||||
return;
|
||||
}
|
||||
var ignores = ignoresArray();
|
||||
|
||||
function arrayDiffers(a, b) {
|
||||
return !a !== !b || a.length !== b.length || a.some(function(v, i) { return v !== b[i]; });
|
||||
}
|
||||
if (arrayDiffers(ignores, $scope.ignores.originalLines)) {
|
||||
return saveIgnores(ignores);
|
||||
};
|
||||
}
|
||||
|
||||
function saveFolderAddIgnores(folderID, useDefault) {
|
||||
var ignores = useDefault ? $scope.ignores.defaultLines : ignoresArray();
|
||||
return saveIgnores(ignores).then(function () {
|
||||
return $scope.setFolderPause(folderID, $scope.currentFolder.paused);
|
||||
});
|
||||
};
|
||||
|
||||
function ignoresArray() {
|
||||
var ignores = $scope.ignores.text.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) {
|
||||
folderCfg.paused = true;
|
||||
};
|
||||
|
||||
$scope.folders[folderCfg.id] = folderCfg;
|
||||
$scope.config.folders = folderList($scope.folders);
|
||||
|
||||
function arrayEquals(a, b) {
|
||||
return a.length === b.length && a.every(function(v, i) { return v === b[i] });
|
||||
}
|
||||
|
||||
if (ignoresLoaded && $scope.editingExisting && !arrayEquals(ignores, folderCfg.ignores)) {
|
||||
saveIgnores(ignores);
|
||||
};
|
||||
|
||||
$scope.saveConfig(function () {
|
||||
if (!$scope.editingExisting && ignores.length) {
|
||||
saveIgnores(ignores, function () {
|
||||
$scope.setFolderPause(folderCfg.id, false);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
return ignores;
|
||||
}
|
||||
|
||||
$scope.ignoreFolder = function (device, folderID, offeringDevice) {
|
||||
var ignoredFolder = {
|
||||
@@ -2282,8 +2377,8 @@ angular.module('syncthing.core')
|
||||
};
|
||||
|
||||
$scope.deleteFolder = function (id) {
|
||||
$('#editFolder').modal('hide');
|
||||
if (!$scope.editingExisting) {
|
||||
hideFolderModal();
|
||||
if ($scope.currentFolder._editing != "existing") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2295,6 +2390,10 @@ angular.module('syncthing.core')
|
||||
$scope.saveConfig();
|
||||
};
|
||||
|
||||
function hideFolderModal() {
|
||||
$('#editFolder').modal('hide');
|
||||
}
|
||||
|
||||
function resetRestoreVersions() {
|
||||
$scope.restoreVersions = {
|
||||
folder: null,
|
||||
@@ -2839,6 +2938,10 @@ angular.module('syncthing.core')
|
||||
return Object.keys(dict).length;
|
||||
};
|
||||
|
||||
$scope.has = function (array, element) {
|
||||
return array.indexOf(element) >= 0;
|
||||
};
|
||||
|
||||
$scope.dismissNotification = function (id) {
|
||||
var idx = $scope.config.options.unackedNotificationIDs.indexOf(id);
|
||||
if (idx > -1) {
|
||||
|
||||
@@ -4,7 +4,7 @@ angular.module('syncthing.core')
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
if (scope.currentFolder._editing != "add") {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('uniqueFolder', true);
|
||||
} else if (scope.folders.hasOwnProperty(viewValue)) {
|
||||
|
||||
@@ -4,7 +4,7 @@ angular.module('syncthing.core')
|
||||
require: 'ngModel',
|
||||
link: function (scope, elm, attrs, ctrl) {
|
||||
ctrl.$parsers.unshift(function (viewValue) {
|
||||
if (scope.editingExisting) {
|
||||
if (scope.currentDevice._editing != "add") {
|
||||
// we shouldn't validate
|
||||
ctrl.$setValidity('validDeviceid', true);
|
||||
} else {
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
<form role="form" name="deviceEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#device-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li ng-if="!editingDefaults"><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li ng-if="!editingDeviceDefaults()"><a data-toggle="tab" href="#device-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#device-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
<div id="device-general" class="tab-pane in active">
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<div ng-if="!editingDeviceDefaults()" class="form-group" ng-class="{'has-error': deviceEditor.deviceID.$invalid && deviceEditor.deviceID.$dirty}" ng-init="loadFormIntoScope(deviceEditor)">
|
||||
<label translate for="deviceID">Device ID</label>
|
||||
<div ng-if="!editingExisting">
|
||||
<div ng-if="!editingDeviceExisting()">
|
||||
<div class="input-group">
|
||||
<input name="deviceID" id="deviceID" class="form-control text-monospace" type="text" ng-model="currentDevice.deviceID" required="" valid-deviceid list="discovery-list" aria-required="true" />
|
||||
<div class="input-group-btn">
|
||||
@@ -40,7 +40,7 @@
|
||||
<span translate ng-if="deviceEditor.deviceID.$error.unique && deviceEditor.deviceID.$dirty">A device with that ID is already added.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div ng-if="editingExisting" class="input-group">
|
||||
<div ng-if="editingDeviceExisting()" class="input-group">
|
||||
<div class="well well-sm text-monospace form-control" style="height: auto;" select-on-click>{{currentDevice.deviceID}}</div>
|
||||
<div class="input-group-btn">
|
||||
<button type="button" class="btn btn-default" data-toggle="modal" data-target="#idqr">
|
||||
@@ -56,7 +56,7 @@
|
||||
<p translate ng-if="currentDevice.deviceID != myID" class="help-block">Shown instead of Device ID in the cluster status. Will be updated to the name the device advertises if left empty.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-if="!editingDefaults" id="device-sharing" class="tab-pane">
|
||||
<div ng-if="!editingDeviceDefaults()" id="device-sharing" class="tab-pane">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
@@ -172,7 +172,7 @@
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<div ng-if="editingExisting && !editingDefaults" class="pull-left">
|
||||
<div ng-if="has(['existing', 'defaults'], currentDevice._editing)" class="pull-left">
|
||||
<button type="button" class="btn btn-warning btn-sm" data-toggle="modal" data-target="#remove-device-confirmation">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
|
||||
@@ -2,44 +2,44 @@
|
||||
<div class="modal-body">
|
||||
<form role="form" name="folderEditor">
|
||||
<ul class="nav nav-tabs" ng-init="loadFormIntoScope(folderEditor)">
|
||||
<li class="active"><a data-toggle="tab" href="#folder-general"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-sharing"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-versioning"><span class="fas fa-copy"></span> <span translate>File Versioning</span></a></li>
|
||||
<li ng-if="!editingDefaults" ng-class="{'disabled': currentFolder._recvEnc}"><a ng-attr-data-toggle="{{ currentFolder._recvEnc ? undefined : 'tab'}}" href="{{currentFolder._recvEnc ? '#' : '#folder-ignores'}}"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li><a data-toggle="tab" href="#folder-advanced"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
<li ng-class="{'disabled': currentFolder._editing == 'add-ignores'}" class="active"><a data-toggle="tab" href="{{currentFolder._editing == 'add-ignores' ? '' : '#folder-general'}}"><span class="fas fa-cog"></span> <span translate>General</span></a></li>
|
||||
<li ng-class="{'disabled': currentFolder._editing == 'add-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'add-ignores' ? '' : '#folder-sharing'}}"><span class="fas fa-share-alt"></span> <span translate>Sharing</span></a></li>
|
||||
<li ng-class="{'disabled': currentFolder._editing == 'add-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'add-ignores' ? '' : '#folder-versioning'}}"><span class="fas fa-copy"></span> <span translate>File Versioning</span></a></li>
|
||||
<li ng-class="{'disabled': currentFolder._recvEnc}"><a data-toggle="tab" href="{{currentFolder._recvEnc ? '' : '#folder-ignores'}}"><span class="fas fa-filter"></span> <span translate>Ignore Patterns</span></a></li>
|
||||
<li ng-class="{'disabled': currentFolder._editing == 'add-ignores'}"><a data-toggle="tab" href="{{currentFolder._editing == 'add-ignores' ? '' : '#folder-advanced'}}"><span class="fas fa-cogs"></span> <span translate>Advanced</span></a></li>
|
||||
</ul>
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="folder-general" class="tab-pane in active">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty && !editingDefaults}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderLabel.$invalid && folderEditor.folderLabel.$dirty && !editingFolderDefaults()}">
|
||||
<label for="folderLabel"><span translate>Folder Label</span></label>
|
||||
<input name="folderLabel" id="folderLabel" class="form-control" type="text" ng-model="currentFolder.label" value="{{currentFolder.label}}" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.folderLabel.$valid || folderEditor.folderLabel.$pristine">Optional descriptive label for the folder. Can be different on each device.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div ng-if="!editingDefaults" class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<div ng-if="!editingFolderDefaults()" class="form-group" ng-class="{'has-error': folderEditor.folderID.$invalid && folderEditor.folderID.$dirty}">
|
||||
<label for="folderID"><span translate>Folder ID</span></label>
|
||||
<input name="folderID" ng-readonly="editingExisting || (!editingExisting && currentFolder.viewFlags.importFromOtherDevice)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}" />
|
||||
<input name="folderID" ng-readonly="has(['existing', 'add'], currentFolder._editing)" id="folderID" class="form-control" type="text" ng-model="currentFolder.id" required="" aria-required="true" unique-folder value="{{currentFolder.id}}" />
|
||||
<p class="help-block">
|
||||
<span translate ng-if="folderEditor.folderID.$valid || folderEditor.folderID.$pristine">Required identifier for the folder. Must be the same on all cluster devices.</span>
|
||||
<span translate ng-if="folderEditor.folderID.$error.uniqueFolder">The folder ID must be unique.</span>
|
||||
<span translate ng-if="folderEditor.folderID.$error.required && folderEditor.folderID.$dirty">The folder ID cannot be blank.</span>
|
||||
<span translate ng-show="!editingExisting">When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.</span>
|
||||
<span translate ng-show="!editingFolderExisting()">When adding a new folder, keep in mind that the Folder ID is used to tie folders together between devices. They are case sensitive and must match exactly between all devices.</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty && !editingDefaults}">
|
||||
<div class="form-group" ng-class="{'has-error': folderEditor.folderPath.$invalid && folderEditor.folderPath.$dirty && !editingFolderDefaults()}">
|
||||
<label translate for="folderPath">Folder Path</label>
|
||||
<input name="folderPath" ng-readonly="editingExisting && !editingDefaults" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" ng-required="!editingDefaults" ng-aria-required="!editingDefaults" path-is-sub-dir />
|
||||
<input name="folderPath" ng-readonly="editingFolderExisting()" id="folderPath" class="form-control" type="text" ng-model="currentFolder.path" list="directory-list" ng-required="!editingFolderDefaults()" ng-aria-required="!editingFolderDefaults()" path-is-sub-dir />
|
||||
<datalist id="directory-list">
|
||||
<option ng-repeat="directory in directoryList" value="{{ directory }}" />
|
||||
</datalist>
|
||||
<p class="help-block">
|
||||
<span ng-if="folderEditor.folderPath.$valid || folderEditor.folderPath.$pristine"><span translate>Path to the folder on the local computer. Will be created if it does not exist. The tilde character (~) can be used as a shortcut for</span> <code>{{system.tilde}}</code>.</br></span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty && !editingDefaults">The folder path cannot be blank.</span>
|
||||
<span translate ng-if="folderEditor.folderPath.$error.required && folderEditor.folderPath.$dirty && !editingFolderDefaults()">The folder path cannot be blank.</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length == 0">Warning, this path is a subdirectory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.isSub && folderPathErrors.otherLabel.length != 0">Warning, this path is a subdirectory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
<span ng-if="folderPathErrors.isParent && !editingDefaults">
|
||||
<span ng-if="folderPathErrors.isParent && !editingFolderDefaults()">
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" ng-if="folderPathErrors.otherLabel.length == 0">Warning, this path is a parent directory of an existing folder "{%otherFolder%}".</span>
|
||||
<span class="text-danger" translate translate-value-other-folder="{{folderPathErrors.otherID}}" translate-value-other-folder-label="{{folderPathErrors.otherLabel}}" ng-if="folderPathErrors.otherLabel.length != 0">Warning, this path is a parent directory of an existing folder "{%otherFolderLabel%}" ({%otherFolder%}).</span>
|
||||
</span>
|
||||
@@ -148,33 +148,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="!editingDefaults" id="folder-ignores" class="tab-pane">
|
||||
<p translate>Enter ignore patterns, one per line.</p>
|
||||
<div ng-class="{'has-error': ignores.error != null}">
|
||||
<textarea class="form-control" rows="5" ng-model="ignores.text" ng-disabled="ignores.disabled"></textarea>
|
||||
<p class="help-block" ng-if="ignores.error">
|
||||
{{ignores.error}}
|
||||
</p>
|
||||
<div id="folder-ignores" class="tab-pane" ng-switch="currentFolder._editing">
|
||||
<div ng-switch-when="add">
|
||||
<label>
|
||||
<input type="checkbox" ng-model="currentFolder._addIgnores" > <span translate>Add ignore patterns</span>
|
||||
</label>
|
||||
<p translate class="help-block">Ignore patterns can only be added after the folder is created. If checked, an input field to enter ignore patterns will be presented after saving.</p>
|
||||
</div>
|
||||
<div ng-switch-default>
|
||||
<p translate>Enter ignore patterns, one per line.</p>
|
||||
<div ng-class="{'has-error': ignores.error != null}">
|
||||
<textarea class="form-control" name="ignoresText" rows="5" ng-model="ignores.text" ng-disabled="ignores.disabled"></textarea>
|
||||
<p class="help-block" ng-if="ignores.error">
|
||||
{{ignores.error}}
|
||||
</p>
|
||||
</div>
|
||||
<hr />
|
||||
<p class="small"><span translate>Quick guide to supported patterns</span> (<a href="https://docs.syncthing.net/users/ignoring.html" target="_blank" translate>full documentation</a>):</p>
|
||||
<dl class="dl-horizontal dl-narrow small">
|
||||
<dt><code>(?d)</code></dt>
|
||||
<dd><b><span translate>Prefix indicating that the file can be deleted if preventing directory removal</span></b></dd>
|
||||
<dt><code>(?i)</code></dt>
|
||||
<dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd>
|
||||
<dt><code>!</code></dt>
|
||||
<dd><span translate>Inversion of the given condition (i.e. do not exclude)</span></dd>
|
||||
<dt><code>*</code></dt>
|
||||
<dd><span translate>Single level wildcard (matches within a directory only)</span></dd>
|
||||
<dt><code>**</code></dt>
|
||||
<dd><span translate>Multi level wildcard (matches multiple directory levels)</span></dd>
|
||||
<dt><code>//</code></dt>
|
||||
<dd><span translate>Comment, when used at the start of a line</span></dd>
|
||||
</dl>
|
||||
<div ng-if="!editingFolderDefaults()">
|
||||
<hr />
|
||||
<span translate translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<p class="small"><span translate>Quick guide to supported patterns</span> (<a href="https://docs.syncthing.net/users/ignoring.html" target="_blank" translate>full documentation</a>):</p>
|
||||
<dl class="dl-horizontal dl-narrow small">
|
||||
<dt><code>(?d)</code></dt>
|
||||
<dd><b><span translate>Prefix indicating that the file can be deleted if preventing directory removal</span></b></dd>
|
||||
<dt><code>(?i)</code></dt>
|
||||
<dd><span translate>Prefix indicating that the pattern should be matched without case sensitivity</span></dd>
|
||||
<dt><code>!</code></dt>
|
||||
<dd><span translate>Inversion of the given condition (i.e. do not exclude)</span></dd>
|
||||
<dt><code>*</code></dt>
|
||||
<dd><span translate>Single level wildcard (matches within a directory only)</span></dd>
|
||||
<dt><code>**</code></dt>
|
||||
<dd><span translate>Multi level wildcard (matches multiple directory levels)</span></dd>
|
||||
<dt><code>//</code></dt>
|
||||
<dd><span translate>Comment, when used at the start of a line</span></dd>
|
||||
</dl>
|
||||
<hr />
|
||||
<span translate ng-show="editingExisting" translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Editing {%path%}.</span>
|
||||
<span translate ng-show="!editingExisting" translate-value-path="{{currentFolder.path}}{{system.pathSeparator}}.stignore">Creating ignore patterns, overwriting an existing file at {%path%}.</span>
|
||||
</div>
|
||||
|
||||
<div id="folder-advanced" class="tab-pane">
|
||||
@@ -205,17 +214,17 @@
|
||||
<div class="col-md-6 form-group">
|
||||
<label translate>Folder Type</label>
|
||||
<a href="https://docs.syncthing.net/users/foldertypes.html" target="_blank"><span class="fas fa-question-circle"></span> <span translate>Help</span></a>
|
||||
<select class="form-control" ng-change="setDefaultsForFolderType()" ng-model="currentFolder.type" ng-disabled="editingExisting && currentFolder.type == 'receiveencrypted'">
|
||||
<select class="form-control" ng-change="setDefaultsForFolderType()" ng-model="currentFolder.type" ng-disabled="editingFolderExisting() && currentFolder.type == 'receiveencrypted'">
|
||||
<option value="sendreceive" translate>Send & Receive</option>
|
||||
<option value="sendonly" translate>Send Only</option>
|
||||
<option value="receiveonly" translate>Receive Only</option>
|
||||
<option value="receiveencrypted" ng-disabled="editingExisting" translate>Receive Encrypted</option>
|
||||
<option value="receiveencrypted" ng-disabled="editingFolderExisting()" translate>Receive Encrypted</option>
|
||||
</select>
|
||||
<p ng-if="currentFolder.type == 'sendonly'" translate class="help-block">Files are protected from changes made on other devices, but changes made on this device will be sent to the rest of the cluster.</p>
|
||||
<p ng-if="currentFolder.type == 'receiveonly'" translate class="help-block">Files are synchronized from the cluster, but any changes made locally will not be sent to other devices.</p>
|
||||
<p ng-if="currentFolder.type == 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Stores and syncs only encrypted data. Folders on all connected devices need to be set up with the same password or be of type "{%receiveEncrypted%}" too.</p>
|
||||
<p ng-if="editingExisting && currentFolder.type == 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.</p>
|
||||
<p ng-if="editingExisting && currentFolder.type != 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" can only be set when adding a new folder.</p>
|
||||
<p ng-if="editingFolderExisting() && currentFolder.type == 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" cannot be changed after adding the folder. You need to remove the folder, delete or decrypt the data on disk, and add the folder again.</p>
|
||||
<p ng-if="editingFolderExisting() && currentFolder.type != 'receiveencrypted'" translate class="help-block" translate-value-receive-encrypted="{{'Receive Encrypted' | translate}}">Folder type "{%receiveEncrypted%}" can only be set when adding a new folder.</p>
|
||||
</div>
|
||||
<div class="col-md-6 form-group">
|
||||
<label translate>File Pull Order</label>
|
||||
@@ -274,7 +283,7 @@
|
||||
<button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
|
||||
<span class="fas fa-times"></span> <span translate>Close</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingExisting && !editingDefaults">
|
||||
<button type="button" class="btn btn-warning pull-left btn-sm" data-toggle="modal" data-target="#remove-folder-confirmation" ng-if="editingFolderExisting()">
|
||||
<span class="fas fa-minus-circle"></span> <span translate>Remove</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user