Skip to content

Commit e9dab9e

Browse files
authored
Merge pull request #1600 from oli-ver/feature/noid/stableAttachmentId
newhinton: Expose image api for other clients as api v1.4 including routes fix
2 parents c0344da + 4183669 commit e9dab9e

File tree

4 files changed

+157
-17
lines changed

4 files changed

+157
-17
lines changed

appinfo/routes.php

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -183,15 +183,7 @@
183183
'url' => '/api/{apiVersion}/settings',
184184
'verb' => 'GET',
185185
'requirements' => [
186-
'apiVersion' => '(v1)',
187-
],
188-
],
189-
[
190-
'name' => 'notes_api#fail',
191-
'url' => '/api/{catchAll}',
192-
'verb' => 'GET',
193-
'requirements' => [
194-
'catchAll' => '.*',
186+
'apiVersion' => '(v1|v1.4)',
195187
],
196188
],
197189
[
@@ -203,4 +195,30 @@
203195
'path' => '.+',
204196
],
205197
],
198+
[
199+
'name' => 'notes_api#getAttachment',
200+
'url' => '/api/{apiVersion}/attachment/{noteid}',
201+
'verb' => 'GET',
202+
'requirements' => [
203+
'apiVersion' => '(v1.4)',
204+
'noteid' => '\d+'
205+
],
206+
],
207+
[
208+
'name' => 'notes_api#uploadFile',
209+
'url' => '/api/{apiVersion}/attachment/{noteid}',
210+
'verb' => 'POST',
211+
'requirements' => [
212+
'apiVersion' => '(v1.4)',
213+
'noteid' => '\d+'
214+
],
215+
],
216+
[
217+
'name' => 'notes_api#fail',
218+
'url' => '/api/{catchAll}',
219+
'verb' => 'GET',
220+
'requirements' => [
221+
'catchAll' => '.*',
222+
],
223+
],
206224
]];

docs/api/v1.md

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ In this document, the Notes API major version 1 and all its minor versions are d
99

1010
## Minor versions
1111

12-
| API version | Introduced with app version | Remarkable Changes |
13-
|:-----------:|:----------------------------|:-------------------|
14-
| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content |
15-
| **1.1** | Notes 3.4 (May 2020) | Filter "Get all notes" by category |
16-
| **1.2** | Notes 4.1 (June 2021) | Preventing lost updates, read-only notes, settings |
17-
| **1.3** | Notes 4.5 (August 2022) | Allow custom file suffixes |
12+
| API version | Introduced with app version | Remarkable Changes |
13+
|:-----------:|:----------------------------|:---------------------------------------------------|
14+
| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content |
15+
| **1.1** | Notes 3.4 (May 2020) | Filter "Get all notes" by category |
16+
| **1.2** | Notes 4.1 (June 2021) | Preventing lost updates, read-only notes, settings |
17+
| **1.3** | Notes 4.5 (August 2022) | Allow custom file suffixes |
18+
| **1.4** | Notes 4.9 (August 2025) | Add external image api |
1819

1920

2021

@@ -136,7 +137,7 @@ Note not found.
136137
<details><summary>Details</summary>
137138

