Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@
'url' => '/api/{apiVersion}/attachment/{noteid}',
'verb' => 'GET',
'requirements' => [
'apiVersion' => '(v1.4)',
'apiVersion' => '(v1|v1.4)',
'noteid' => '\d+'
],
],
Expand All @@ -209,7 +209,7 @@
'url' => '/api/{apiVersion}/attachment/{noteid}',
'verb' => 'POST',
'requirements' => [
'apiVersion' => '(v1.4)',
'apiVersion' => '(v1|v1.4)',
'noteid' => '\d+'
],
],
Expand Down
12 changes: 6 additions & 6 deletions docs/api/v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ In this document, the Notes API major version 1 and all its minor versions are d
| **1.1** | Notes 3.4 (May 2020) | Filter "Get all notes" by category |
| **1.2** | Notes 4.1 (June 2021) | Preventing lost updates, read-only notes, settings |
| **1.3** | Notes 4.5 (August 2022) | Allow custom file suffixes |
| **1.4** | Notes 4.9 (August 2025) | Add external image api |
| **1.4** | Notes 4.9 (August 2025) | Add external image API |



Expand Down Expand Up @@ -288,7 +288,7 @@ No valid authentication credentials supplied.
| Parameter | Type | Description |
|:----------|:-----------------------------|:-------------------------------------------|
| `id` | integer, required (path) | ID of the note to load the attachment from |
| `path` | string, required (request) | Path or name of the attachment to load. |
| `path` | string, required (request) | Path of the attachment to load |

Example:

Expand Down Expand Up @@ -327,16 +327,16 @@ curl -u "user:password" \
-F "file=@/path/to/image.png" \
"https://yournextcloud.com/index.php/apps/notes/api/v1.4/attachment/<id>"

