Browse Source

Merge pull request #1174 from AudriusButkevicius/intro

New device, folder prompts (fixes #120, fixes #330)
Jakob Borg 10 years ago
parent
commit
46343f2f9e

+ 10 - 5
cmd/syncthing/main.go

@@ -975,11 +975,16 @@ next:
 			}
 		}
 
-		events.Default.Log(events.DeviceRejected, map[string]string{
-			"device":  remoteID.String(),
-			"address": conn.RemoteAddr().String(),
-		})
-		l.Infof("Connection from %s with unknown device ID %s; ignoring", conn.RemoteAddr(), remoteID)
+		if !cfg.IgnoredDevice(remoteID) {
+			events.Default.Log(events.DeviceRejected, map[string]string{
+				"device":  remoteID.String(),
+				"address": conn.RemoteAddr().String(),
+			})
+			l.Infof("Connection from %s with unknown device ID %s", conn.RemoteAddr(), remoteID)
+		} else {
+			l.Infof("Connection from %s with ignored device ID %s", conn.RemoteAddr(), remoteID)
+		}
+
 		conn.Close()
 	}
 }

+ 12 - 1
gui/assets/lang/lang-en.json

@@ -1,8 +1,10 @@
 {
    "API Key": "API Key",
    "About": "About",
+   "Add": "Add",
    "Add Device": "Add Device",
    "Add Folder": "Add Folder",
+   "Add new folder?": "Add new folder?",
    "Address": "Address",
    "Addresses": "Addresses",
    "Allow Anonymous Usage Reporting?": "Allow Anonymous Usage Reporting?",
@@ -22,6 +24,7 @@
    "Device ID": "Device ID",
    "Device Identification": "Device Identification",
    "Device Name": "Device Name",
+   "Device {%device%} ({%address%}) wants to connect. Add new device?": "Device {{device}} ({{address}}) wants to connect. Add new device?",
    "Disconnected": "Disconnected",
    "Documentation": "Documentation",
    "Download Rate": "Download Rate",
@@ -51,6 +54,7 @@
    "Global Discovery Server": "Global Discovery Server",
    "Global State": "Global State",
    "Idle": "Idle",
+   "Ignore": "Ignore",
    "Ignore Patterns": "Ignore Patterns",
    "Ignore Permissions": "Ignore Permissions",
    "Incoming Rate Limit (KiB/s)": "Incoming Rate Limit (KiB/s)",
@@ -59,12 +63,15 @@
    "Keep Versions": "Keep Versions",
    "Last File Synced": "Last File Synced",
    "Last seen": "Last seen",
+   "Later": "Later",
    "Latest Release": "Latest Release",
    "Local Discovery": "Local Discovery",
    "Local State": "Local State",
    "Maximum Age": "Maximum Age",
    "Multi level wildcard (matches multiple directory levels)": "Multi level wildcard (matches multiple directory levels)",
    "Never": "Never",
+   "New Device": "New Device",
+   "New Folder": "New Folder",
    "No": "No",
    "No File Versioning": "No File Versioning",
    "Notice": "Notice",
@@ -92,8 +99,11 @@
    "Select the devices to share this folder with.": "Select the devices to share this folder with.",
    "Select the folders to share with this device.": "Select the folders to share with this device.",
    "Settings": "Settings",
+   "Share": "Share",
+   "Share Folder": "Share Folder",
    "Share Folders With Device": "Share Folders With Device",
    "Share With Devices": "Share With Devices",
+   "Share this folder?": "Share this folder?",
    "Shared With": "Shared With",
    "Short identifier for the folder. Must be the same on all cluster devices.": "Short identifier for the folder. Must be the same on all cluster devices.",
    "Show ID": "Show ID",
@@ -149,5 +159,6 @@
    "Yes": "Yes",
    "You must keep at least one version.": "You must keep at least one version.",
    "full documentation": "full documentation",
-   "items": "items"
+   "items": "items",
+   "{%device%} wants to share folder \"{%folder%}\".": "{{device}} wants to share folder \"{{folder}}\"."
 }

+ 63 - 0
gui/index.html

