From 79aadc85c2992cb504bcfc477b7ba838c52fd1aa Mon Sep 17 00:00:00 2001
From: baldo
Date: Mon, 16 May 2016 18:27:03 +0200
Subject: [PATCH] Added delete feature for nodes.
---
app/index.html | 2 +
app/scripts/app.js | 8 +++
app/scripts/controllers/deleteNodeCtrl.js | 26 +++++++++
app/scripts/controllers/main.js | 4 ++
app/scripts/dialogs/confirmDeletionDialog.js | 29 ++++++++++
app/scripts/directives/tokenForm.js | 4 +-
app/scripts/services/nodeService.js | 4 ++
app/styles/main.scss | 2 +
app/styles/views/_deleteNodeForm.scss | 44 +++++++++++++++
app/styles/views/_main.scss | 20 +++++--
.../views/dialogs/_confirmDeletionDialog.scss | 33 +++++++++++
app/styles/views/directives/_tokenForm.scss | 4 ++
app/views/deleteNodeForm.html | 56 +++++++++++++++++++
app/views/dialogs/confirmDeletionDialog.html | 18 ++++++
app/views/directives/tokenForm.html | 2 +-
app/views/main.html | 24 ++++----
app/views/updateNodeForm.html | 2 +-
server/resources/nodeResource.js | 16 ++++++
server/router.js | 1 +
server/services/nodeService.js | 21 +++++++
20 files changed, 300 insertions(+), 20 deletions(-)
create mode 100644 app/scripts/controllers/deleteNodeCtrl.js
create mode 100644 app/scripts/dialogs/confirmDeletionDialog.js
create mode 100644 app/styles/views/_deleteNodeForm.scss
create mode 100644 app/styles/views/dialogs/_confirmDeletionDialog.scss
create mode 100644 app/views/deleteNodeForm.html
create mode 100644 app/views/dialogs/confirmDeletionDialog.html
diff --git a/app/index.html b/app/index.html
index 445591b..dd1d66a 100644
--- a/app/index.html
+++ b/app/index.html
@@ -49,6 +49,7 @@
+
@@ -58,6 +59,7 @@
+
diff --git a/app/scripts/app.js b/app/scripts/app.js
index 7c24975..a5f9d85 100644
--- a/app/scripts/app.js
+++ b/app/scripts/app.js
@@ -25,6 +25,11 @@ angular.module('ffffng', [
controller: 'UpdateNodeCtrl',
title: 'Knotendaten ändern'
})
+ .when('/delete', {
+ templateUrl: 'views/deleteNodeForm.html',
+ controller: 'DeleteNodeCtrl',
+ title: 'Knoten löschen'
+ })
.otherwise({
redirectTo: '/'
});
@@ -39,6 +44,9 @@ angular.module('ffffng', [
},
updateNode: function () {
$location.url('/update');
+ },
+ deleteNode: function () {
+ $location.url('/delete');
}
};
})
diff --git a/app/scripts/controllers/deleteNodeCtrl.js b/app/scripts/controllers/deleteNodeCtrl.js
new file mode 100644
index 0000000..c935854
--- /dev/null
+++ b/app/scripts/controllers/deleteNodeCtrl.js
@@ -0,0 +1,26 @@
+'use strict';
+
+angular.module('ffffng')
+.controller('DeleteNodeCtrl', function ($scope, Navigator, NodeService, config, ConfirmDeletionDialog) {
+ $scope.config = config;
+ $scope.token = undefined;
+ $scope.deleted = false;
+
+ $scope.onSubmitToken = function (token) {
+ $scope.token = token;
+ return NodeService.getNode(token)
+ .success(function (node) {
+ ConfirmDeletionDialog.open(node).result.then(function () {
+ NodeService.deleteNode(token)
+ .success(function () {
+ $scope.deleted = true;
+ $scope.hostname = node.hostname;
+ });
+ });
+ });
+ };
+
+ $scope.cancel = function () {
+ Navigator.home();
+ };
+});
diff --git a/app/scripts/controllers/main.js b/app/scripts/controllers/main.js
index a845ca2..7dd0518 100644
--- a/app/scripts/controllers/main.js
+++ b/app/scripts/controllers/main.js
@@ -11,4 +11,8 @@ angular.module('ffffng')
$scope.updateNode = function () {
Navigator.updateNode();
};
+
+ $scope.deleteNode = function () {
+ Navigator.deleteNode();
+ };
});
diff --git a/app/scripts/dialogs/confirmDeletionDialog.js b/app/scripts/dialogs/confirmDeletionDialog.js
new file mode 100644
index 0000000..23e00e3
--- /dev/null
+++ b/app/scripts/dialogs/confirmDeletionDialog.js
@@ -0,0 +1,29 @@
+'use strict';
+
+angular.module('ffffng')
+.factory('ConfirmDeletionDialog', function ($uibModal, config) {
+ var ctrl = function ($scope, $uibModalInstance, node) {
+ $scope.node = node;
+ $scope.config = config;
+
+ $scope.proceed = function () {
+ $uibModalInstance.close();
+ };
+
+ $scope.cancel = function () {
+ $uibModalInstance.dismiss('cancel');
+ };
+ };
+
+ return {
+ open: function (node) {
+ return $uibModal.open({
+ controller: ctrl,
+ templateUrl: 'views/dialogs/confirmDeletionDialog.html',
+ resolve: {
+ node: function () { return node; }
+ }
+ });
+ }
+ };
+});
diff --git a/app/scripts/directives/tokenForm.js b/app/scripts/directives/tokenForm.js
index 749b43a..2c19f4f 100644
--- a/app/scripts/directives/tokenForm.js
+++ b/app/scripts/directives/tokenForm.js
@@ -33,7 +33,9 @@ angular.module('ffffng')
'templateUrl': 'views/directives/tokenForm.html',
'scope': {
'onSubmit': '=fSubmit',
- 'onCancel': '=fCancel'
+ 'onCancel': '=fCancel',
+ 'submitIcon': '@fSubmitIcon',
+ 'submitLabel': '@fSubmitLabel'
}
};
});
diff --git a/app/scripts/services/nodeService.js b/app/scripts/services/nodeService.js
index 7bd9825..67f68b3 100644
--- a/app/scripts/services/nodeService.js
+++ b/app/scripts/services/nodeService.js
@@ -11,6 +11,10 @@ angular.module('ffffng')
return $http.put('/api/node/' + token, node);
},
+ 'deleteNode': function (token) {
+ return $http.delete('/api/node/' + token);
+ },
+
'getNode': function (token) {
return $http.get('/api/node/' + token);
}
diff --git a/app/styles/main.scss b/app/styles/main.scss
index 1f404f5..948806b 100644
--- a/app/styles/main.scss
+++ b/app/styles/main.scss
@@ -8,10 +8,12 @@
@import "views/_main";
@import "views/_newNodeForm";
@import "views/_updateNodeForm";
+@import "views/_deleteNodeForm";
@import "views/directives/_help";
@import "views/directives/_nodeForm";
@import "views/directives/_nodeSaved";
@import "views/directives/_tokenForm";
+@import "views/dialogs/_confirmDeletionDialog";
@import "views/dialogs/_outsideOfCommunityDialog";
body {
diff --git a/app/styles/views/_deleteNodeForm.scss b/app/styles/views/_deleteNodeForm.scss
new file mode 100644
index 0000000..8cebebd
--- /dev/null
+++ b/app/styles/views/_deleteNodeForm.scss
@@ -0,0 +1,44 @@
+.delete-node-form {
+ @extend .container;
+}
+
+.node-deleted {
+ @extend .jumbotron, .container;
+
+ .summary, .actions {
+ text-align: center;
+ }
+
+ .actions {
+ margin-bottom: 10px;
+ }
+
+ .token-hint {
+ font: {
+ weight: bold;
+ size: 32px;
+ }
+
+ text-align: center;
+ margin-top: 32px;
+ }
+
+ .summary {
+ .node {
+ @extend .well;
+
+ border: 3px dashed $gray;
+ font-family: monospace;
+ font-size: 32px;
+ }
+
+ margin: {
+ top: 30px;
+ bottom: 30px;
+ }
+ }
+
+ .back-button {
+ @extend .btn, .btn-lg, .btn-info;
+ }
+}
diff --git a/app/styles/views/_main.scss b/app/styles/views/_main.scss
index 2101100..c213b19 100644
--- a/app/styles/views/_main.scss
+++ b/app/styles/views/_main.scss
@@ -9,11 +9,19 @@
bottom: 25px;
}
- .new-node, .update-node {
- @extend .col-md-5;
+ .new-node, .update-node, .delete-node {
+ @extend .col-md-4;
button {
@extend .btn, .btn-lg, .btn-block;
+
+ text-overflow: ellipsis;
+ overflow: hidden;
+
+ padding: {
+ left: 10px;
+ right: 10px;
+ };
}
}
@@ -29,10 +37,10 @@
}
}
- .or {
- @extend .col-md-2;
-
- text-align: center;
+ .delete-node {
+ button {
+ //@extend .btn-link;
+ }
}
}
}
diff --git a/app/styles/views/dialogs/_confirmDeletionDialog.scss b/app/styles/views/dialogs/_confirmDeletionDialog.scss
new file mode 100644
index 0000000..efee618
--- /dev/null
+++ b/app/styles/views/dialogs/_confirmDeletionDialog.scss
@@ -0,0 +1,33 @@
+.confirm-deletion-dialog {
+ .modal-header {
+ h3 {
+ @extend .modal-title;
+ }
+
+ .cancel-icon {
+ @extend .fa, .fa-times;
+
+ position: relative;
+ float: right;
+
+ top: 10px;
+ right: 0;
+
+ margin-left: 15px;
+
+ cursor: pointer;
+ color: $gray;
+ }
+ }
+ .modal-footer {
+ .proceed {
+ @extend .btn;
+
+ margin-left: 5px;
+
+ &.proceed {
+ @extend .btn-primary;
+ }
+ }
+ }
+}
diff --git a/app/styles/views/directives/_tokenForm.scss b/app/styles/views/directives/_tokenForm.scss
index 51e57da..977fd86 100644
--- a/app/styles/views/directives/_tokenForm.scss
+++ b/app/styles/views/directives/_tokenForm.scss
@@ -1,4 +1,8 @@
f-token-form {
+ form {
+ margin-bottom: 10px;
+ }
+
.main-error {
@extend .alert, .alert-danger;
}
diff --git a/app/views/deleteNodeForm.html b/app/views/deleteNodeForm.html
new file mode 100644
index 0000000..2e93085
--- /dev/null
+++ b/app/views/deleteNodeForm.html
@@ -0,0 +1,56 @@
+
+
+
+
Erledigt!
+
+ Die Daten Deines Freifunk-Knotens sind gelöscht worden. Es kann jetzt noch bis zu 20 Minuten dauern,
+ bis die Änderungen überall wirksam werden und sich im Knotengraph
+ und in der Knotenkarte auswirken.
+
+
+
+
+
+ {{hostname}}
+
+
+
+
+ Zurück zum Anfang
+
+
+
+
+ Hinweis: Nach dem Löschen kann der Knoten ggf. weiterhin in der Knotenkarte angezeigt werden. Dies
+ ist dann der Fall, wenn der Knoten eingeschaltet ist und in Reichweite eines anderen aktiven Knotens
+ steht. Die angezeigten Daten sind dann die während der Einrichtung des Knotens im Config-Mode
+ (Konfigurationsoberfläche des Routers) hinterlegten.
+
+
+
diff --git a/app/views/dialogs/confirmDeletionDialog.html b/app/views/dialogs/confirmDeletionDialog.html
new file mode 100644
index 0000000..33e56af
--- /dev/null
+++ b/app/views/dialogs/confirmDeletionDialog.html
@@ -0,0 +1,18 @@
+
+
+
+
+ Soll der Knoten „{{node.hostname}}“ wirklich endgültig gelöscht werden?
+ Du kannst ihn selbstverständlich später jederzeit erneut anmelden!
+
+
+
+
diff --git a/app/views/directives/tokenForm.html b/app/views/directives/tokenForm.html
index 2c4cb98..edf205f 100644
--- a/app/views/directives/tokenForm.html
+++ b/app/views/directives/tokenForm.html
@@ -10,7 +10,7 @@
diff --git a/app/views/updateNodeForm.html b/app/views/updateNodeForm.html
index 67ab42a..2498bc5 100644
--- a/app/views/updateNodeForm.html
+++ b/app/views/updateNodeForm.html
@@ -13,7 +13,7 @@
{{ config.community.contactEmail }} .
-
+
diff --git a/server/resources/nodeResource.js b/server/resources/nodeResource.js
index 130a112..33233df 100644
--- a/server/resources/nodeResource.js
+++ b/server/resources/nodeResource.js
@@ -80,6 +80,22 @@ angular.module('ffffng').factory('NodeResource', function (
});
},
+ delete: function (req, res) {
+ var data = getData(req);
+
+ var token = Strings.normalizeString(data.token);
+ if (!isValidToken(token)) {
+ return error(res, {data: 'Invalid token.', type: ErrorTypes.badRequest});
+ }
+
+ return NodeService.deleteNode(token, function (err) {
+ if (err) {
+ return error(res, err);
+ }
+ return success(res, {});
+ });
+ },
+
get: function (req, res) {
var token = Strings.normalizeString(getData(req).token);
if (!isValidToken(token)) {
diff --git a/server/router.js b/server/router.js
index 802554e..0fb78b4 100644
--- a/server/router.js
+++ b/server/router.js
@@ -5,6 +5,7 @@ angular.module('ffffng').factory('Router', function (app, NodeResource) {
init: function () {
app.post('/api/node', NodeResource.create);
app.put('/api/node/:token', NodeResource.update);
+ app.delete('/api/node/:token', NodeResource.delete);
app.get('/api/node/:token', NodeResource.get);
}
};
diff --git a/server/services/nodeService.js b/server/services/nodeService.js
index 07a946b..5dc24d0 100644
--- a/server/services/nodeService.js
+++ b/server/services/nodeService.js
@@ -101,6 +101,23 @@ angular.module('ffffng')
return callback(null, token, node);
}
+ function deleteNodeFile(token, callback) {
+ var files = findNodeFiles('*@*@*@' + token);
+ if (files.length !== 1) {
+ return callback({data: 'Node not found.', type: ErrorTypes.notFound});
+ }
+
+ try {
+ fs.unlinkSync(files[0]);
+ }
+ catch (error) {
+ console.log(error);
+ return callback({data: 'Could not delete node.', type: ErrorTypes.internalError});
+ }
+
+ return callback(null);
+ }
+
function parseNodeFile(file, callback) {
var lines = fs.readFileSync(file).toString();
@@ -140,6 +157,10 @@ angular.module('ffffng')
writeNodeFile(true, token, node, callback);
},
+ deleteNode: function (token, callback) {
+ deleteNodeFile(token, callback);
+ },
+
getNodeData: function (token, callback) {
var files = findNodeFiles('*@*@*@' + token);