# The post request will return the filename that was generated:
{"filename":"d8aef2005b4f815fec8ade5388240f2c.png"}
# The post request will return the path where the image is stored:
{"filename":".attachments.<id>/image.png"}
```

#### Response
##### 200 OK
- **Body**: Filename in json encoded:
- **Body**: Path in JSON encoded, example:
```js
{
"filename": "image.jpg"
"filaname": ".attachments.1234/image.png"
}
```

Expand Down
90 changes: 74 additions & 16 deletions lib/Service/NotesService.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,26 @@
use OCP\Files\File;
use OCP\Files\FileInfo;
use OCP\Files\Folder;
use OCP\Files\IFilenameValidator;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;

class NotesService {
private MetaService $metaService;
private SettingsService $settings;
private NoteUtil $noteUtil;
private IFilenameValidator $filenameValidator;

Check failure on line 24 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedClass

lib/Service/NotesService.php:24:10: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)

Check failure on line 24 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable29

UndefinedClass

lib/Service/NotesService.php:24:10: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)

public function __construct(
MetaService $metaService,
SettingsService $settings,
NoteUtil $noteUtil,
IFilenameValidator $filenameValidator,

Check failure on line 30 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedClass

lib/Service/NotesService.php:30:3: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)

Check failure on line 30 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable29

UndefinedClass

lib/Service/NotesService.php:30:3: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)
) {
$this->metaService = $metaService;
$this->settings = $settings;
$this->noteUtil = $noteUtil;
$this->filenameValidator = $filenameValidator;
}

public function getAll(string $userId, bool $autoCreateNotesFolder = false) : array {
Expand Down Expand Up @@ -223,8 +228,8 @@
* @NoCSRFRequired
* @return \OCP\Files\File
*/
public function getAttachment(string $userId, int $noteid, string $path) : File {
$note = $this->get($userId, $noteid);
public function getAttachment(string $userId, int $noteId, string $path) : File {
$note = $this->get($userId, $noteId);
$notesFolder = $this->getNotesFolder($userId);
$path = str_replace('\\', '/', $path); // change windows style path
$p = explode('/', $note->getCategory());
Expand All @@ -243,24 +248,24 @@

/**
* @param $userId
* @param $noteid
* @param $noteId
* @param $fileDataArray
*
* @return array
* @throws NotPermittedException
* @throws ImageNotWritableException
* https://github.com/nextcloud/deck/blob/master/lib/Service/AttachmentService.php
* @throws NotFoundException
* @throws InvalidPathException

Check failure on line 258 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedDocblockClass

lib/Service/NotesService.php:258:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 258 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable30

UndefinedDocblockClass

lib/Service/NotesService.php:258:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 258 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable32

UndefinedDocblockClass

lib/Service/NotesService.php:258:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 258 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable29

UndefinedDocblockClass

lib/Service/NotesService.php:258:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 258 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable31

UndefinedDocblockClass

lib/Service/NotesService.php:258:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)
* https://github.com/nextcloud/text/blob/main/lib/Service/AttachmentService.php
*/
public function createImage(string $userId, int $noteid, $fileDataArray) {
$note = $this->get($userId, $noteid);
public function createImage(string $userId, int $noteId, $fileDataArray) : array {
$note = $this->get($userId, $noteId);
$notesFolder = $this->getNotesFolder($userId);
$parent = $this->noteUtil->getCategoryFolder($notesFolder, $note->getCategory());
$parentFolder = $this->noteUtil->getCategoryFolder($notesFolder, $note->getCategory());

// try to generate long id, if not available on system fall back to a shorter one
try {
$filename = bin2hex(random_bytes(16));
} catch (\Exception $e) {
$filename = uniqid();
}
$filename = $filename . '.' . explode('.', $fileDataArray['name'])[1];
$saveDir = $this->getAttachmentDirectoryForNote($note, $userId);
$fileName = self::getUniqueFileName($saveDir, $fileDataArray['name']);
$this->filenameValidator->validateFilename($fileName);

Check failure on line 268 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedClass

lib/Service/NotesService.php:268:3: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)

Check failure on line 268 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable29

UndefinedClass

lib/Service/NotesService.php:268:3: UndefinedClass: Class, interface or enum named OCP\Files\IFilenameValidator does not exist (see https://psalm.dev/019)

if ($fileDataArray['tmp_name'] === '') {
throw new ImageNotWritableException();
Expand All @@ -272,8 +277,61 @@
fclose($fp);

$result = [];
$result['filename'] = $filename;
$this->noteUtil->getRoot()->newFile($parent->getPath() . '/' . $filename, $content);
$result['filename'] = '.attachments.' . $note->getId() . '/' . $fileName;
$saveDir->newFile($fileName, $content);
return $result;
}

/**
* Get unique file name in a directory. Add '(n)' suffix.
*
* @param Folder $dir
* @param string $fileName
*
* @return string
*/
public static function getUniqueFileName(Folder $dir, string $fileName) : string {
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
$counter = 1;
$uniqueFileName = $fileName;
if ($extension !== '') {
while ($dir->nodeExists($uniqueFileName)) {
$counter++;
$uniqueFileName = (string)preg_replace('/\.' . $extension . '$/', ' (' . $counter . ').' . $extension, $fileName);
}
} else {
while ($dir->nodeExists($uniqueFileName)) {
$counter++;
$uniqueFileName = (string)preg_replace('/$/', ' (' . $counter . ')', $fileName);
}
}
return $uniqueFileName;
}

/**
* Get or create file--specific attachment folder
*
* @param Note $note
* @param string $userid
*
* @return Folder
* @throws NotFoundException
* @throws NotPermittedException
* @throws InvalidPathException

Check failure on line 320 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable28

UndefinedDocblockClass

lib/Service/NotesService.php:320:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 320 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable30

UndefinedDocblockClass

lib/Service/NotesService.php:320:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 320 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable32

UndefinedDocblockClass

lib/Service/NotesService.php:320:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 320 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable29

UndefinedDocblockClass

lib/Service/NotesService.php:320:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)

Check failure on line 320 in lib/Service/NotesService.php

View workflow job for this annotation

GitHub Actions / static-psalm-analysis dev-stable31

UndefinedDocblockClass

lib/Service/NotesService.php:320:13: UndefinedDocblockClass: Docblock-defined class, interface or enum named OCA\Notes\Service\InvalidPathException does not exist (see https://psalm.dev/200)
*/
private function getAttachmentDirectoryForNote(Note $note, string $userId) : Folder {
$notesFolder = $this->getNotesFolder($userId);
$parentFolder = $this->noteUtil->getCategoryFolder($notesFolder, $note->getCategory());

$attachmentFolderName = '.attachments.' . $note->getId();
if ($parentFolder->nodeExists($attachmentFolderName)) {
$attachmentFolder = $parentFolder->get($attachmentFolderName);
if ($attachmentFolder instanceof Folder) {
return $attachmentFolder;
}
} else {
return $parentFolder->newFolder($attachmentFolderName);
}
throw new NotFoundException('Attachment dir for note ' . $note->getId() . ' was not found or could not be created.');
}
}
Loading