Browse Source

Implement Nextcloud Notes API, close #2

master
Skylar Ittner 10 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 @@
# Rewrite for Nextcloud Notes API
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteRule index.php/apps/notes/api/v0.2 lib/nextcloudnotes.php [PT]
</IfModule>

+ 5
- 5
action.php View File

@@ -35,13 +35,13 @@ switch ($VARS['action']) {
header('Location: index.php');
die("Logged out.");
case "savenote":
if (empty($VARS['content']) || empty($VARS['noteid'])) {
die($Strings->get("invalid parameters"));
if (!isset($VARS['content']) || empty($VARS['noteid'])) {
die($Strings->get("invalid parameters", false));
}
http_response_code(204);
$note = Note::loadNote($VARS['noteid']);
if ($note->getOwnerID() != $_SESSION['uid']) {
die($Strings->get("invalid parameters"));
if (!$note->hasWriteAccess(new User($_SESSION['uid']))) {
die($Strings->get("invalid parameters", false));
}
$note->setText($VARS['content']);
$note->setColor($VARS['color']);
@@ -52,7 +52,7 @@ switch ($VARS['action']) {
die($Strings->get("invalid parameters"));
}
$note = Note::loadNote($VARS['noteid']);
if ($note->getOwnerID() != $_SESSION['uid']) {
if (!$note->hasWriteAccess(new User($_SESSION['uid']))) {
die($Strings->get("invalid parameters"));
}
$note->deleteNote();

BIN
database.mwb View File


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

@@ -11,6 +11,8 @@ class Note {
private $noteid;
private $ownerid;
private $content = "";
private $title = "";
private $modified = "";
private $color = "FFFFFF";

/**
@@ -40,9 +42,13 @@ class Note {
throw new NoSuchNoteException();
}

$notedata = $database->get('notes', ['noteid', 'ownerid', 'color', 'content'], ['noteid' => $noteid]);
$notedata = $database->get('notes', ['noteid', 'ownerid', 'color', 'content', 'title', 'modified'], ['noteid' => $noteid]);

return new Note($notedata['content'], $notedata['color'], $notedata['ownerid'], $notedata['noteid']);
$note = new Note($notedata['content'], $notedata['color'], $notedata['ownerid'], $notedata['noteid']);
$note->setTitle(is_null($notedata['title']) ? "" : $notedata['title']);
$note->setModified(is_null($notedata['modified']) ? date("Y-m-d H:i:s") : $notedata['modified']);

return $note;
}

/**
@@ -57,9 +63,11 @@ class Note {
global $database;

$data = [
'ownerid' => $this->ownerid,
'color' => $this->color,
'content' => $this->content
'ownerid' => $this->getOwnerID(),
'color' => $this->getColor(),
'content' => $this->getText(),
'title' => $this->getTitle(),
'modified' => $this->getModified()
];

// We can't UPDATE the database, so use save as for INSERT
@@ -142,6 +150,34 @@ class Note {
return new User($this->ownerid);
}

/**
* Get the note title
* @return string
*/
public function getTitle(): string {
global $Strings;
$title = $this->title;
if (empty($title)) {
if (empty($this->getText())) {
$title = $Strings->get("New note", false);
} else {
$title = explode("\n", $this->getText())[0];
}
}
return $title;
}

/**
* Get the last modified date/time as "Y-m-d H:i:s"
* @return string
*/
public function getModified(): string {
if (empty($this->modified)) {
return date("Y-m-d H:i:s");
}
return date("Y-m-d H:i:s", strtotime($this->modified));
}

/**
* Set the note content
* @param string $markdown
@@ -174,6 +210,22 @@ class Note {
$this->ownerid = $owner->getUID();
}

/**
* Set the note title
* @param string $title
*/
public function setTitle(string $title) {
$this->title = $title;
}

/**
* Set the last modified date/time
* @param string $datetime
*/
public function setModified(string $datetime) {
$this->modified = date("Y-m-d H:i:s", strtotime($datetime));
}

/**
* Get this note as an array.
* @return string
@@ -181,9 +233,11 @@ class Note {
public function toArray(): array {
$owner = new User($this->ownerid);
$arr = [
'noteid' => $this->noteid,
'color' => $this->color,
'content' => $this->content,
'noteid' => $this->getID(),
'color' => $this->getColor(),
'content' => $this->getText(),
'title' => $this->getTitle(),
'modified' => $this->getModified(),
'owner' => [
'uid' => $owner->getUID(),
'username' => $owner->getUsername(),
@@ -194,13 +248,54 @@ class Note {
return $arr;
}

/**
* Get an array suitable for returning via the Nextcloud Notes API
* https://github.com/nextcloud/notes/wiki/Notes-0.2#get-a-note
* @return array
*/
public function toNextcloud(): array {
return [
"id" => $this->getID(),
"modified" => strtotime($this->getModified()),
"title" => $this->getTitle(),
"category" => null,
"content" => $this->getText(),
"favorite" => false
];
}

/**
* Check whether the given User has read access to this note.
* @param User $user
*/
public function hasReadAccess(User $user): bool {
if ($this->getOwnerID() == $user->getUID()) {
return true;
}
return false;
}

/**
* Check whether the given User has write access to this note.
* @param User $user
*/
public function hasWriteAccess(User $user): bool {
if ($this->getOwnerID() == $user->getUID()) {
return true;
}
return false;
}

/**
* Read an array with the structure from toArray().
* @param array $arr
* @return \Note A Note constructed from the array data.
*/
public static function fromArray(array $arr): Note {
return new Note($arr['content'], $arr['color'], $arr['owner']['uid'], $arr['noteid']);
$note = new Note($arr['content'], $arr['color'], $arr['owner']['uid'], $arr['noteid']);
$note->setTitle($arr['title']);
$note->setModified($arr['modified']);
return $note;
}

}

+ 112
- 0
lib/nextcloudnotes.php View File

@@ -0,0 +1,112 @@
<?php

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

require __DIR__ . "/../required.php";

header("Content-Security-Policy: default-src '*';");
header('Access-Control-Allow-Origin: *');

// Get the API route/action
$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/v0.2/") + 9));

// HTTP authentication
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="' . SITE_TITLE . '"');
header('HTTP/1.0 401 Unauthorized');
exit;
} else {
$user = User::byUsername($_SERVER['PHP_AUTH_USER']);
if (!$user->checkPassword($_SERVER['PHP_AUTH_PW'])) {
header('HTTP/1.0 401 Unauthorized');
die($Strings->get("login incorrect"));
}
}

header("Content-Type: application/json");

$requestdata = $_GET;
$requestbody = file_get_contents('php://input');
$requestjson = json_decode($requestbody, TRUE);
if (json_last_error() == JSON_ERROR_NONE) {
$requestdata = array_merge($requestdata, $requestjson);
}

switch ($_SERVER['REQUEST_METHOD']) {
case "GET":
if (count($route) == 1) {
$noteids = $database->select('notes', 'noteid', ['ownerid' => $user->getUID()]);
$notes = [];
foreach ($noteids as $n) {
$notes[] = Note::loadNote($n)->toNextcloud();
}
exit(json_encode($notes));
} else if (count($route) == 2 && is_numeric($route[1])) {
try {
$note = Note::loadNote($route[1]);
if ($note->getOwner()->getUID() == $user->getUID()) {
exit(json_encode($note->toNextcloud()));
} else {
http_response_code(401);
}
} catch (NoSuchNoteException $ex) {
http_response_code(404);
}
}
break;
case "POST":
$note = new Note($requestdata['content']);

if (empty($requestdata['modified']) || !is_numeric($requestdata['modified'])) {
$note->setModified(date("Y-m-d H:i:s"));
} else {
$note->setModified($requestdata['modified']);
}

$note->setOwner($user);

$note->saveNote();

exit(json_encode($note->toNextcloud()));
break;
case "PUT":
if (count($route) == 2 && is_numeric($route[1])) {
try {
$note = Note::loadNote($route[1]);
if ($note->hasWriteAccess($user)) {
$note->setText($requestdata['content']);
if (empty($requestdata['modified']) || !is_numeric($requestdata['modified'])) {
$note->setModified(date("Y-m-d H:i:s"));
} else {
$note->setModified($requestdata['modified']);
}
$note->saveNote();
exit(json_encode($note->toNextcloud()));
} else {
http_response_code(401);
}
} catch (NoSuchNoteException $ex) {
http_response_code(404);
}
}
break;
case "DELETE":
if (count($route) == 2 && is_numeric($route[1])) {
try {
$note = Note::loadNote($route[1]);
if ($note->hasWriteAccess($user)) {
$note->deleteNote();
exit;
} else {
http_response_code(401);
}
} catch (NoSuchNoteException $ex) {
http_response_code(404);
}
}
break;
}

Loading…
Cancel
Save