2020-07-13 08:27:52 +02:00
|
|
|
<?php
|
2013-04-03 17:50:29 +02:00
|
|
|
|
2020-07-13 08:27:52 +02:00
|
|
|
declare(strict_types=1);
|
2013-04-03 17:50:29 +02:00
|
|
|
|
2020-07-13 08:27:52 +02:00
|
|
|
namespace OCA\Notes\Service;
|
2020-04-12 19:26:57 +02:00
|
|
|
|
2019-09-20 22:07:56 +02:00
|
|
|
use OCP\Files\File;
|
2020-04-12 19:26:57 +02:00
|
|
|
use OCP\Files\FileInfo;
|
2019-09-20 22:07:56 +02:00
|
|
|
use OCP\Files\Folder;
|
2019-11-16 23:35:37 +01:00
|
|
|
|
2013-08-12 12:55:10 +02:00
|
|
|
class NotesService {
|
2020-05-30 14:42:25 +02:00
|
|
|
private $metaService;
|
2019-05-25 08:15:05 +02:00
|
|
|
private $settings;
|
2019-09-20 22:07:56 +02:00
|
|
|
private $noteUtil;
|
2015-04-27 11:08:32 +02:00
|
|
|
|
2019-09-20 22:07:56 +02:00
|
|
|
public function __construct(
|
2020-05-30 14:42:25 +02:00
|
|
|
MetaService $metaService,
|
2019-09-20 22:07:56 +02:00
|
|
|
SettingsService $settings,
|
2020-04-12 19:26:57 +02:00
|
|
|
NoteUtil $noteUtil
|
2019-09-20 22:07:56 +02:00
|
|
|
) {
|
2020-05-30 14:42:25 +02:00
|
|
|
$this->metaService = $metaService;
|
2019-05-25 08:15:05 +02:00
|
|
|
$this->settings = $settings;
|
2019-09-20 22:07:56 +02:00
|
|
|
$this->noteUtil = $noteUtil;
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
2020-05-18 19:59:16 +02:00
|
|
|
public function getAll(string $userId) : array {
|
2020-04-12 19:26:57 +02:00
|
|
|
$notesFolder = $this->getNotesFolder($userId);
|
2020-05-18 19:59:16 +02:00
|
|
|
$data = $this->gatherNoteFiles($notesFolder);
|
2020-04-12 19:26:57 +02:00
|
|
|
$fileIds = array_map(function (File $file) : int {
|
|
|
|
return $file->getId();
|
2020-05-18 19:59:16 +02:00
|
|
|
}, $data['files']);
|
2020-04-12 19:26:57 +02:00
|
|
|
// pre-load tags for all notes (performance improvement)
|
|
|
|
$this->noteUtil->getTagService()->loadTags($fileIds);
|
|
|
|
$notes = array_map(function (File $file) use ($notesFolder) : Note {
|
|
|
|
return new Note($file, $notesFolder, $this->noteUtil);
|
2020-05-18 19:59:16 +02:00
|
|
|
}, $data['files']);
|
|
|
|
return [ 'notes' => $notes, 'categories' => $data['categories'] ];
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
2020-09-20 14:45:49 +02:00
|
|
|
public function getTopNotes(string $userId, int $count) : array {
|
|
|
|
$notes = $this->getAll($userId)['notes'];
|
|
|
|
usort($notes, function (Note $a, Note $b) {
|
|
|
|
$favA = $a->getFavorite();
|
|
|
|
$favB = $b->getFavorite();
|
|
|
|
if ($favA === $favB) {
|
|
|
|
return $b->getModified() - $a->getModified();
|
|
|
|
} else {
|
|
|
|
return $favA > $favB ? -1 : 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return array_slice($notes, 0, $count);
|
|
|
|
}
|
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
public function get(string $userId, int $id) : Note {
|
|
|
|
$notesFolder = $this->getNotesFolder($userId);
|
2020-05-30 14:42:25 +02:00
|
|
|
$note = new Note($this->getFileById($notesFolder, $id), $notesFolder, $this->noteUtil);
|
|
|
|
$this->metaService->update($userId, $note);
|
|
|
|
return $note;
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
2020-09-15 21:45:28 +02:00
|
|
|
public function search(string $userId, string $search) : array {
|
|
|
|
$terms = preg_split('/\s+/', $search);
|
|
|
|
$notes = $this->getAll($userId)['notes'];
|
|
|
|
return array_values(array_filter(
|
|
|
|
$notes,
|
|
|
|
function (Note $note) use ($terms) : bool {
|
|
|
|
return $this->searchTermsInNote($note, $terms);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
}
|
|
|
|
private function searchTermsInNote(Note $note, array $terms) : bool {
|
|
|
|
try {
|
|
|
|
$d = $note->getData();
|
|
|
|
$strings = [ $d['title'], $d['category'], $d['content'] ];
|
|
|
|
foreach ($terms as $term) {
|
|
|
|
if (!$this->searchTermInData($strings, $term)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private function searchTermInData(array $strings, string $term) : bool {
|
|
|
|
foreach ($strings as $str) {
|
|
|
|
if (stripos($str, $term) !== false) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-05-25 08:15:05 +02:00
|
|
|
|
|
|
|
/**
|
2020-04-12 19:26:57 +02:00
|
|
|
* @throws \OCP\Files\NotPermittedException
|
2019-05-25 08:15:05 +02:00
|
|
|
*/
|
2020-04-12 19:26:57 +02:00
|
|
|
public function create(string $userId, string $title, string $category) : Note {
|
|
|
|
// get folder based on category
|
|
|
|
$notesFolder = $this->getNotesFolder($userId);
|
|
|
|
$folder = $this->noteUtil->getCategoryFolder($notesFolder, $category);
|
2020-02-06 07:02:14 +01:00
|
|
|
$this->noteUtil->ensureSufficientStorage($folder, 1);
|
2019-05-25 08:15:05 +02:00
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
// get file name
|
|
|
|
$fileSuffix = $this->settings->get($userId, 'fileSuffix');
|
|
|
|
$filename = $this->noteUtil->generateFileName($folder, $title, $fileSuffix, -1);
|
|
|
|
|
|
|
|
// create file
|
|
|
|
$file = $folder->newFile($filename);
|
2019-05-25 08:15:05 +02:00
|
|
|
|
2020-02-01 12:08:39 +01:00
|
|
|
// try to write some content
|
|
|
|
try {
|
|
|
|
// If server-side encryption is activated, the server creates an empty file without signature
|
|
|
|
// which leads to an GenericEncryptionException('Missing Signature') afterwards.
|
|
|
|
// Saving a space-char (and removing it later) is a working work-around.
|
|
|
|
$file->putContent(' ');
|
|
|
|
} catch (\Throwable $e) {
|
|
|
|
// if writing the content fails, we have to roll back the note creation
|
2020-04-12 19:26:57 +02:00
|
|
|
$this->delete($userId, $file->getId());
|
2020-02-01 12:08:39 +01:00
|
|
|
throw $e;
|
|
|
|
}
|
2019-05-25 08:15:05 +02:00
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
return new Note($file, $notesFolder, $this->noteUtil);
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws NoteDoesNotExistException if note does not exist
|
|
|
|
*/
|
2020-04-12 19:26:57 +02:00
|
|
|
public function delete(string $userId, int $id) {
|
|
|
|
$notesFolder = $this->getNotesFolder($userId);
|
2019-05-25 08:15:05 +02:00
|
|
|
$file = $this->getFileById($notesFolder, $id);
|
2020-04-12 19:26:57 +02:00
|
|
|
$parent = $file->getParent();
|
|
|
|
$file->delete();
|
|
|
|
$this->noteUtil->deleteEmptyFolder($parent, $notesFolder);
|
|
|
|
}
|
2019-05-25 08:15:05 +02:00
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
public function getTitleFromContent(string $content) : string {
|
2020-09-20 14:45:49 +02:00
|
|
|
$content = $this->noteUtil->stripMarkdown($content);
|
2020-04-12 19:26:57 +02:00
|
|
|
return $this->noteUtil->getSafeTitle($content);
|
|
|
|
}
|
2019-05-25 08:15:05 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-02-03 21:36:41 +01:00
|
|
|
|
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
/**
|
|
|
|
* @param string $userId the user id
|
|
|
|
* @return Folder
|
|
|
|
*/
|
|
|
|
private function getNotesFolder(string $userId) : Folder {
|
|
|
|
$userPath = $this->noteUtil->getRoot()->getUserFolder($userId)->getPath();
|
|
|
|
$path = $userPath . '/' . $this->settings->get($userId, 'notesPath');
|
2020-06-19 12:06:24 +02:00
|
|
|
$folder = $this->noteUtil->getOrCreateFolder($path);
|
2020-04-12 19:26:57 +02:00
|
|
|
return $folder;
|
2020-02-03 21:36:41 +01:00
|
|
|
}
|
|
|
|
|
2019-05-25 08:15:05 +02:00
|
|
|
/**
|
2020-04-12 19:26:57 +02:00
|
|
|
* gather note files in given directory and all subdirectories
|
2019-05-25 08:15:05 +02:00
|
|
|
*/
|
2020-05-18 19:59:16 +02:00
|
|
|
private static function gatherNoteFiles(Folder $folder, string $categoryPrefix = '') : array {
|
|
|
|
$data = [
|
|
|
|
'files' => [],
|
|
|
|
'categories' => [],
|
|
|
|
];
|
2020-04-12 19:26:57 +02:00
|
|
|
$nodes = $folder->getDirectoryListing();
|
|
|
|
foreach ($nodes as $node) {
|
|
|
|
if ($node->getType() === FileInfo::TYPE_FOLDER && $node instanceof Folder) {
|
2020-05-18 19:59:16 +02:00
|
|
|
$subCategory = $categoryPrefix . $node->getName();
|
|
|
|
$data['categories'][] = $subCategory;
|
|
|
|
$data_sub = self::gatherNoteFiles($node, $subCategory . '/');
|
|
|
|
$data['files'] = array_merge($data['files'], $data_sub['files']);
|
|
|
|
$data['categories'] = array_merge($data['categories'], $data_sub['categories']);
|
|
|
|
} elseif (self::isNote($node)) {
|
|
|
|
$data['files'][] = $node;
|
2019-11-17 10:12:02 +01:00
|
|
|
}
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
2020-05-18 19:59:16 +02:00
|
|
|
return $data;
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2020-04-12 19:26:57 +02:00
|
|
|
* test if file is a note
|
2019-05-25 08:15:05 +02:00
|
|
|
*/
|
2020-04-12 19:26:57 +02:00
|
|
|
private static function isNote(FileInfo $file) : bool {
|
|
|
|
static $allowedExtensions = ['txt', 'org', 'markdown', 'md', 'note'];
|
|
|
|
$ext = strtolower(pathinfo($file->getName(), PATHINFO_EXTENSION));
|
|
|
|
return $file->getType() === 'file' && in_array($ext, $allowedExtensions);
|
2019-05-25 08:15:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @throws NoteDoesNotExistException
|
|
|
|
*/
|
2020-04-12 19:26:57 +02:00
|
|
|
private static function getFileById(Folder $folder, int $id) : File {
|
2019-05-25 08:15:05 +02:00
|
|
|
$file = $folder->getById($id);
|
|
|
|
|
2020-04-12 19:26:57 +02:00
|
|
|
if (count($file) <= 0 || !($file[0] instanceof File) || !self::isNote($file[0])) {
|
2019-05-25 08:15:05 +02:00
|
|
|
throw new NoteDoesNotExistException();
|
|
|
|
}
|
|
|
|
return $file[0];
|
|
|
|
}
|
2013-04-03 17:50:29 +02:00
|
|
|
}
|