|
|
|
<?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/.
|
|
|
|
*/
|
|
|
|
|
|
|
|
class Note {
|
|
|
|
|
|
|
|
private $noteid;
|
|
|
|
private $ownerid;
|
|
|
|
private $content = "";
|
|
|
|
private $title = "";
|
|
|
|
private $modified = "";
|
|
|
|
private $color = "FFFFFF";
|
|
|
|
private $favorite = false;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a new Note object.
|
|
|
|
* @param string $content Note content in Markdown
|
|
|
|
* @param string $color Hex color "RRGGBB"
|
|
|
|
* @param int $ownerid The owner's user ID
|
|
|
|
*/
|
|
|
|
public function __construct(string $content, string $color = "FFFFFF", int $ownerid = null, int $noteid = null) {
|
|
|
|
$this->content = $content;
|
|
|
|
$this->color = $color;
|
|
|
|
$this->ownerid = $ownerid;
|
|
|
|
$this->noteid = $noteid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Load a note from the database and return it.
|
|
|
|
* @global type $database
|
|
|
|
* @param int $noteid
|
|
|
|
* @return \Note
|
|
|
|
* @throws NoSuchNoteException When the note ID isn't found.
|
|
|
|
*/
|
|
|
|
public static function loadNote(int $noteid): Note {
|
|
|
|
global $database;
|
|
|
|
|
|
|
|
if (!$database->has('notes', ['noteid' => $noteid])) {
|
|
|
|
throw new NoSuchNoteException();
|
|
|
|
}
|
|
|
|
|
|
|
|
$notedata = $database->get('notes', ['noteid', 'ownerid', 'color', 'content', 'title', 'modified', 'favorite'], ['noteid' => $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']);
|
|
|
|
$note->setFavorite($notedata['favorite'] == true);
|
|
|
|
|
|
|
|
return $note;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the note to the database.
|
|
|
|
* @global type $database
|
|
|
|
* @param bool $saveas If true, save the note under a new ID. Forced to
|
|
|
|
* true if the current note ID is missing or invalid.
|
|
|
|
* @return int The database ID of the saved note
|
|
|
|
* @throws Exception If there is no note owner set.
|
|
|
|
*/
|
|
|
|
public function saveNote(bool $saveas = false): int {
|
|
|
|
global $database;
|
|
|
|
|
|
|
|
$data = [
|
|
|
|
'ownerid' => $this->getOwnerID(),
|
|
|
|
'color' => $this->getColor(),
|
|
|
|
'content' => $this->getText(),
|
|
|
|
'title' => $this->getTitle(),
|
|
|
|
'modified' => $this->getModified(),
|
|
|
|
'favorite' => $this->getFavorite() ? 1 : 0
|
|
|
|
];
|
|
|
|
|
|
|
|
// We can't UPDATE the database, so use save as for INSERT
|
|
|
|
if (empty($this->noteid) || !$database->has('notes', ['noteid' => $this->noteid])) {
|
|
|
|
$saveas = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($this->ownerid)) {
|
|
|
|
throw new Exception("No owner set.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($saveas) {
|
|
|
|
$database->insert('notes', $data);
|
|
|
|
$this->noteid = $database->id();
|
|
|
|
} else {
|
|
|
|
$database->update('notes', $data, ['noteid' => $this->noteid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->noteid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete this note from the database.
|
|
|
|
* @global type $database
|
|
|
|
*/
|
|
|
|
public function deleteNote() {
|
|
|
|
global $database;
|
|
|
|
|
|
|
|
$database->delete('notes', ['noteid' => $this->noteid]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the Markdown content of the note.
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getText(): string {
|
|
|
|
return $this->content;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the HTML render of the note.
|
|
|
|
* @param bool $fragment Get just the HTML content, instead of a whole HTML5 file
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getHTML(bool $fragment = true): string {
|
|
|
|
$parsedown = new ParsedownCheckbox();
|
|
|
|
$html = $parsedown->text($this->content);
|
|
|
|
$safehtml = Htmlawed::filter($html, ['safe' => 1]);
|
|
|
|
if ($fragment) {
|
|
|
|
return $safehtml;
|
|
|
|
}
|
|
|
|
$document = "<!DOCTYPE html>\n"
|
|
|
|
. "<meta charset=\"UTF-8\">\n"
|
|
|
|
. "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"
|
|
|
|
. "<meta name=\"author\" content=\"" . htmlentities($this->getOwner()->getName()) . "\">\n"
|
|
|
|
. "<meta name=\"generator\" content=\"" . SITE_TITLE . "\">\n"
|
|
|
|
. "<link rel=\"schema.dcterms\" href=\"http://purl.org/dc/terms/\">\n"
|
|
|
|
. "<meta name=\"dcterms.modified\" content=\"" . date("Y-m-d", strtotime($this->getModified())) . "\">\n"
|
|
|
|
. "\n"
|
|
|
|
. "<title>" . $this->getCleanTitle() . "</title>\n"
|
|
|
|
. "\n"
|
|
|
|
. $safehtml;
|
|
|
|
return $document;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the note ID.
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getID(): int {
|
|
|
|
return $this->noteid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the note color as RRGGBB hex.
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getColor(): string {
|
|
|
|
$this->setColor($this->color);
|
|
|
|
return $this->color;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the owner UID
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getOwnerID(): int {
|
|
|
|
return $this->ownerid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the owner as a User object
|
|
|
|
* @return \User
|
|
|
|
*/
|
|
|
|
public function getOwner(): User {
|
|
|
|
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 note title stripped of Markdown and trimmed
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getCleanTitle(): string {
|
|
|
|
$title = $this->getTitle();
|
|
|
|
$title = str_replace("*", "", $title);
|
|
|
|
$title = str_replace("#", "", $title);
|
|
|
|
$title = str_replace("_", "", $title);
|
|
|
|
$title = str_replace("`", "", $title);
|
|
|
|
return trim($title);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the last modified date/time as "Y-m-d H:i:s"
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getModified(): string {
|
|
|
|
if (empty($this->modified) || strtotime($this->modified) == 0) {
|
|
|
|
return date("Y-m-d H:i:s");
|
|
|
|
}
|
|
|
|
return date("Y-m-d H:i:s", strtotime($this->modified));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get if the note is favorited (starred).
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
public function getFavorite(): bool {
|
|
|
|
return $this->favorite;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the text color for this note, based on the background color.
|
|
|
|
* Thanks to https://stackoverflow.com/a/8468448
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function getTextColor(): string {
|
|
|
|
$bg = $this->getColor();
|
|
|
|
$r = hexdec(substr($bg, 0, 2));
|
|
|
|
$g = hexdec(substr($bg, 2, 2));
|
|
|
|
$b = hexdec(substr($bg, 4, 2));
|
|
|
|
|
|
|
|
$contrast = sqrt(
|
|
|
|
$r * $r * .241 +
|
|
|
|
$g * $g * .691 +
|
|
|
|
$b * $b * .068
|
|
|
|
);
|
|
|
|
|
|
|
|
if ($contrast > 130) {
|
|
|
|
return '000000';
|
|
|
|
} else {
|
|
|
|
return 'FFFFFF';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the note content
|
|
|
|
* @param string $markdown
|
|
|
|
*/
|
|
|
|
public function setText(string $markdown) {
|
|
|
|
$this->content = $markdown;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the note color to a hex string
|
|
|
|
* @param string $color "RRGGBB"
|
|
|
|
*/
|
|
|
|
public function setColor(string $color) {
|
|
|
|
$color = strtoupper($color);
|
|
|
|
$color = str_replace("#", "", $color);
|
|
|
|
// Make sure we have a valid RRGGBB hex
|
|
|
|
if (!preg_match("/[A-F0-9]{6}/", $color)) {
|
|
|
|
$color = "FFFFFF";
|
|
|
|
}
|
|
|
|
$this->color = $color;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the owner by UID
|
|
|
|
* @param int $uid
|
|
|
|
*/
|
|
|
|
public function setOwnerID(int $uid) {
|
|
|
|
$this->ownerid = $uid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the owner
|
|
|
|
* @param User $user
|
|
|
|
*/
|
|
|
|
public function setOwner(User $owner) {
|
|
|
|
$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) {
|
|
|
|
if (is_numeric($datetime)) {
|
|
|
|
$this->modified = date("Y-m-d H:i:s", $datetime);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
$this->modified = date("Y-m-d H:i:s", strtotime($datetime));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the note as favorite or not
|
|
|
|
* @param bool $favorite
|
|
|
|
*/
|
|
|
|
public function setFavorite(bool $favorite) {
|
|
|
|
$this->favorite = $favorite;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert normal lines of text to checklist items.
|
|
|
|
*/
|
|
|
|
public function toChecklist() {
|
|
|
|
$text = explode("\n", $this->getText());
|
|
|
|
for ($i = 0; $i < count($text); $i++) {
|
|
|
|
if (preg_match("/^[^\s\=\#\-<](.+)/", $text[$i])) {
|
|
|
|
if (count($text) > $i && preg_match("/^[\=-](.+)/", $text[$i + 1])) {
|
|
|
|
// Don't do it if the next line makes this one a heading
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$text[$i] = "- [ ] " . $text[$i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->setText(implode("\n", $text));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Toggle the checked status of a checklist item.
|
|
|
|
* @param string $item The text of the item to toggle.
|
|
|
|
*/
|
|
|
|
public function toggleChecklistItem(string $item) {
|
|
|
|
$text = explode("\n", $this->getText());
|
|
|
|
$item = trim(str_replace("\n", "", $item));
|
|
|
|
for ($i = 0; $i < count($text); $i++) {
|
|
|
|
if (!preg_match("/^- \[[Xx ]\] .*/", $text[$i])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$linecleaned = trim(preg_replace("/^- \[[Xx ]\] /", "", $text[$i]));
|
|
|
|
if ($item != $linecleaned) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (preg_match("/^- \[[Xx]\] .*/", $text[$i])) {
|
|
|
|
$text[$i] = preg_replace("/^- \[[Xx]\] /", "- [ ] ", $text[$i]);
|
|
|
|
} else if (preg_match("/^- \[ \] .*/", $text[$i])) {
|
|
|
|
$text[$i] = preg_replace("/^- \[ \] /", "- [x] ", $text[$i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$this->setText(implode("\n", $text));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get this note as an array.
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function toArray(): array {
|
|
|
|
$owner = new User($this->ownerid);
|
|
|
|
$arr = [
|
|
|
|
'noteid' => $this->getID(),
|
|
|
|
'color' => $this->getColor(),
|
|
|
|
'content' => $this->getText(),
|
|
|
|
'html' => $this->getHTML(true),
|
|
|
|
'title' => $this->getTitle(),
|
|
|
|
'modified' => $this->getModified(),
|
|
|
|
'favorite' => $this->getFavorite(),
|
|
|
|
'owner' => [
|
|
|
|
'uid' => $owner->getUID(),
|
|
|
|
'username' => $owner->getUsername(),
|
|
|
|
'name' => $owner->getName()
|
|
|
|
]
|
|
|
|
];
|
|
|
|
|
|
|
|
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" => $this->getFavorite()
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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 {
|
|
|
|
$note = new Note($arr['content'], $arr['color'], $arr['owner']['uid'], $arr['noteid']);
|
|
|
|
$note->setTitle($arr['title']);
|
|
|
|
$note->setModified($arr['modified']);
|
|
|
|
$note->setFavorite($arr['favorite']);
|
|
|
|
return $note;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|