Browse Source

- Implement Node and NetInterface classes

- Add Parsedown dependency for rendering the node documentation
- Rename templates/locations/ to templates/location/
- Modify URIs for locations and nodes
Bernhard Fröhlich 1 year ago
parent
commit
f840ab6ca3

+ 3
- 0
LEGAL View File

@@ -28,6 +28,9 @@ js/popper.min.js:
28 28
 js/jquery.slim.min.js:
29 29
   "jQuery" used under MIT License
30 30
 
31
+vendor/erusev:
32
+  "Parsedown" used under MIT License
33
+
31 34
 vendor/slim:
32 35
   "Slim Framework" used under MIT License
33 36
 

+ 1
- 0
composer.json View File

@@ -4,6 +4,7 @@
4 4
    "homepage": "https://github.com/decke/nodeman",
5 5
    "license": "BSD-2-Clause",
6 6
    "require": {
7
+      "erusev/parsedown": "1.*",
7 8
       "slim/slim": "^3.0",
8 9
       "slim/flash": "^0.2.0",
9 10
       "slim/php-view": "~2.0",

+ 43
- 1
composer.lock View File

@@ -4,7 +4,7 @@
4 4
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
5 5
         "This file is @generated automatically"
6 6
     ],
7
-    "content-hash": "b7dbff0428f0a12ed2b5854719dee5cc",
7
+    "content-hash": "424e00446aae856d9b313003b561f793",
8 8
     "packages": [
9 9
         {
10 10
             "name": "container-interop/container-interop",
@@ -37,6 +37,48 @@
37 37
             "homepage": "https://github.com/container-interop/container-interop",
38 38
             "time": "2017-02-14T19:40:03+00:00"
39 39
         },
40
+        {
41
+            "name": "erusev/parsedown",
42
+            "version": "1.6.3",
43
+            "source": {
44
+                "type": "git",
45
+                "url": "https://github.com/erusev/parsedown.git",
46
+                "reference": "728952b90a333b5c6f77f06ea9422b94b585878d"
47
+            },
48
+            "dist": {
49
+                "type": "zip",
50
+                "url": "https://api.github.com/repos/erusev/parsedown/zipball/728952b90a333b5c6f77f06ea9422b94b585878d",
51
+                "reference": "728952b90a333b5c6f77f06ea9422b94b585878d",
52
+                "shasum": ""
53
+            },
54
+            "require": {
55
+                "php": ">=5.3.0"
56
+            },
57
+            "type": "library",
58
+            "autoload": {
59
+                "psr-0": {
60
+                    "Parsedown": ""
61
+                }
62
+            },
63
+            "notification-url": "https://packagist.org/downloads/",
64
+            "license": [
65
+                "MIT"
66
+            ],
67
+            "authors": [
68
+                {
69
+                    "name": "Emanuil Rusev",
70
+                    "email": "hello@erusev.com",
71
+                    "homepage": "http://erusev.com"
72
+                }
73
+            ],
74
+            "description": "Parser for Markdown.",
75
+            "homepage": "http://parsedown.org",
76
+            "keywords": [
77
+                "markdown",
78
+                "parser"
79
+            ],
80
+            "time": "2017-05-14T14:47:48+00:00"
81
+        },
40 82
         {
41 83
             "name": "nikic/fast-route",
42 84
             "version": "v1.2.0",

+ 75
- 5
index.php View File

@@ -160,7 +160,7 @@ $app->get('/map.js', function ($request, $response) use ($session) {
160 160
 });
161 161
 
162 162
 /* Locations */
163
-$app->get('/locations/', function ($request, $response) use ($session) {
163
+$app->get('/locations', function ($request, $response) use ($session) {
164 164
     $loc = new Location();
165 165
 
166 166
     return $this->view->render($response, 'locations.html', array(
@@ -168,20 +168,20 @@ $app->get('/locations/', function ($request, $response) use ($session) {
168 168
     ));
169 169
 });
170 170
 
171
-$app->get('/locations/add', function ($request, $response) use ($session) {
171
+$app->get('/location/add', function ($request, $response) use ($session) {
172 172
     if (!$session->isAuthenticated()) {
173 173
         $this->flash->addMessage('error', 'Please login first');
174 174
 
175 175
         return $response->withStatus(302)->withHeader('Location', '/');
176 176
     }
177 177
 
178
-    return $this->view->render($response, 'locations/add.html', array(
178
+    return $this->view->render($response, 'location/add.html', array(
179 179
         'css' => array('/css/leaflet.css'),
180 180
         'js'  => array('/js/leaflet.js', '/js/grazmap.js')
181 181
     ));
182 182
 });
183 183
 
184
-$app->post('/locations/add', function ($request, $response) use ($session) {
184
+$app->post('/location/add', function ($request, $response) use ($session) {
185 185
     if (!$session->isAuthenticated()) {
186 186
         $this->flash->addMessage('error', 'Please login first');
187 187
 
@@ -232,11 +232,81 @@ $app->post('/locations/add', function ($request, $response) use ($session) {
232 232
         'address' => $request->getParam('address')
233 233
     );
234 234
 
235
-    return $this->view->render($response, 'locations/add.html', array(
235
+    return $this->view->render($response, 'location/add.html', array(
236 236
         'data' => $data,
237 237
         'css'  => array('/css/leaflet.css'),
238 238
         'js'   => array('/js/leaflet.js', '/js/grazmap.js')
239 239
     ));
240 240
 });
241 241
 
242
+
243
+/* Nodes */
244
+$app->get('/location/{locationid}/add', function ($request, $response, $args) use ($session) {
245
+    if (!$session->isAuthenticated()) {
246
+        $this->flash->addMessage('error', 'Please login first');
247
+
248
+        return $response->withStatus(302)->withHeader('Location', '/');
249
+    }
250
+     
251
+    return $this->view->render($response, 'location/node/add.html', array(
252
+        'data' => array('locationid' => $args['locationid'])
253
+    ));
254
+});
255
+
256
+$app->post('/location/{locationid}/add', function ($request, $response, $args) use ($session) {
257
+    if (!$session->isAuthenticated()) {
258
+        $this->flash->addMessage('error', 'Please login first');
259
+
260
+        return $response->withStatus(302)->withHeader('Location', '/');
261
+    }
262
+
263
+    $location = new Location($args['locationid']);
264
+    if ($location->owner != $session->getUser()->userid) {
265
+        $this->flash->addMessage('error', 'Permission denied');
266
+
267
+        return $response->withStatus(302)->withHeader('Location', '/');
268
+    }
269
+
270
+    if (!preg_match('/^[0-9A-Za-z]{3,50}$/', $request->getParam('name'))) {
271
+        $this->flash->addMessage('error', 'Node name is invalid. Length from 3-50. Allowed characters only 0-9, A-Z, a-z');
272
+    }
273
+    if (strlen($request->getParam('documentation')) > 16384) {
274
+        $this->flash->addMessage('error', 'Documentation is too long (max 16K)');
275
+    }
276
+
277
+    $location = new Location($args['locationid']);
278
+    if ($location->nodeExists($request->getParam('name'))) {
279
+        $this->flash->addMessage('error', 'Node name already exists');
280
+    }
281
+
282
+    /* HACK: Slim-Flash hasMessage('error') does not see messages for next request */
283
+    if (!isset($_SESSION['slimFlash']['error'])) {
284
+        $node = new node();
285
+        $node->name = $request->getParam('name');
286
+        $node->owner = $session->getUser()->userid;
287
+        $node->location = $args['locationid'],
288
+        $node->hardware = 0;
289
+        $node->documentation = $request->getParam('documentation');
290
+
291
+        if ($node->save()) {
292
+            $this->flash->addMessage('success', 'Node created');
293
+
294
+            return $response->withStatus(302)->withHeader('Location', '/location/'.$node->location.'/node/'.$node->nodeid.'/');
295
+        } else {
296
+            $this->flash->addMessage('error', 'Location creation failed');
297
+        }
298
+    }
299
+
300
+    $data = array(
301
+        'name'          => $request->getParam('name'),
302
+        'documentation' => $request->getParam('documentation'),
303
+        'locationid'    => $args['locationid']
304
+    );
305
+
306
+    return $this->view->render($response, 'location/node/add.html', array(
307
+        'data' => $data
308
+    ));
309
+});
310
+
311
+
242 312
 $app->run();

+ 114
- 0
lib/FunkFeuer/Nodeman/Interface.php View File

@@ -0,0 +1,114 @@
1
+<?php
2
+
3
+namespace FunkFeuer\Nodeman;
4
+
5
+/**
6
+ * Network Interface with assigned IP Address.
7
+ *
8
+ * @author     Bernhard Froehlich <decke@bluelife.at>
9
+ * @copyright  2017 Bernhard Froehlich
10
+ * @license    BSD License (2 Clause)
11
+ *
12
+ * @link       https://github.com/decke/nodeman
13
+ */
14
+class NetInterface
15
+{
16
+    private $_handle;
17
+    private $_data = array(
18
+        'interfaceid'   => null,
19
+        'name'          => null,
20
+        'node'          => null,
21
+        'category'      => null,
22
+        'type'          => null,
23
+        'address'       => null,
24
+        'status'        => null,
25
+        'ping'          => null,
26
+        'comment'       => null
27
+    );
28
+
29
+    public function __construct($interfaceid = null)
30
+    {
31
+        $this->_handle = Config::getDbHandle();
32
+
33
+        if ($interfaceid !== null) {
34
+            $this->load($interfaceid);
35
+        }
36
+    }
37
+
38
+    public function __get($name)
39
+    {
40
+        if (array_key_exists($name, $this->_data)) {
41
+            return $this->_data[$name];
42
+        }
43
+
44
+        throw new \Exception('Undefined property '.$name.' in class '.__CLASS__);
45
+    }
46
+
47
+    public function __set($name, $value)
48
+    {
49
+        if (array_key_exists($name, $this->_data)) {
50
+            $this->_data[$name] = $value;
51
+
52
+            return true;
53
+        }
54
+
55
+        throw new \Exception('Undefined property '.$name.' in class '.__CLASS__);
56
+    }
57
+
58
+    public function load($id)
59
+    {
60
+        $stmt = $this->_handle->prepare('SELECT interfaceid, name, node, category, type, address,
61
+            status, ping, comment FROM interfaces WHERE interfaceid = ?');
62
+        if (!$stmt->execute(array($id))) {
63
+            return false;
64
+        }
65
+
66
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
67
+            $this->_data = $row;
68
+
69
+            return true;
70
+        }
71
+
72
+        return false;
73
+    }
74
+
75
+    public function loadByName($name)
76
+    {
77
+        $stmt = $this->_handle->prepare('SELECT interfaceid, name, node, category, type, address,
78
+            status, ping, comment FROM interfaces WHERE name = ?');
79
+        if (!$stmt->execute(array($name))) {
80
+            return false;
81
+        }
82
+
83
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
84
+            $this->_data = $row;
85
+
86
+            return true;
87
+        }
88
+
89
+        return false;
90
+    }
91
+
92
+    public function save()
93
+    {
94
+        if (!$this->locationid) {
95
+            $stmt = $this->_handle->prepare('INSERT INTO interfaces (name, node, category, type, address,
96
+                status, ping, comment) VALUES (?, ?, ?, ?, ?, ?, ?, ?)');
97
+
98
+            if ($stmt->execute(array($this->name, $this->node, $this->category, $this->type,
99
+                $this->address, $this->status, $this->ping, $this->comment))) {
100
+                $this->interfaceid = $this->_handle->lastInsertId();
101
+
102
+                return true;
103
+            }
104
+        } else {
105
+            $stmt = $this->_handle->prepare('UPDATE interfaces SET name = ?, node = ?, category = ?,
106
+                type = ?, address = ?, status = ?, ping = ?, comment = ? WHERE interfaceid = ?');
107
+
108
+            return $stmt->execute(array($this->name, $this->node, $this->category, $this->type,
109
+                $this->address, $this->status, $this->ping, $this->comment, $this->interfaceid));
110
+        }
111
+
112
+        return false;
113
+    }
114
+}

+ 26
- 0
lib/FunkFeuer/Nodeman/Location.php View File

@@ -142,4 +142,30 @@ class Location
142 142
 
143 143
         return $stmt->fetch(\PDO::FETCH_BOTH)[0];
144 144
     }
145
+
146
+    public function getAllNodes()
147
+    {
148
+        $data = array();
149
+
150
+        $stmt = $this->_handle->prepare('SELECT nodeid FROM nodes WHERE (location = ? OR ? IS NULL) ORDER BY nodeid');
151
+        if (!$stmt->execute(array($this->location, $this->location))) {
152
+            return $data;
153
+        }
154
+
155
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
156
+            $data[] = new Node($row['nodeid']);
157
+        }
158
+
159
+        return $data;
160
+    }
161
+
162
+    public function nodeExists($name)
163
+    {
164
+        $stmt = $this->_handle->prepare('SELECT count(*) FROM nodes WHERE location = ? AND LOWER(name) = LOWER(?)');
165
+        if (!$stmt->execute(array($this->location, $name))) {
166
+            return false;
167
+        }
168
+
169
+        return $stmt->fetch(\PDO::FETCH_BOTH)[0] > 0;
170
+    }
145 171
 }

+ 127
- 0
lib/FunkFeuer/Nodeman/Node.php View File

@@ -0,0 +1,127 @@
1
+<?php
2
+
3
+namespace FunkFeuer\Nodeman;
4
+
5
+/**
6
+ * Node implementation which groups together various interfaces.
7
+ *
8
+ * @author     Bernhard Froehlich <decke@bluelife.at>
9
+ * @copyright  2017 Bernhard Froehlich
10
+ * @license    BSD License (2 Clause)
11
+ *
12
+ * @link       https://github.com/decke/nodeman
13
+ */
14
+class Node
15
+{
16
+    private $_handle;
17
+    private $_data = array(
18
+        'nodeid'        => null,
19
+        'name'          => null,
20
+        'owner'         => null,
21
+        'location'      => null,
22
+        'hardware'      => null,
23
+        'documentation' => null
24
+    );
25
+
26
+    public function __construct($nodeid = null)
27
+    {
28
+        $this->_handle = Config::getDbHandle();
29
+
30
+        if ($nodeid !== null) {
31
+            $this->load($nodeid);
32
+        }
33
+    }
34
+
35
+    public function __get($name)
36
+    {
37
+        if (array_key_exists($name, $this->_data)) {
38
+            return $this->_data[$name];
39
+        }
40
+
41
+        throw new \Exception('Undefined property '.$name.' in class Node');
42
+    }
43
+
44
+    public function __set($name, $value)
45
+    {
46
+        if (array_key_exists($name, $this->_data)) {
47
+            $this->_data[$name] = $value;
48
+
49
+            return true;
50
+        }
51
+
52
+        throw new \Exception('Undefined property '.$name.' in class Node');
53
+    }
54
+
55
+    public function load($id)
56
+    {
57
+        $stmt = $this->_handle->prepare('SELECT nodeid, name, owner, location, hardware,
58
+            documentation FROM nodes WHERE nodeid = ?');
59
+        if (!$stmt->execute(array($id))) {
60
+            return false;
61
+        }
62
+
63
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
64
+            $this->_data = $row;
65
+
66
+            return true;
67
+        }
68
+
69
+        return false;
70
+    }
71
+
72
+    public function loadByName($name)
73
+    {
74
+        $stmt = $this->_handle->prepare('SELECT nodeid, name, owner, location, hardware,
75
+            documentation FROM nodes WHERE name = ?');
76
+        if (!$stmt->execute(array($name))) {
77
+            return false;
78
+        }
79
+
80
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
81
+            $this->_data = $row;
82
+
83
+            return true;
84
+        }
85
+
86
+        return false;
87
+    }
88
+
89
+    public function save()
90
+    {
91
+        if (!$this->locationid) {
92
+            $stmt = $this->_handle->prepare('INSERT INTO nodes (name, owner, location, hardware,
93
+                documentation) VALUES (?, ?, ?, ?, ?)');
94
+
95
+            if ($stmt->execute(array($this->name, $this->owner, $this->location, $this->hardware,
96
+                $this->documentation))) {
97
+                $this->nodeid = $this->_handle->lastInsertId();
98
+
99
+                return true;
100
+            }
101
+        } else {
102
+            $stmt = $this->_handle->prepare('UPDATE nodes SET name = ?, owner = ?, location = ?,
103
+                hardware = ?, documentation = ? WHERE nodeid = ?');
104
+
105
+            return $stmt->execute(array($this->name, $this->owner, $this->location, $this->hardware,
106
+                $this->documentation, $this->nodeid));
107
+        }
108
+
109
+        return false;
110
+    }
111
+
112
+    public function getAllInterfaces()
113
+    {
114
+        $data = array();
115
+
116
+        $stmt = $this->_handle->prepare('SELECT interfaceid FROM interfaces WHERE (node = ? OR ? IS NULL) ORDER BY interfacid');
117
+        if (!$stmt->execute(array($this->node, $this->node))) {
118
+            return $data;
119
+        }
120
+
121
+        while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
122
+            $data[] = new Interface($row['interfaceid']);
123
+        }
124
+
125
+        return $data;
126
+    }
127
+}

+ 5
- 2
templates/_header.html View File

@@ -28,16 +28,19 @@
28 28
           <li class="nav-item active">
29 29
             <a class="nav-link" href="/">Home <span class="sr-only">(current)</span></a>
30 30
           </li>
31
-          <li class="nav-item">
31
+          <li class="nav-item active">
32 32
             <a class="nav-link" href="/map">Map</a>
33 33
           </li>
34
+          <li class="nav-item active">
35
+            <a class="nav-link" href="/locations">Locations</a>
36
+          </li>
34 37
           {% if not session.isAuthenticated() %}
35 38
           <li class="nav-item active">
36 39
             <a class="nav-link" href="/register">Register</a>
37 40
           </li>
38 41
           {% else %}
39 42
           <li class="nav-item active">
40
-            <a class="nav-link" href="/locations/add">Add Location</a>
43
+            <a class="nav-link" href="/location/add">Add Location</a>
41 44
           </li>
42 45
           {% endif %}
43 46
         </ul>

templates/locations/add.html → templates/location/add.html View File


+ 32
- 0
templates/location/node/add.html View File

@@ -0,0 +1,32 @@
1
+{% include('_header.html') %}
2
+
3
+    <div class="container">
4
+
5
+      <h1>Create new Node</h1>
6
+
7
+      {% for msg in flash.getMessage('error') %}
8
+        <div class="alert alert-danger" role="alert"><strong>Sorry</strong> {{ msg }}</div>
9
+      {% endfor %}
10
+
11
+      <form action="/location/{{ data['locationid'] }}/add" method="post">
12
+        <div class="form-group">
13
+          <label for="name">Node name</label>
14
+          <input type="text" id="name" name="name" class="form-control" aria-describedby="nameHelp" value="{{ data['name'] }}">
15
+          <small id="nameHelp" class="form-text text-muted">
16
+            Length from 3-50. Allowed characters only 0-9, A-Z, a-z.
17
+          </small>
18
+        </div>
19
+
20
+        <div class="form-group">
21
+          <label for="documentation">Documentation</label>
22
+          <textarea id="documentation" name="documentation" class="form-control" aria-describedby="documentationHelp">{{ data['documentation'] }}</textarea>
23
+          <small id="documentationHelp" class="form-text text-muted">
24
+            Please use Markdown Syntax to format your node documentation. <a href="https://daringfireball.net/projects/markdown/basics" target="_blank">Markdown examples</a> and <a href="https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet" target="_blank">more examples</a>.</p>
25
+          </small>
26
+        </div>
27
+
28
+        <button type="submit" class="btn btn-primary">Create</button>
29
+      </form>
30
+    </div>
31
+
32
+{% include('_footer.html') %}

Loading…
Cancel
Save