138139
#### Request parameters
139-
- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example:
140+
- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example:
140141
```js
141142
{
142143
"title": "New note",
@@ -278,6 +279,74 @@ No valid authentication credentials supplied.
278279

279280

280281

282+
### Get attachment (`GET /attachment/{id}`)
283+
<details><summary>Details</summary>
284+
285+
*(since API v1.4)*
286+
287+
#### Request parameters
288+
| Parameter | Type | Description |
289+
|:----------|:-----------------------------|:-------------------------------------------|
290+
| `id` | integer, required (path) | ID of the note to load the attachment from |
291+
| `path` | string, required (request) | Path or name of the attachment to load. |
292+
293+
Example:
294+
295+
```bash
296+
curl -u "user:password" "https://yournextcloud.com/index.php/apps/notes/api/v1.4/attachment/<id>?path=<path>" -o <outputfilename>.jpg
297+
```
298+
299+
300+
#### Response
301+
##### 200 OK
302+
- **Body**: Image or File
303+
304+
##### 400 Bad Request
305+
Endpoint not supported by installed notes app version (requires API version 1.4).
306+
307+
##### 401 Unauthorized
308+
No valid authentication credentials supplied.
309+
</details>
310+
311+
312+
### Put attachment (`POST /attachment/{id}`)
313+
<details><summary>Details</summary>
314+
315+
*(since API v1.4)*
316+
317+
#### Request parameters
318+
| Parameter | Type | Description |
319+
|:----------|:------------------------|:------------------------------------------------|
320+
| `id` | integer, required (path)| ID of the note to upload the attachment to |
321+
322+
Example:
323+
324+
```bash
325+
curl -u "user:password" \
326+
-X POST \
327+
-F "file=@/path/to/image.png" \
328+
"https://yournextcloud.com/index.php/apps/notes/api/v1.4/attachment/<id>"
329+
330+
# The post request will return the filename that was generated:
331+
{"filename":"d8aef2005b4f815fec8ade5388240f2c.png"}
332+
```
333+
334+
#### Response
335+
##### 200 OK
336+
- **Body**: Filename in json encoded:
337+
```js
338+
{
339+
"filename": "image.jpg"
340+
}
341+
```
342+
343+
##### 400 Bad Request
344+
Endpoint not supported by installed notes app version (requires API version 1.4).
345+
346+
##### 401 Unauthorized
347+
No valid authentication credentials supplied.
348+
</details>
349+
281350
## Preventing lost updates and conflict solution
282351

283352
While changing a note using a Notes client, the same note may be changed by another client.

lib/AppInfo/Application.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
class Application extends App implements IBootstrap {
2323
public const APP_ID = 'notes';
24-
public static array $API_VERSIONS = [ '0.2', '1.3' ];
24+
public static array $API_VERSIONS = [ '0.2', '1.3', '1.4' ];
2525

2626
public function __construct(array $urlParams = []) {
2727
parent::__construct(self::APP_ID, $urlParams);

lib/Controller/NotesApiController.php

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,16 @@
1818
use OCP\AppFramework\ApiController;
1919
use OCP\AppFramework\Http;
2020
use OCP\AppFramework\Http\JSONResponse;
21+
use OCP\AppFramework\Http\StreamResponse;
22+
use OCP\Files\IMimeTypeDetector;
2123
use OCP\IRequest;
2224

2325
class NotesApiController extends ApiController {
2426
private NotesService $service;
2527
private MetaService $metaService;
2628
private SettingsService $settingsService;
2729
private Helper $helper;
30+
private IMimeTypeDetector $mimeTypeDetector;
2831

2932
public function __construct(
3033
string $AppName,
@@ -33,12 +36,14 @@ public function __construct(
3336
MetaService $metaService,
3437
SettingsService $settingsService,
3538
Helper $helper,
39+
IMimeTypeDetector $mimeTypeDetector,
3640
) {
3741
parent::__construct($AppName, $request);
3842
$this->service = $service;
3943
$this->metaService = $metaService;
4044
$this->settingsService = $settingsService;
4145
$this->helper = $helper;
46+
$this->mimeTypeDetector = $mimeTypeDetector;
4247
}
4348

4449

@@ -259,4 +264,52 @@ public function fail() : JSONResponse {
259264
return new JSONResponse([], Http::STATUS_BAD_REQUEST);
260265
});
261266
}
267+
268+
269+
270+
/**
271+
* With help from: https://github.com/nextcloud/cookbook
272+
* @NoAdminRequired
273+
* @CORS
274+
* @NoCSRFRequired
275+
* @return JSONResponse|StreamResponse
276+
*/
277+
public function getAttachment(int $noteid, string $path): Http\Response {
278+
try {
279+
$targetimage = $this->service->getAttachment(
280+
$this->helper->getUID(),
281+
$noteid,
282+
$path
283+
);
284+
$fileHandle = $targetimage->fopen('rb');
285+
if ($fileHandle === false) {
286+
throw new \Exception('Could not open file');
287+
}
288+
$response = new StreamResponse($fileHandle);
289+
$response->addHeader('Content-Disposition', 'attachment; filename="' . rawurldecode($targetimage->getName()) . '"');
290+
$response->addHeader('Content-Type', $this->mimeTypeDetector->getSecureMimeType($targetimage->getMimeType()));
291+
$response->addHeader('Cache-Control', 'public, max-age=604800');
292+
return $response;
293+
} catch (\Exception $e) {
294+
$this->helper->logException($e);
295+
return $this->helper->createErrorResponse($e, Http::STATUS_NOT_FOUND);
296+
}
297+
}
298+
299+
/**
300+
* @NoAdminRequired
301+
* @CORS
302+
* @NoCSRFRequired
303+
*/
304+
public function uploadFile(int $noteid): JSONResponse {
305+
$file = $this->request->getUploadedFile('file');
306+
return $this->helper->handleErrorResponse(function () use ($noteid, $file): array {
307+
return $this->service->createImage(
308+
$this->helper->getUID(),
309+
$noteid,
310+
$file
311+
);
312+
});
313+
}
314+
262315
}

0 commit comments

Comments
 (0)