Browse Source

Implement Nextcloud Notes API, close #2

master
Skylar Ittner 6 months ago
parent
commit
88b2146135
5 changed files with 226 additions and 14 deletions
  1. 5
    0
      .htaccess
  2. 5
    5
      action.php
  3. BIN
      database.mwb
  4. 104
    9
      lib/Note.lib.php
  5. 112
    0
      lib/nextcloudnotes.php

+ 5
- 0
.htaccess View File

@@ -0,0 +1,5 @@
1
+# Rewrite for Nextcloud Notes API
2
+<IfModule mod_rewrite.c>
3
+    RewriteEngine on
4
+    RewriteRule index.php/apps/notes/api/v0.2 lib/nextcloudnotes.php [PT]
5
+</IfModule>

+ 5
- 5
action.php View File

@@ -35,13 +35,13 @@ switch ($VARS['action']) {
35 35
         header('Location: index.php');
36 36
         die("Logged out.");
37 37
     case "savenote":
38
-        if (empty($VARS['content']) || empty($VARS['noteid'])) {
39
-            die($Strings->get("invalid parameters"));
38
+        if (!isset($VARS['content']) || empty($VARS['noteid'])) {
39
+            die($Strings->get("invalid parameters", false));
40 40
         }
41 41
         http_response_code(204);
42 42
         $note = Note::loadNote($VARS['noteid']);
43
-        if ($note->getOwnerID() != $_SESSION['uid']) {
44
-            die($Strings->get("invalid parameters"));
43
+        if (!$note->hasWriteAccess(new User($_SESSION['uid']))) {
44
+            die($Strings->get("invalid parameters", false));
45 45
         }
46 46
         $note->setText($VARS['content']);
47 47
         $note->setColor($VARS['color']);
@@ -52,7 +52,7 @@ switch ($VARS['action']) {
52 52
             die($Strings->get("invalid parameters"));
53 53
         }
54 54
         $note = Note::loadNote($VARS['noteid']);
55
-        if ($note->getOwnerID() != $_SESSION['uid']) {
55
+        if (!$note->hasWriteAccess(new User($_SESSION['uid']))) {
56 56
             die($Strings->get("invalid parameters"));
57 57
         }
58 58
         $note->deleteNote();

BIN
database.mwb View File


+ 104
- 9
lib/Note.lib.php View File

@@ -11,6 +11,8 @@ class Note {
11 11
     private $noteid;
12 12
     private $ownerid;
13 13
     private $content = "";
14
+    private $title = "";
15
+    private $modified = "";
14 16
     private $color = "FFFFFF";
15 17
 
16 18
     /**
@@ -40,9 +42,13 @@ class Note {
40 42
             throw new NoSuchNoteException();
41 43
         }
42 44
 
43
-        $notedata = $database->get('notes', ['noteid', 'ownerid', 'color', 'content'], ['noteid' => $noteid]);
45
+        $notedata = $database->get('notes', ['noteid', 'ownerid', 'color', 'content', 'title', 'modified'], ['noteid' => $noteid]);
44 46
 
45
-        return new Note($notedata['content'], $notedata['color'], $notedata['ownerid'], $notedata['noteid']);
47
+        $note = new Note($notedata['content'], $notedata['color'], $notedata['ownerid'], $notedata['noteid']);
48
+        $note->setTitle(is_null($notedata['title']) ? "" : $notedata['title']);
49
+        $note->setModified(is_null($notedata['modified']) ? date("Y-m-d H:i:s") : $notedata['modified']);
50
+
51
+        return $note;
46 52
     }
47 53
 
48 54
     /**
@@ -57,9 +63,11 @@ class Note {
57 63
         global $database;
58 64
 
59 65
         $data = [
60
-            'ownerid' => $this->ownerid,
61
-            'color' => $this->color,
62
-            'content' => $this->content
66
+            'ownerid' => $this->getOwnerID(),
67
+            'color' => $this->getColor(),
68
+            'content' => $this->getText(),
69
+            'title' => $this->getTitle(),
70
+            'modified' => $this->getModified()
63 71
         ];
64 72
 
65 73
         // We can't UPDATE the database, so use save as for INSERT
@@ -142,6 +150,34 @@ class Note {
142 150
         return new User($this->ownerid);
143 151
     }
144 152
 
153
+    /**
154
+     * Get the note title
155
+     * @return string
156
+     */
157
+    public function getTitle(): string {
158
+        global $Strings;
159
+        $title = $this->title;
160
+        if (empty($title)) {
161
+            if (empty($this->getText())) {
162
+                $title = $Strings->get("New note", false);
163
+            } else {
164
+                $title = explode("\n", $this->getText())[0];
165
+            }
166
+        }
167
+        return $title;
168
+    }
169
+
170
+    /**
171
+     * Get the last modified date/time as "Y-m-d H:i:s"
172
+     * @return string
173
+     */
174
+    public function getModified(): string {
175
+        if (empty($this->modified)) {
176
+            return date("Y-m-d H:i:s");
177
+        }
178
+        return date("Y-m-d H:i:s", strtotime($this->modified));
179
+    }
180
+
145 181
     /**
146 182
      * Set the note content
147 183
      * @param string $markdown
@@ -174,6 +210,22 @@ class Note {
174 210
         $this->ownerid = $owner->getUID();
175 211
     }
176 212
 
213
+    /**
214
+     * Set the note title
215
+     * @param string $title
216
+     */
217
+    public function setTitle(string $title) {
218
+        $this->title = $title;
219
+    }
220
+
221
+    /**
222
+     * Set the last modified date/time
223
+     * @param string $datetime
224
+     */
225
+    public function setModified(string $datetime) {
226
+        $this->modified = date("Y-m-d H:i:s", strtotime($datetime));
227
+    }
228
+
177 229
     /**
178 230
      * Get this note as an array.
179 231
      * @return string
@@ -181,9 +233,11 @@ class Note {
181 233
     public function toArray(): array {
182 234
         $owner = new User($this->ownerid);
183 235
         $arr = [
184
-            'noteid' => $this->noteid,
185
-            'color' => $this->color,
186
-            'content' => $this->content,
236
+            'noteid' => $this->getID(),
237
+            'color' => $this->getColor(),
238
+            'content' => $this->getText(),
239
+            'title' => $this->getTitle(),
240
+            'modified' => $this->getModified(),
187 241
             'owner' => [
188 242
                 'uid' => $owner->getUID(),
189 243
                 'username' => $owner->getUsername(),
@@ -194,13 +248,54 @@ class Note {
194 248
         return $arr;
195 249
     }
196 250
 
251
+    /**
252
+     * Get an array suitable for returning via the Nextcloud Notes API
253
+     * https://github.com/nextcloud/notes/wiki/Notes-0.2#get-a-note
254
+     * @return array
255
+     */
256
+    public function toNextcloud(): array {
257
+        return [
258
+            "id" => $this->getID(),
259
+            "modified" => strtotime($this->getModified()),
260
+            "title" => $this->getTitle(),
261
+            "category" => null,
262
+            "content" => $this->getText(),
263
+            "favorite" => false
264
+        ];
265
+    }
266
+
267
+    /**
268
+     * Check whether the given User has read access to this note.
269
+     * @param User $user
270
+     */
271
+    public function hasReadAccess(User $user): bool {
272
+        if ($this->getOwnerID() == $user->getUID()) {
273
+            return true;
274
+        }
275
+        return false;
276
+    }
277
+
278
+    /**
279
+     * Check whether the given User has write access to this note.
280
+     * @param User $user
281
+     */
282
+    public function hasWriteAccess(User $user): bool {
283
+        if ($this->getOwnerID() == $user->getUID()) {
284
+            return true;
285
+        }
286
+        return false;
287
+    }
288
+
197 289
     /**
198 290
      * Read an array with the structure from toArray().
199 291
      * @param array $arr
200 292
      * @return \Note A Note constructed from the array data.
201 293
      */
202 294
     public static function fromArray(array $arr): Note {
203
-        return new Note($arr['content'], $arr['color'], $arr['owner']['uid'], $arr['noteid']);
295
+        $note = new Note($arr['content'], $arr['color'], $arr['owner']['uid'], $arr['noteid']);
296
+        $note->setTitle($arr['title']);
297
+        $note->setModified($arr['modified']);
298
+        return $note;
204 299
     }
205 300
 
206 301
 }

+ 112
- 0
lib/nextcloudnotes.php View File

@@ -0,0 +1,112 @@
1
+<?php
2
+
3
+/*
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+require __DIR__ . "/../required.php";
10
+
11
+header("Content-Security-Policy: default-src '*';");
12
+header('Access-Control-Allow-Origin: *');
13
+
14
+// Get the API route/action
15
+$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/v0.2/") + 9));
16
+
17
+// HTTP authentication
18
+if (!isset($_SERVER['PHP_AUTH_USER'])) {
19
+    header('WWW-Authenticate: Basic realm="' . SITE_TITLE . '"');
20
+    header('HTTP/1.0 401 Unauthorized');
21
+    exit;
22
+} else {
23
+    $user = User::byUsername($_SERVER['PHP_AUTH_USER']);
24
+    if (!$user->checkPassword($_SERVER['PHP_AUTH_PW'])) {
25
+        header('HTTP/1.0 401 Unauthorized');
26
+        die($Strings->get("login incorrect"));
27
+    }
28
+}
29
+
30
+header("Content-Type: application/json");
31
+
32
+$requestdata = $_GET;
33
+$requestbody = file_get_contents('php://input');
34
+$requestjson = json_decode($requestbody, TRUE);
35
+if (json_last_error() == JSON_ERROR_NONE) {
36
+    $requestdata = array_merge($requestdata, $requestjson);
37
+}
38
+
39
+switch ($_SERVER['REQUEST_METHOD']) {
40
+    case "GET":
41
+        if (count($route) == 1) {
42
+            $noteids = $database->select('notes', 'noteid', ['ownerid' => $user->getUID()]);
43
+            $notes = [];
44
+            foreach ($noteids as $n) {
45
+                $notes[] = Note::loadNote($n)->toNextcloud();
46
+            }
47
+            exit(json_encode($notes));
48
+        } else if (count($route) == 2 && is_numeric($route[1])) {
49
+            try {
50
+                $note = Note::loadNote($route[1]);
51
+                if ($note->getOwner()->getUID() == $user->getUID()) {
52
+                    exit(json_encode($note->toNextcloud()));
53
+                } else {
54
+                    http_response_code(401);
55
+                }
56
+            } catch (NoSuchNoteException $ex) {
57
+                http_response_code(404);
58
+            }
59
+        }
60
+        break;
61
+    case "POST":
62
+        $note = new Note($requestdata['content']);
63
+
64
+        if (empty($requestdata['modified']) || !is_numeric($requestdata['modified'])) {
65
+            $note->setModified(date("Y-m-d H:i:s"));
66
+        } else {
67
+            $note->setModified($requestdata['modified']);
68
+        }
69
+
70
+        $note->setOwner($user);
71
+
72
+        $note->saveNote();
73
+
74
+        exit(json_encode($note->toNextcloud()));
75
+        break;
76
+    case "PUT":
77
+        if (count($route) == 2 && is_numeric($route[1])) {
78
+            try {
79
+                $note = Note::loadNote($route[1]);
80
+                if ($note->hasWriteAccess($user)) {
81
+                    $note->setText($requestdata['content']);
82
+                    if (empty($requestdata['modified']) || !is_numeric($requestdata['modified'])) {
83
+                        $note->setModified(date("Y-m-d H:i:s"));
84
+                    } else {
85
+                        $note->setModified($requestdata['modified']);
86
+                    }
87
+                    $note->saveNote();
88
+                    exit(json_encode($note->toNextcloud()));
89
+                } else {
90
+                    http_response_code(401);
91
+                }
92
+            } catch (NoSuchNoteException $ex) {
93
+                http_response_code(404);
94
+            }
95
+        }
96
+        break;
97
+    case "DELETE":
98
+        if (count($route) == 2 && is_numeric($route[1])) {
99
+            try {
100
+                $note = Note::loadNote($route[1]);
101
+                if ($note->hasWriteAccess($user)) {
102
+                    $note->deleteNote();
103
+                    exit;
104
+                } else {
105
+                    http_response_code(401);
106
+                }
107
+            } catch (NoSuchNoteException $ex) {
108
+                http_response_code(404);
109
+            }
110
+        }
111
+        break;
112
+}

Loading…
Cancel
Save