From 17eda4e798f402441cb89aaf37782e5cc88387a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20N=C3=BCsse?= Date: Mon, 2 Oct 2023 21:26:46 +0200 Subject: [PATCH 1/6] feat: expose image api for other clients as api v1.4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Felix NĂ¼sse --- appinfo/routes.php | 20 +- composer.lock | 64 +++--- docs/api/v1.md | 61 +++++- lib/AppInfo/Application.php | 2 +- lib/Controller/NotesApiController.php | 49 +++++ package-lock.json | 267 -------------------------- 6 files changed, 159 insertions(+), 304 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 27fda7d02..5de2affee 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -183,7 +183,7 @@ 'url' => '/api/{apiVersion}/settings', 'verb' => 'GET', 'requirements' => [ - 'apiVersion' => '(v1)', + 'apiVersion' => '(v1|v1.4)', ], ], [ @@ -203,4 +203,22 @@ 'path' => '.+', ], ], + [ + 'name' => 'notes_api#getAttachment', + 'url' => '/api/{apiVersion}/attachment/{noteid}', + 'verb' => 'GET', + 'requirements' => [ + 'apiVersion' => '(v1.4)', + 'noteid' => '\d+' + ], + ], + [ + 'name' => 'notes_api#uploadFile', + 'url' => '/api/{apiVersion}/attachment/{noteid}', + 'verb' => 'POST', + 'requirements' => [ + 'apiVersion' => '(v1.4)', + 'noteid' => '\d+' + ], + ], ]]; diff --git a/composer.lock b/composer.lock index 20ea77a99..fcb042168 100644 --- a/composer.lock +++ b/composer.lock @@ -653,26 +653,26 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.22.0", + "version": "v3.28.0", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "8701394f0c7cd450ac4fa577d24589122c1d5d5e" + "reference": "6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/8701394f0c7cd450ac4fa577d24589122c1d5d5e", - "reference": "8701394f0c7cd450ac4fa577d24589122c1d5d5e", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc", + "reference": "6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc", "shasum": "" }, "require": { "ext-filter": "*", "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.61.1", + "friendsofphp/php-cs-fixer": "^3.76", "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.4 || ^10.5.29" + "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" }, "type": "library", "autoload": { @@ -693,9 +693,15 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.22.0" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.28.0" }, - "time": "2024-08-16T20:44:35+00:00" + "funding": [ + { + "url": "https://github.com/kubawerlos", + "type": "github" + } + ], + "time": "2025-07-01T18:08:34+00:00" }, { "name": "microsoft/tolerant-php-parser", @@ -795,21 +801,21 @@ }, { "name": "nextcloud/coding-standard", - "version": "v1.3.2", + "version": "v1.4.0", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d" + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", - "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", + "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", "shasum": "" }, "require": { "kubawerlos/php-cs-fixer-custom-fixers": "^3.22", - "php": "^7.3|^8.0", + "php": "^8.0", "php-cs-fixer/shim": "^3.17" }, "type": "library", @@ -829,11 +835,14 @@ } ], "description": "Nextcloud coding standards for the php cs fixer", + "keywords": [ + "dev" + ], "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.3.2" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" }, - "time": "2024-10-14T16:49:05+00:00" + "time": "2025-06-19T12:27:27+00:00" }, { "name": "nextcloud/ocp", @@ -1188,16 +1197,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", - "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", "shasum": "" }, "require": { @@ -1229,9 +1238,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" }, - "time": "2025-02-19T13:28:12+00:00" + "time": "2025-07-13T07:04:09+00:00" }, { "name": "psalm/phar", @@ -2097,20 +2106,19 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.31.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", "shasum": "" }, "require": { - "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -2158,7 +2166,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" }, "funding": [ { @@ -2174,7 +2182,7 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-php80", diff --git a/docs/api/v1.md b/docs/api/v1.md index 57d5e4d8b..962cbbda5 100644 --- a/docs/api/v1.md +++ b/docs/api/v1.md @@ -9,12 +9,13 @@ In this document, the Notes API major version 1 and all its minor versions are d ## Minor versions -| API version | Introduced with app version | Remarkable Changes | -|:-----------:|:----------------------------|:-------------------| -| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content | -| **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 | +| API version | Introduced with app version | Remarkable Changes | +|:-----------:|:----------------------------|:---------------------------------------------------| +| **1.0** | Notes 3.3 (May 2020) | Separate title, no auto rename based on content | +| **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 (November 2023) | Add external image api | @@ -136,7 +137,7 @@ Note not found.
Details #### Request parameters -- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example: +- **Body**: some or all "read/write" attributes (see section [Note attributes](#note-attributes)), example: ```js { "title": "New note", @@ -278,6 +279,52 @@ No valid authentication credentials supplied. +### Get attachment (`GET /attachment/{id}`) +
Details + +*(since API v1.4)* + +#### Request parameters +| Parameter | Type | Description | +|:----------|:------------------------|:-----------------------------------| +| `path` | string, required (path) | Path or name of the image to load. | + +#### Response +##### 200 OK +- **Body**: Image or File + +##### 400 Bad Request +Endpoint not supported by installed notes app version (requires API version 1.4). + +##### 401 Unauthorized +No valid authentication credentials supplied. +
+ + +### Put attachment (`POST /attachment/{id}`) +
Details + +*(since API v1.4)* + +#### Request parameters +None. + +#### Response +##### 200 OK +- **Body**: Filename in json encoded: +```js +{ + "filename": "image.jpg" +} +``` + +##### 400 Bad Request +Endpoint not supported by installed notes app version (requires API version 1.4). + +##### 401 Unauthorized +No valid authentication credentials supplied. +
+ ## Preventing lost updates and conflict solution While changing a note using a Notes client, the same note may be changed by another client. diff --git a/lib/AppInfo/Application.php b/lib/AppInfo/Application.php index 29e9dc18f..44337ee62 100644 --- a/lib/AppInfo/Application.php +++ b/lib/AppInfo/Application.php @@ -21,7 +21,7 @@ class Application extends App implements IBootstrap { public const APP_ID = 'notes'; - public static array $API_VERSIONS = [ '0.2', '1.3' ]; + public static array $API_VERSIONS = [ '0.2', '1.3', '1.4' ]; public function __construct(array $urlParams = []) { parent::__construct(self::APP_ID, $urlParams); diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index 8ff0a300d..a2feaf80e 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -18,6 +18,8 @@ use OCP\AppFramework\ApiController; use OCP\AppFramework\Http; use OCP\AppFramework\Http\JSONResponse; +use OCP\AppFramework\Http\StreamResponse; +use OCP\Files\IMimeTypeDetector; use OCP\IRequest; class NotesApiController extends ApiController { @@ -25,6 +27,7 @@ class NotesApiController extends ApiController { private MetaService $metaService; private SettingsService $settingsService; private Helper $helper; + private IMimeTypeDetector $mimeTypeDetector; public function __construct( string $AppName, @@ -33,12 +36,14 @@ public function __construct( MetaService $metaService, SettingsService $settingsService, Helper $helper, + IMimeTypeDetector $mimeTypeDetector ) { parent::__construct($AppName, $request); $this->service = $service; $this->metaService = $metaService; $this->settingsService = $settingsService; $this->helper = $helper; + $this->mimeTypeDetector = $mimeTypeDetector; } @@ -259,4 +264,48 @@ public function fail() : JSONResponse { return new JSONResponse([], Http::STATUS_BAD_REQUEST); }); } + + + + /** + * With help from: https://github.com/nextcloud/cookbook + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + * @return JSONResponse|StreamResponse + */ + public function getAttachment(int $noteid, string $path): Http\Response { + try { + $targetimage = $this->service->getAttachment( + $this->helper->getUID(), + $noteid, + $path + ); + $response = new StreamResponse($targetimage->fopen('rb')); + $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurldecode($targetimage->getName()) . '"'); + $response->addHeader('Content-Type', $this->mimeTypeDetector->getSecureMimeType($targetimage->getMimeType())); + $response->addHeader('Cache-Control', 'public, max-age=604800'); + return $response; + } catch (\Exception $e) { + $this->helper->logException($e); + return $this->helper->createErrorResponse($e, Http::STATUS_NOT_FOUND); + } + } + + /** + * @NoAdminRequired + * @CORS + * @NoCSRFRequired + */ + public function uploadFile(int $noteid): JSONResponse { + $file = $this->request->getUploadedFile('file'); + return $this->helper->handleErrorResponse(function () use ($noteid, $file) { + return $this->service->createImage( + $this->helper->getUID(), + $noteid, + $file + ); + }); + } + } diff --git a/package-lock.json b/package-lock.json index c55194148..ec077fe50 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3067,258 +3067,6 @@ "@parcel/watcher-win32-x64": "2.4.1" } }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", - "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "android" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", - "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", - "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", - "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "freebsd" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", - "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", - "cpu": [ - "arm" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", - "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", - "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", - "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", - "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "linux" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", - "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", - "cpu": [ - "arm64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", - "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", - "cpu": [ - "ia32" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", - "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", - "cpu": [ - "x64" - ], - "dev": true, - "optional": true, - "os": [ - "win32" - ], - "peer": true, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -7601,21 +7349,6 @@ "dev": true, "peer": true }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "peer": true, - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", From 00775d2d0cb58da1648f0494bf4ca0996a119087 Mon Sep 17 00:00:00 2001 From: oli-ver Date: Tue, 29 Jul 2025 21:52:23 +0530 Subject: [PATCH 2/6] fix: Place notes_api#fail route at end of route list Signed-off-by: oli-ver --- appinfo/routes.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/appinfo/routes.php b/appinfo/routes.php index 5de2affee..769ace2af 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -186,14 +186,6 @@ 'apiVersion' => '(v1|v1.4)', ], ], - [ - 'name' => 'notes_api#fail', - 'url' => '/api/{catchAll}', - 'verb' => 'GET', - 'requirements' => [ - 'catchAll' => '.*', - ], - ], [ 'name' => 'notes_api#preflighted_cors', 'url' => '/api/{apiVersion}/{path}', @@ -221,4 +213,12 @@ 'noteid' => '\d+' ], ], + [ + 'name' => 'notes_api#fail', + 'url' => '/api/{catchAll}', + 'verb' => 'GET', + 'requirements' => [ + 'catchAll' => '.*', + ], + ], ]]; From 65ccec33829fb42b6c81c520e55279a3df0ef958 Mon Sep 17 00:00:00 2001 From: oli-ver Date: Sat, 9 Aug 2025 10:23:34 +0530 Subject: [PATCH 3/6] docs: Update attachment api documentation Updates the date of the contribution Adds missing parameter documentation Adds example curl requests how to call the API Use term attachment instead of image to make more generic Resolves review comment https://github.com/nextcloud/notes/pull/1600#discussion_r2263835708 Signed-off-by: oli-ver --- docs/api/v1.md | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/docs/api/v1.md b/docs/api/v1.md index 962cbbda5..ab43d5ff1 100644 --- a/docs/api/v1.md +++ b/docs/api/v1.md @@ -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 (November 2023) | Add external image api | +| **1.4** | Notes 4.9 (August 2025) | Add external image api | @@ -285,9 +285,17 @@ No valid authentication credentials supplied. *(since API v1.4)* #### Request parameters -| Parameter | Type | Description | -|:----------|:------------------------|:-----------------------------------| -| `path` | string, required (path) | Path or name of the image to load. | +| 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. | + +Example: + +```bash +curl -u "user:password" "https://yournextcloud.com/index.php/apps/notes/api/v1.4/attachment/?path=" -o .jpg +``` + #### Response ##### 200 OK @@ -307,7 +315,21 @@ No valid authentication credentials supplied. *(since API v1.4)* #### Request parameters -None. +| Parameter | Type | Description | +|:----------|:------------------------|:------------------------------------------------| +| `id` | integer, required (path)| ID of the note to upload the attachment to | + +Example: + +```bash +curl -u "user:password" \ + -X POST \ + -F "file=@/path/to/image.png" \ + "https://yournextcloud.com/index.php/apps/notes/api/v1.4/attachment/" + +# The post request will return the filename that was generated: +{"filename":"d8aef2005b4f815fec8ade5388240f2c.png"} +``` #### Response ##### 200 OK From d9f5d71b392283cc6df2ea59d855a82862334969 Mon Sep 17 00:00:00 2001 From: oli-ver Date: Sat, 9 Aug 2025 10:31:56 +0530 Subject: [PATCH 4/6] Revert composer.lock and package-lock.json to state of main branch Signed-off-by: oli-ver --- composer.lock | 64 +++++------ package-lock.json | 267 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 295 insertions(+), 36 deletions(-) diff --git a/composer.lock b/composer.lock index fcb042168..20ea77a99 100644 --- a/composer.lock +++ b/composer.lock @@ -653,26 +653,26 @@ }, { "name": "kubawerlos/php-cs-fixer-custom-fixers", - "version": "v3.28.0", + "version": "v3.22.0", "source": { "type": "git", "url": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers.git", - "reference": "6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc" + "reference": "8701394f0c7cd450ac4fa577d24589122c1d5d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc", - "reference": "6115bffca3b8d1bfbd7eff7bdd5df1ffb3c8eddc", + "url": "https://api.github.com/repos/kubawerlos/php-cs-fixer-custom-fixers/zipball/8701394f0c7cd450ac4fa577d24589122c1d5d5e", + "reference": "8701394f0c7cd450ac4fa577d24589122c1d5d5e", "shasum": "" }, "require": { "ext-filter": "*", "ext-tokenizer": "*", - "friendsofphp/php-cs-fixer": "^3.76", + "friendsofphp/php-cs-fixer": "^3.61.1", "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^9.6.22 || 10.5.45 || ^11.5.7" + "phpunit/phpunit": "^9.6.4 || ^10.5.29" }, "type": "library", "autoload": { @@ -693,15 +693,9 @@ "description": "A set of custom fixers for PHP CS Fixer", "support": { "issues": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/issues", - "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.28.0" + "source": "https://github.com/kubawerlos/php-cs-fixer-custom-fixers/tree/v3.22.0" }, - "funding": [ - { - "url": "https://github.com/kubawerlos", - "type": "github" - } - ], - "time": "2025-07-01T18:08:34+00:00" + "time": "2024-08-16T20:44:35+00:00" }, { "name": "microsoft/tolerant-php-parser", @@ -801,21 +795,21 @@ }, { "name": "nextcloud/coding-standard", - "version": "v1.4.0", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/nextcloud/coding-standard.git", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011" + "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/8e06808c1423e9208d63d1bd205b9a38bd400011", - "reference": "8e06808c1423e9208d63d1bd205b9a38bd400011", + "url": "https://api.github.com/repos/nextcloud/coding-standard/zipball/9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", + "reference": "9c719c4747fa26efc12f2e8b21c14a9a75c6ba6d", "shasum": "" }, "require": { "kubawerlos/php-cs-fixer-custom-fixers": "^3.22", - "php": "^8.0", + "php": "^7.3|^8.0", "php-cs-fixer/shim": "^3.17" }, "type": "library", @@ -835,14 +829,11 @@ } ], "description": "Nextcloud coding standards for the php cs fixer", - "keywords": [ - "dev" - ], "support": { "issues": "https://github.com/nextcloud/coding-standard/issues", - "source": "https://github.com/nextcloud/coding-standard/tree/v1.4.0" + "source": "https://github.com/nextcloud/coding-standard/tree/v1.3.2" }, - "time": "2025-06-19T12:27:27+00:00" + "time": "2024-10-14T16:49:05+00:00" }, { "name": "nextcloud/ocp", @@ -1197,16 +1188,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.2.0", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8" + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8", - "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", "shasum": "" }, "require": { @@ -1238,9 +1229,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.2.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" }, - "time": "2025-07-13T07:04:09+00:00" + "time": "2025-02-19T13:28:12+00:00" }, { "name": "psalm/phar", @@ -2106,19 +2097,20 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -2166,7 +2158,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -2182,7 +2174,7 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php80", diff --git a/package-lock.json b/package-lock.json index ec077fe50..c55194148 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3067,6 +3067,258 @@ "@parcel/watcher-win32-x64": "2.4.1" } }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.4.1.tgz", + "integrity": "sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.4.1.tgz", + "integrity": "sha512-ln41eihm5YXIY043vBrrHfn94SIBlqOWmoROhsMVTSXGh0QahKGy77tfEywQ7v3NywyxBBkGIfrWRHm0hsKtzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.4.1.tgz", + "integrity": "sha512-yrw81BRLjjtHyDu7J61oPuSoeYWR3lDElcPGJyOvIXmor6DEo7/G2u1o7I38cwlcoBHQFULqF6nesIX3tsEXMg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.4.1.tgz", + "integrity": "sha512-TJa3Pex/gX3CWIx/Co8k+ykNdDCLx+TuZj3f3h7eOjgpdKM+Mnix37RYsYU4LHhiYJz3DK5nFCCra81p6g050w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.4.1.tgz", + "integrity": "sha512-4rVYDlsMEYfa537BRXxJ5UF4ddNwnr2/1O4MHM5PjI9cvV2qymvhwZSFgXqbS8YoTk5i/JR0L0JDs69BUn45YA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.4.1.tgz", + "integrity": "sha512-BJ7mH985OADVLpbrzCLgrJ3TOpiZggE9FMblfO65PlOCdG++xJpKUJ0Aol74ZUIYfb8WsRlUdgrZxKkz3zXWYA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.4.1.tgz", + "integrity": "sha512-p4Xb7JGq3MLgAfYhslU2SjoV9G0kI0Xry0kuxeG/41UfpjHGOhv7UoUDAz/jb1u2elbhazy4rRBL8PegPJFBhA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.4.1.tgz", + "integrity": "sha512-s9O3fByZ/2pyYDPoLM6zt92yu6P4E39a03zvO0qCHOTjxmt3GHRMLuRZEWhWLASTMSrrnVNWdVI/+pUElJBBBg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.4.1.tgz", + "integrity": "sha512-L2nZTYR1myLNST0O632g0Dx9LyMNHrn6TOt76sYxWLdff3cB22/GZX2UPtJnaqQPdCRoszoY5rcOj4oMTtp5fQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.4.1.tgz", + "integrity": "sha512-Uq2BPp5GWhrq/lcuItCHoqxjULU1QYEcyjSO5jqqOK8RNFDBQnenMMx4gAl3v8GiWa59E9+uDM7yZ6LxwUIfRg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.4.1.tgz", + "integrity": "sha512-maNRit5QQV2kgHFSYwftmPBxiuK5u4DXjbXx7q6eKjq5dsLXZ4FJiVvlcw35QXzk0KrUecJmuVFbj4uV9oYrcw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.4.1.tgz", + "integrity": "sha512-+DvS92F9ezicfswqrvIRM2njcYJbd5mb9CUgtrHCHmvn7pPPa+nMDRu1o1bYYz/l5IB2NVGNJWiH7h1E58IF2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "peer": true, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -7349,6 +7601,21 @@ "dev": true, "peer": true }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", From 2137827952e2831944285706a613953fd7936361 Mon Sep 17 00:00:00 2001 From: oli-ver Date: Sat, 9 Aug 2025 11:34:08 +0530 Subject: [PATCH 5/6] fix: Correct psalm linter findings Signed-off-by: oli-ver --- lib/Controller/NotesApiController.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index a2feaf80e..c0ce166d6 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -281,7 +281,11 @@ public function getAttachment(int $noteid, string $path): Http\Response { $noteid, $path ); - $response = new StreamResponse($targetimage->fopen('rb')); + $fileHandle = $targetimage->fopen('rb'); + if ($fileHandle === false) { + throw new \Exception('Could not open file'); + } + $response = new StreamResponse($fileHandle); $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurldecode($targetimage->getName()) . '"'); $response->addHeader('Content-Type', $this->mimeTypeDetector->getSecureMimeType($targetimage->getMimeType())); $response->addHeader('Cache-Control', 'public, max-age=604800'); @@ -299,7 +303,7 @@ public function getAttachment(int $noteid, string $path): Http\Response { */ public function uploadFile(int $noteid): JSONResponse { $file = $this->request->getUploadedFile('file'); - return $this->helper->handleErrorResponse(function () use ($noteid, $file) { + return $this->helper->handleErrorResponse(function () use ($noteid, $file): array { return $this->service->createImage( $this->helper->getUID(), $noteid, From 4183669ff688ff7327d720dfcbde08dedac197a6 Mon Sep 17 00:00:00 2001 From: oli-ver Date: Sat, 9 Aug 2025 11:40:52 +0530 Subject: [PATCH 6/6] fix: Format code correctly Signed-off-by: oli-ver --- lib/Controller/NotesApiController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Controller/NotesApiController.php b/lib/Controller/NotesApiController.php index c0ce166d6..215ea67ba 100644 --- a/lib/Controller/NotesApiController.php +++ b/lib/Controller/NotesApiController.php @@ -36,7 +36,7 @@ public function __construct( MetaService $metaService, SettingsService $settingsService, Helper $helper, - IMimeTypeDetector $mimeTypeDetector + IMimeTypeDetector $mimeTypeDetector, ) { parent::__construct($AppName, $request); $this->service = $service;