@@ -308,6 +308,69 @@
       </div>
     </div> <!-- /row -->
 
+    <!-- Device Rejections -->
+
+    <div ng-repeat="(device, event) in deviceRejections" class="row">
+      <div class="col-md-12">
+        <div class="panel panel-warning">
+          <div class="panel-heading"><h3 class="panel-title"><span translate>New Device</span></h3></div>
+          <div class="panel-body">
+            <p>
+              <small>{{ event.time | date:"H:mm:ss" }}:</small>
+              <span translate translate-value-device="{{ device }}" translate-value-address="{{ event.data.address }}">
+                Device {%device%} ({%address%}) wants to connect. Add new device?
+              <span>
+            </p>
+          </div>
+          <div class="panel-footer clearfix">
+            <div class="pull-right">
+              <button class="btn btn-sm btn-success" ng-click="addNewDeviceID(device)"><span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Add</span></button>
+              <button class="btn btn-sm btn-danger" ng-click="ignoreRejectedDevice(device)"><span class="glyphicon glyphicon-remove"></span>&emsp;<span translate>Ignore</span></button>
+              <button class="btn btn-sm btn-default" ng-click="dismissDeviceRejection(device)"><span class="glyphicon glyphicon-time"></span>&emsp;<span translate>Later</span></button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
+    <!-- Folder Rejections -->
+
+    <div ng-repeat="(key, event) in folderRejections" class="row reject">
+      <div class="col-md-12">
+        <div class="panel panel-warning">
+          <div class="panel-heading">
+            <h3 class="panel-title">
+              <span translate ng-if="!folders[event.data.folder]">New Folder</span>
+              <span translate ng-if="folders[event.data.folder]">Share Folder</span>
+            </h3>
+          </div>
+          <div class="panel-body">
+            <p>
+              <small>{{ event.time | date:"H:mm:ss" }}:</small>
+              <span translate translate-value-device="{{ deviceName(findDevice(event.data.device)) }}" translate-value-folder="{{ event.data.folder }}">
+                {%device%} wants to share folder "{%folder%}".
+              </span>
+              <span translate ng-if="folders[event.data.folder]">Share this folder?</span>
+              <span translate ng-if="!folders[event.data.folder]">Add new folder?</span>
+            </p>
+          </div>
+          <div class="panel-footer clearfix">
+            <div class="pull-right">
+              <button class="btn btn-sm btn-success" ng-click="addFolderAndShare(event.data.folder, event.data.device)" ng-if="!folders[event.data.folder]">
+                <span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Add</span>
+              </button>
+              <button class="btn btn-sm btn-success" ng-click="shareFolderWithDevice(event.data.folder, event.data.device)" ng-if="folders[event.data.folder]">
+                <span class="glyphicon glyphicon-ok"></span>&emsp;<span translate>Share</span>
+              </button>
+              <button class="btn btn-sm btn-default" ng-click="dismissFolderRejection(event.data.folder, event.data.device)">
+                <span class="glyphicon glyphicon-time"></span>&emsp;<span translate>Later</span>
+              </button>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+
     <!-- Errors -->
 
     <div ng-if="errorList().length > 0" class="row">

+ 81 - 6
gui/scripts/syncthing/core/controllers/syncthingController.js

@@ -47,6 +47,8 @@ angular.module('syncthing.core')
         $scope.model = {};
         $scope.myID = '';
         $scope.devices = [];
+        $scope.deviceRejections = {};
+        $scope.folderRejections = {};
         $scope.protocolChanged = false;
         $scope.reportData = {};
         $scope.reportPreview = false;
@@ -168,6 +170,14 @@ angular.module('syncthing.core')
             }
         });
 
+        $scope.$on('DeviceRejected', function (event, arg) {
+            $scope.deviceRejections[arg.data.device] = arg;
+        });
+
+        $scope.$on('FolderRejected', function (event, arg) {
+            $scope.folderRejections[arg.data.folder + "-" + arg.data.device] = arg;
+        });
+
         $scope.$on('ConfigSaved', function (event, arg) {
             updateLocalConfig(arg.data);
 
@@ -698,6 +708,11 @@ angular.module('syncthing.core')
                 return n.DeviceID !== $scope.currentDevice.DeviceID;
             });
             $scope.config.Devices = $scope.devices;
+            // In case we later added the device manually, remove the ignoral
+            // record.
+            $scope.config.IgnoredDevices = $scope.config.IgnoredDevices.filter(function (id) {
+                return id !== $scope.currentDevice.DeviceID;
+            });
 
             for (var id in $scope.folders) {
                 $scope.folders[id].Devices = $scope.folders[id].Devices.filter(function (n) {
@@ -709,10 +724,24 @@ angular.module('syncthing.core')
         };
 
         $scope.saveDevice = function () {
-            var deviceCfg, done, i;
-
             $('#editDevice').modal('hide');
-            deviceCfg = $scope.currentDevice;
+            $scope.saveDeviceConfig($scope.currentDevice);
+        };
+
+        $scope.addNewDeviceID = function (device) {
+            var deviceCfg = {
+                DeviceID: device,
+                AddressesStr: 'dynamic',
+                Compression: true,
+                Introducer: false,
+                selectedFolders: {}
+            };
+            $scope.saveDeviceConfig(deviceCfg);
+            $scope.dismissDeviceRejection(device);
+        };
+
+        $scope.saveDeviceConfig = function (deviceCfg) {
+            var done, i;
             deviceCfg.Addresses = deviceCfg.AddressesStr.split(',').map(function (x) {
                 return x.trim();
             });
@@ -732,6 +761,11 @@ angular.module('syncthing.core')
 
             $scope.devices.sort(deviceCompare);
             $scope.config.Devices = $scope.devices;
+            // In case we are adding the device manually, remove the ignoral
+            // record.
+            $scope.config.IgnoredDevices = $scope.config.IgnoredDevices.filter(function (id) {
+                return id !== deviceCfg.DeviceID;
+            });
 
             if (!$scope.editingSelf) {
                 for (var id in deviceCfg.selectedFolders) {
@@ -749,7 +783,6 @@ angular.module('syncthing.core')
                                 DeviceID: deviceCfg.DeviceID
                             });
                         }
-                        continue
                     } else {
                         $scope.folders[id].Devices = $scope.folders[id].Devices.filter(function (n) {
                             return n.DeviceID != deviceCfg.DeviceID;
@@ -761,6 +794,16 @@ angular.module('syncthing.core')
             $scope.saveConfig();
         };
 
+        $scope.dismissDeviceRejection = function (device) {
+            delete $scope.deviceRejections[device];
+        };
+
+        $scope.ignoreRejectedDevice = function (device) {
+            $scope.config.IgnoredDevices.push(device);
+            $scope.saveConfig();
+            $scope.dismissDeviceRejection(device);
+        };
+
         $scope.otherDevices = function () {
             return $scope.devices.filter(function (n) {
                 return n.DeviceID !== $scope.myID;
@@ -817,8 +860,8 @@ angular.module('syncthing.core')
             });
         });
 
-        $scope.editFolder = function (deviceCfg) {
-            $scope.currentFolder = angular.copy(deviceCfg);
+        $scope.editFolder = function (folderCfg) {
+            $scope.currentFolder = angular.copy(folderCfg);
             $scope.currentFolder.selectedDevices = {};
             $scope.currentFolder.Devices.forEach(function (n) {
                 $scope.currentFolder.selectedDevices[n.DeviceID] = true;
@@ -867,6 +910,34 @@ angular.module('syncthing.core')
             $('#editFolder').modal();
         };
 
+        $scope.addFolderAndShare = function (folder, device) {
+            $scope.dismissFolderRejection(folder, device);
+            $scope.currentFolder = {
+                ID: folder,
+                selectedDevices: {}
+            };
+            $scope.currentFolder.selectedDevices[device] = true;
+
+            $scope.currentFolder.RescanIntervalS = 60;
+            $scope.currentFolder.FileVersioningSelector = "none";
+            $scope.currentFolder.simpleKeep = 5;
+            $scope.currentFolder.staggeredMaxAge = 365;
+            $scope.currentFolder.staggeredCleanInterval = 3600;
+            $scope.currentFolder.staggeredVersionsPath = "";
+            $scope.editingExisting = false;
+            $scope.folderEditor.$setPristine();
+            $('#editFolder').modal();
+        };
+
+        $scope.shareFolderWithDevice = function (folder, device) {
+            $scope.folders[folder].Devices.push({
+                DeviceID: device
+            });
+            $scope.config.Folders = folderList($scope.folders);
+            $scope.saveConfig();
+            $scope.dismissFolderRejection(folder, device);
+        };
+
         $scope.saveFolder = function () {
             var folderCfg, done, i;
 
@@ -916,6 +987,10 @@ angular.module('syncthing.core')
             $scope.saveConfig();
         };
 
+        $scope.dismissFolderRejection = function (folder, device) {
+            delete $scope.folderRejections[folder + "-" + device];
+        };
+
         $scope.sharesFolder = function (folderCfg) {
             var names = [];
             folderCfg.Devices.forEach(function (device) {

File diff suppressed because it is too large
+ 0 - 0
internal/auto/gui.files.go


+ 11 - 7
internal/config/config.go

@@ -38,12 +38,13 @@ var l = logger.DefaultLogger
 const CurrentVersion = 7
 
 type Configuration struct {
-	Version int                   `xml:"version,attr"`
-	Folders []FolderConfiguration `xml:"folder"`
-	Devices []DeviceConfiguration `xml:"device"`
-	GUI     GUIConfiguration      `xml:"gui"`
-	Options OptionsConfiguration  `xml:"options"`
-	XMLName xml.Name              `xml:"configuration" json:"-"`
+	Version        int                   `xml:"version,attr"`
+	Folders        []FolderConfiguration `xml:"folder"`
+	Devices        []DeviceConfiguration `xml:"device"`
+	GUI            GUIConfiguration      `xml:"gui"`
+	Options        OptionsConfiguration  `xml:"options"`
+	IgnoredDevices []protocol.DeviceID   `xml:"ignoredDevice"`
+	XMLName        xml.Name              `xml:"configuration" json:"-"`
 
 	OriginalVersion         int                   `xml:"-" json:"-"` // The version we read from disk, before any conversion
 	Deprecated_Repositories []FolderConfiguration `xml:"repository" json:"-"`
@@ -241,10 +242,13 @@ func (cfg *Configuration) WriteXML(w io.Writer) error {
 func (cfg *Configuration) prepare(myID protocol.DeviceID) {
 	fillNilSlices(&cfg.Options)
 
-	// Initialize an empty slice for folders if the config has none
+	// Initialize an empty slices
 	if cfg.Folders == nil {
 		cfg.Folders = []FolderConfiguration{}
 	}
+	if cfg.IgnoredDevices == nil {
+		cfg.IgnoredDevices = []protocol.DeviceID{}
+	}
 
 	// Check for missing, bad or duplicate folder ID:s
 	var seenFolders = map[string]*FolderConfiguration{}

+ 13 - 0
internal/config/wrapper.go

@@ -245,6 +245,19 @@ func (w *Wrapper) InvalidateFolder(id string, err string) {
 	}
 }
 
+// Returns whether or not connection attempts from the given device should be
+// silently ignored.
+func (w *Wrapper) IgnoredDevice(id protocol.DeviceID) bool {
+	w.mut.Lock()
+	defer w.mut.Unlock()
+	for _, device := range w.cfg.IgnoredDevices {
+		if device == id {
+			return true
+		}
+	}
+	return false
+}
+
 // Save writes the configuration to disk, and generates a ConfigSaved event.
 func (w *Wrapper) Save() error {
 	fd, err := ioutil.TempFile(filepath.Dir(w.path), "cfg")

+ 1 - 1
internal/model/model.go

@@ -477,7 +477,7 @@ func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.F
 			"folder": folder,
 			"device": deviceID.String(),
 		})
-		l.Warnf("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
+		l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
 		return
 	}
 

Some files were not shown because too many files changed in this diff