From faf1523a75a71008c762b66d753a109cbb684b8f Mon Sep 17 00:00:00 2001 From: jld3103 Date: Tue, 18 Oct 2022 12:25:35 +0200 Subject: [PATCH 1/4] Update nextcloud server and notifications to 25.0.0 --- external/nextcloud-notifications | 2 +- external/nextcloud-server | 2 +- .../nextcloud/lib/src/nextcloud.openapi.dart | 15 +- .../lib/src/nextcloud.openapi.g.dart | 6 + .../nextcloud/lib/src/nextcloud.openapi.json | 14 +- .../nextcloud/lib/src/version_supported.dart | 2 +- packages/nextcloud/test/helper.dart | 4 +- packages/nextcloud/test/overlay/.gitkeep | 0 .../apps/user_status/appinfo/routes.php | 42 --- .../lib/Controller/HeartbeatController.php | 114 ------- specs/core.json | 15 +- specs/notifications.json | 2 +- specs/provisioning_api.json | 2 +- specs/templates/appinfo_core.xml | 2 +- specs/templates/core.json | 286 +++++++++++++----- specs/templates/notifications.json | 2 +- specs/templates/provisioning_api.json | 136 ++++++++- specs/templates/user_status.json | 4 +- specs/user_status.json | 3 +- tool/Dockerfile.dev | 5 +- tool/update.sh | 1 + 21 files changed, 403 insertions(+), 256 deletions(-) delete mode 100644 packages/nextcloud/test/overlay/.gitkeep delete mode 100644 packages/nextcloud/test/overlay/apps/user_status/appinfo/routes.php delete mode 100644 packages/nextcloud/test/overlay/apps/user_status/lib/Controller/HeartbeatController.php diff --git a/external/nextcloud-notifications b/external/nextcloud-notifications index 7190056d..65bb0849 160000 --- a/external/nextcloud-notifications +++ b/external/nextcloud-notifications @@ -1 +1 @@ -Subproject commit 7190056dd64da2f2c4b2fd3fd8d40d26cb768937 +Subproject commit 65bb0849179cf93c161387a730f4abc79a782f77 diff --git a/external/nextcloud-server b/external/nextcloud-server index 3aabb381..20ea9a25 160000 --- a/external/nextcloud-server +++ b/external/nextcloud-server @@ -1 +1 @@ -Subproject commit 3aabb381b9e7ffc5b13f951b8fb89494fe903a2c +Subproject commit 20ea9a25353129b56d46951fe7d23939665ab2b2 diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.dart b/packages/nextcloud/lib/src/nextcloud.openapi.dart index ebfe1255..9ad0f003 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.dart +++ b/packages/nextcloud/lib/src/nextcloud.openapi.dart @@ -209,6 +209,8 @@ class CoreServerCapabilities_Ocs_Data_Capabilities_Core { CoreServerCapabilities_Ocs_Data_Capabilities_Core({ required this.pollinterval, required this.webdavRoot, + required this.referenceApi, + required this.referenceRegex, }); factory CoreServerCapabilities_Ocs_Data_Capabilities_Core.fromJson(Map json) => @@ -219,6 +221,12 @@ class CoreServerCapabilities_Ocs_Data_Capabilities_Core { @JsonKey(name: 'webdav-root') final String webdavRoot; + @JsonKey(name: 'reference-api') + final bool referenceApi; + + @JsonKey(name: 'reference-regex') + final String referenceRegex; + // coverage:ignore-start Map toJson() => _$CoreServerCapabilities_Ocs_Data_Capabilities_CoreToJson(this); // coverage:ignore-end @@ -560,11 +568,16 @@ class CoreServerCapabilities_Ocs_Data_Capabilities_Ocm { @JsonSerializable() class CoreServerCapabilities_Ocs_Data_Capabilities_Dav { - CoreServerCapabilities_Ocs_Data_Capabilities_Dav({required this.chunking}); + CoreServerCapabilities_Ocs_Data_Capabilities_Dav({ + this.bulkupload, + required this.chunking, + }); factory CoreServerCapabilities_Ocs_Data_Capabilities_Dav.fromJson(Map json) => _$CoreServerCapabilities_Ocs_Data_Capabilities_DavFromJson(json); + final String? bulkupload; + final String chunking; // coverage:ignore-start diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.g.dart b/packages/nextcloud/lib/src/nextcloud.openapi.g.dart index 72d5fd6b..1c6f3595 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.g.dart +++ b/packages/nextcloud/lib/src/nextcloud.openapi.g.dart @@ -70,6 +70,8 @@ CoreServerCapabilities_Ocs_Data_Capabilities_Core _$CoreServerCapabilities_Ocs_D CoreServerCapabilities_Ocs_Data_Capabilities_Core( pollinterval: json['pollinterval'] as int, webdavRoot: json['webdav-root'] as String, + referenceApi: json['reference-api'] as bool, + referenceRegex: json['reference-regex'] as String, ); Map _$CoreServerCapabilities_Ocs_Data_Capabilities_CoreToJson( @@ -77,6 +79,8 @@ Map _$CoreServerCapabilities_Ocs_Data_Capabilities_CoreToJson( { 'pollinterval': instance.pollinterval, 'webdav-root': instance.webdavRoot, + 'reference-api': instance.referenceApi, + 'reference-regex': instance.referenceRegex, }; CoreServerCapabilities_Ocs_Data_Capabilities_Bruteforce @@ -345,12 +349,14 @@ Map _$CoreServerCapabilities_Ocs_Data_Capabilities_OcmToJson( CoreServerCapabilities_Ocs_Data_Capabilities_Dav _$CoreServerCapabilities_Ocs_Data_Capabilities_DavFromJson( Map json) => CoreServerCapabilities_Ocs_Data_Capabilities_Dav( + bulkupload: json['bulkupload'] as String?, chunking: json['chunking'] as String, ); Map _$CoreServerCapabilities_Ocs_Data_Capabilities_DavToJson( CoreServerCapabilities_Ocs_Data_Capabilities_Dav instance) => { + 'bulkupload': instance.bulkupload, 'chunking': instance.chunking, }; diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.json b/packages/nextcloud/lib/src/nextcloud.openapi.json index f08ce497..81145aa2 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.json +++ b/packages/nextcloud/lib/src/nextcloud.openapi.json @@ -182,7 +182,9 @@ "type": "object", "required": [ "pollinterval", - "webdav-root" + "webdav-root", + "reference-api", + "reference-regex" ], "properties": { "pollinterval": { @@ -190,6 +192,12 @@ }, "webdav-root": { "type": "string" + }, + "reference-api": { + "type": "boolean" + }, + "reference-regex": { + "type": "string" } } }, @@ -466,6 +474,9 @@ "chunking" ], "properties": { + "bulkupload": { + "type": "string" + }, "chunking": { "type": "string" } @@ -3678,7 +3689,6 @@ } }, "/ocs/v1.php/apps/user_status/api/v1/heartbeat": { - "description": "Depends on https://github.com/nextcloud/server/pull/32646", "put": { "operationId": "heartbeat", "tags": [ diff --git a/packages/nextcloud/lib/src/version_supported.dart b/packages/nextcloud/lib/src/version_supported.dart index f0455193..7a9ffca6 100644 --- a/packages/nextcloud/lib/src/version_supported.dart +++ b/packages/nextcloud/lib/src/version_supported.dart @@ -4,7 +4,7 @@ part of '../nextcloud.dart'; extension CoreVersionSupported on CoreClient { /// Checks if the app on the server is supported by the client Future isSupported([final CoreServerCapabilities_Ocs_Data? capabilities]) async => - (capabilities ?? (await getCapabilities()).ocs.data).version.major == 24; + (capabilities ?? (await getCapabilities()).ocs.data).version.major == 25; } // ignore: public_member_api_docs diff --git a/packages/nextcloud/test/helper.dart b/packages/nextcloud/test/helper.dart index ede35791..0a84bfdd 100644 --- a/packages/nextcloud/test/helper.dart +++ b/packages/nextcloud/test/helper.dart @@ -8,7 +8,7 @@ import 'package:nextcloud/nextcloud.dart'; import 'package:process_run/cmd_run.dart'; import 'package:test/test.dart'; -const String nextcloudVersion = '24.0.6'; +const String nextcloudVersion = '25.0.0'; const String defaultUsername = 'user1'; const String defaultPassword = 'user1'; @@ -250,8 +250,6 @@ class TestDockerHelper { 'RUN ./occ maintenance:install --admin-pass admin --admin-email admin@example.com', 'RUN ./occ app:disable password_policy', 'RUN ./occ config:system:set allow_local_remote_servers --value=true', - 'RUN sed -i "s/localhost/host.docker.internal/" /usr/src/nextcloud/apps/notifications/lib/Controller/PushController.php', - 'ADD overlay /usr/src/nextcloud/', generateCreateTestUserInstruction(), if (apps != null) ...[ for (final app in apps) ...[ diff --git a/packages/nextcloud/test/overlay/.gitkeep b/packages/nextcloud/test/overlay/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/packages/nextcloud/test/overlay/apps/user_status/appinfo/routes.php b/packages/nextcloud/test/overlay/apps/user_status/appinfo/routes.php deleted file mode 100644 index 83e944eb..00000000 --- a/packages/nextcloud/test/overlay/apps/user_status/appinfo/routes.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -return [ - 'ocs' => [ - // Routes for querying statuses - ['name' => 'Statuses#findAll', 'url' => '/api/v1/statuses', 'verb' => 'GET'], - ['name' => 'Statuses#find', 'url' => '/api/v1/statuses/{userId}', 'verb' => 'GET'], - // Routes for manipulating your own status - ['name' => 'UserStatus#getStatus', 'url' => '/api/v1/user_status', 'verb' => 'GET'], - ['name' => 'UserStatus#setStatus', 'url' => '/api/v1/user_status/status', 'verb' => 'PUT'], - ['name' => 'UserStatus#setPredefinedMessage', 'url' => '/api/v1/user_status/message/predefined', 'verb' => 'PUT'], - ['name' => 'UserStatus#setCustomMessage', 'url' => '/api/v1/user_status/message/custom', 'verb' => 'PUT'], - ['name' => 'UserStatus#clearMessage', 'url' => '/api/v1/user_status/message', 'verb' => 'DELETE'], - // Routes for listing default routes - ['name' => 'PredefinedStatus#findAll', 'url' => '/api/v1/predefined_statuses/', 'verb' => 'GET'], - // Route for doing heartbeats - ['name' => 'Heartbeat#heartbeat', 'url' => '/api/v1/heartbeat', 'verb' => 'PUT'], - ], -]; diff --git a/packages/nextcloud/test/overlay/apps/user_status/lib/Controller/HeartbeatController.php b/packages/nextcloud/test/overlay/apps/user_status/lib/Controller/HeartbeatController.php deleted file mode 100644 index 02b2af03..00000000 --- a/packages/nextcloud/test/overlay/apps/user_status/lib/Controller/HeartbeatController.php +++ /dev/null @@ -1,114 +0,0 @@ - - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * - */ -namespace OCA\UserStatus\Controller; - -use OCA\UserStatus\Db\UserStatus; -use OCA\UserStatus\Service\StatusService; -use OCP\AppFramework\Controller; -use OCP\AppFramework\Db\DoesNotExistException; -use OCP\AppFramework\Http; -use OCP\AppFramework\Http\JSONResponse; -use OCP\AppFramework\OCSController; -use OCP\AppFramework\Utility\ITimeFactory; -use OCP\EventDispatcher\IEventDispatcher; -use OCP\IRequest; -use OCP\IUserSession; -use OCP\User\Events\UserLiveStatusEvent; -use OCP\UserStatus\IUserStatus; - -class HeartbeatController extends OCSController { - - /** @var IEventDispatcher */ - private $eventDispatcher; - - /** @var IUserSession */ - private $userSession; - - /** @var ITimeFactory */ - private $timeFactory; - - /** @var StatusService */ - private $service; - - public function __construct(string $appName, - IRequest $request, - IEventDispatcher $eventDispatcher, - IUserSession $userSession, - ITimeFactory $timeFactory, - StatusService $service) { - parent::__construct($appName, $request); - $this->eventDispatcher = $eventDispatcher; - $this->userSession = $userSession; - $this->timeFactory = $timeFactory; - $this->service = $service; - } - - /** - * @NoAdminRequired - * - * @param string $status - * @return JSONResponse - */ - public function heartbeat(string $status): JSONResponse { - if (!\in_array($status, [IUserStatus::ONLINE, IUserStatus::AWAY], true)) { - return new JSONResponse([], Http::STATUS_BAD_REQUEST); - } - - $user = $this->userSession->getUser(); - if ($user === null) { - return new JSONResponse([], Http::STATUS_INTERNAL_SERVER_ERROR); - } - - $event = new UserLiveStatusEvent( - $user, - $status, - $this->timeFactory->getTime() - ); - - $this->eventDispatcher->dispatchTyped($event); - - $userStatus = $event->getUserStatus(); - if (!$userStatus) { - return new JSONResponse([], Http::STATUS_NO_CONTENT); - } - - /** @psalm-suppress UndefinedInterfaceMethod */ - return new JSONResponse($this->formatStatus($userStatus->getInternal())); - } - - private function formatStatus(UserStatus $status): array { - return [ - 'userId' => $status->getUserId(), - 'message' => $status->getCustomMessage(), - 'messageId' => $status->getMessageId(), - 'messageIsPredefined' => $status->getMessageId() !== null, - 'icon' => $status->getCustomIcon(), - 'clearAt' => $status->getClearAt(), - 'status' => $status->getStatus(), - 'statusIsUserDefined' => $status->getIsUserDefined(), - ]; - } -} diff --git a/specs/core.json b/specs/core.json index f4b26553..91178778 100644 --- a/specs/core.json +++ b/specs/core.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Core", - "version": "24.0.6", + "version": "25.0.0", "description": "Core functionality of Nextcloud", "license": { "name": "agpl", @@ -168,7 +168,9 @@ "type": "object", "required": [ "pollinterval", - "webdav-root" + "webdav-root", + "reference-api", + "reference-regex" ], "properties": { "pollinterval": { @@ -176,6 +178,12 @@ }, "webdav-root": { "type": "string" + }, + "reference-api": { + "type": "boolean" + }, + "reference-regex": { + "type": "string" } } }, @@ -452,6 +460,9 @@ "chunking" ], "properties": { + "bulkupload": { + "type": "string" + }, "chunking": { "type": "string" } diff --git a/specs/notifications.json b/specs/notifications.json index 1684f4c0..ee878298 100644 --- a/specs/notifications.json +++ b/specs/notifications.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Notifications", - "version": "2.12.1", + "version": "2.13.1", "description": "This app provides a backend and frontend for the notification API available in Nextcloud.", "license": { "name": "agpl", diff --git a/specs/provisioning_api.json b/specs/provisioning_api.json index 63940e08..a58dde21 100644 --- a/specs/provisioning_api.json +++ b/specs/provisioning_api.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Provisioning API", - "version": "1.14.0", + "version": "1.15.0", "description": "This application enables a set of APIs that external systems can use to manage users, groups and apps.", "license": { "name": "agpl", diff --git a/specs/templates/appinfo_core.xml b/specs/templates/appinfo_core.xml index 881f0762..457ef147 100644 --- a/specs/templates/appinfo_core.xml +++ b/specs/templates/appinfo_core.xml @@ -4,6 +4,6 @@ Core Core functionality of Nextcloud - 24.0.6 + 25.0.0 agpl diff --git a/specs/templates/core.json b/specs/templates/core.json index f6c8421c..f1ca574a 100644 --- a/specs/templates/core.json +++ b/specs/templates/core.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Core", - "version": "24.0.6", + "version": "25.0.0", "description": "Core functionality of Nextcloud", "license": { "name": "agpl", @@ -199,6 +199,44 @@ } } }, + "/core/avatar/{userId}/{size}/dark": { + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "get": { + "operationId": "avatar-getavatardark-TODO", + "tags": [ + "core" + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/core/avatar/{userId}/{size}": { "parameters": [ { @@ -247,7 +285,7 @@ { "name": "path", "in": "query", - "required": true, + "required": false, "schema": { "type": "string" } @@ -295,7 +333,7 @@ { "name": "crop", "in": "query", - "required": true, + "required": false, "schema": { "type": "array" } @@ -335,6 +373,44 @@ } } }, + "/core/avatar/guest/{guestName}/{size}/dark": { + "parameters": [ + { + "name": "guestName", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "get": { + "operationId": "guestavatar-getavatardark-TODO", + "tags": [ + "core" + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/core/avatar/guest/{guestName}/{size}": { "parameters": [ { @@ -361,6 +437,17 @@ "tags": [ "core" ], + "parameters": [ + { + "name": "darkTheme", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + } + } + ], "responses": { "200": { "description": "", @@ -1318,18 +1405,10 @@ } } }, - "/core/svg/core/{folder}/{fileName}": { + "/core/core/references/preview/{referenceId}": { "parameters": [ { - "name": "folder", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "fileName", + "name": "referenceId", "in": "path", "required": true, "schema": { @@ -1338,70 +1417,10 @@ } ], "get": { - "operationId": "svg-getsvgfromcore-TODO", + "operationId": "reference-preview-TODO", "tags": [ "core" ], - "parameters": [ - { - "name": "color", - "in": "query", - "required": false, - "schema": { - "type": "string", - "default": "ffffff" - } - } - ], - "responses": { - "200": { - "description": "", - "content": { - "application/json": { - "schema": { - "type": "string" - } - } - } - } - } - } - }, - "/core/svg/{app}/{fileName}": { - "parameters": [ - { - "name": "app", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "fileName", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "get": { - "operationId": "svg-getsvgfromapp-TODO", - "tags": [ - "core" - ], - "parameters": [ - { - "name": "color", - "in": "query", - "required": false, - "schema": { - "type": "string", - "default": "ffffff" - } - } - ], "responses": { "200": { "description": "", @@ -1980,7 +1999,7 @@ { "name": "itemType", "in": "query", - "required": true, + "required": false, "schema": { "type": "string" } @@ -1988,7 +2007,7 @@ { "name": "itemId", "in": "query", - "required": true, + "required": false, "schema": { "type": "string" } @@ -2418,6 +2437,121 @@ } } }, + "/ocs/v1.php/core/resolve": { + "get": { + "operationId": "referenceapi-resolveone-TODO", + "tags": [ + "core" + ], + "parameters": [ + { + "name": "reference", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "post": { + "operationId": "referenceapi-resolve-TODO", + "tags": [ + "core" + ], + "parameters": [ + { + "name": "references", + "in": "query", + "required": true, + "schema": { + "type": "array" + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/ocs/v1.php/core/extract": { + "post": { + "operationId": "referenceapi-extract-TODO", + "tags": [ + "core" + ], + "parameters": [ + { + "name": "text", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "resolve", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 0 + } + }, + { + "name": "limit", + "in": "query", + "required": false, + "schema": { + "type": "integer", + "default": 1 + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/ocs/v1.php/core/{targetUserId}": { "parameters": [ { diff --git a/specs/templates/notifications.json b/specs/templates/notifications.json index 909c9bcc..1c8eaea3 100644 --- a/specs/templates/notifications.json +++ b/specs/templates/notifications.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Notifications", - "version": "2.12.1", + "version": "2.13.1", "description": "This app provides a backend and frontend for the notification API available in Nextcloud.", "license": { "name": "agpl", diff --git a/specs/templates/provisioning_api.json b/specs/templates/provisioning_api.json index 45f494f3..fd54c85d 100644 --- a/specs/templates/provisioning_api.json +++ b/specs/templates/provisioning_api.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "Provisioning API", - "version": "1.14.0", + "version": "1.15.0", "description": "This application enables a set of APIs that external systems can use to manage users, groups and apps.", "license": { "name": "agpl", @@ -1317,6 +1317,140 @@ } } }, + "/ocs/v1.php/apps/provisioning_api/api/v1/config/users/{appId}/{configKey}": { + "parameters": [ + { + "name": "appId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "configKey", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "post": { + "operationId": "preferences-setpreference-TODO", + "tags": [ + "provisioning_api" + ], + "parameters": [ + { + "name": "configValue", + "in": "query", + "required": true, + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "delete": { + "operationId": "preferences-deletepreference-TODO", + "tags": [ + "provisioning_api" + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, + "/ocs/v1.php/apps/provisioning_api/api/v1/config/users/{appId}": { + "parameters": [ + { + "name": "appId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "post": { + "operationId": "preferences-setmultiplepreferences-TODO", + "tags": [ + "provisioning_api" + ], + "parameters": [ + { + "name": "configs", + "in": "query", + "required": true, + "schema": { + "type": "array" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + }, + "delete": { + "operationId": "preferences-deletemultiplepreference-TODO", + "tags": [ + "provisioning_api" + ], + "parameters": [ + { + "name": "configKeys", + "in": "query", + "required": true, + "schema": { + "type": "array" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + } + } + } + }, "/apps/provisioning_api/mailVerification/{key}/{token}/{userId}": { "parameters": [ { diff --git a/specs/templates/user_status.json b/specs/templates/user_status.json index 468c5e40..e6528168 100644 --- a/specs/templates/user_status.json +++ b/specs/templates/user_status.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "User status", - "version": "1.4.0", + "version": "1.5.0", "description": "User status", "license": { "name": "agpl", @@ -257,7 +257,7 @@ } } }, - "/apps/user_status/heartbeat": { + "/ocs/v1.php/apps/user_status/api/v1/heartbeat": { "put": { "operationId": "heartbeat-heartbeat-TODO", "tags": [ diff --git a/specs/user_status.json b/specs/user_status.json index 68bc2be2..c8c44ed9 100644 --- a/specs/user_status.json +++ b/specs/user_status.json @@ -2,7 +2,7 @@ "openapi": "3.1.0", "info": { "title": "User status", - "version": "1.4.0", + "version": "1.5.0", "description": "User status", "license": { "name": "agpl", @@ -542,7 +542,6 @@ } }, "/ocs/v1.php/apps/user_status/api/v1/heartbeat": { - "description": "Depends on https://github.com/nextcloud/server/pull/32646", "put": { "operationId": "heartbeat", "tags": [ diff --git a/tool/Dockerfile.dev b/tool/Dockerfile.dev index e9423f27..546c85f7 100644 --- a/tool/Dockerfile.dev +++ b/tool/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM nextcloud:24.0.6 +FROM nextcloud:25.0.0 WORKDIR /usr/src/nextcloud RUN chown -R www-data:www-data . USER www-data @@ -8,9 +8,6 @@ RUN ./occ app:disable password_policy RUN ./occ config:system:set allow_local_remote_servers --value=true RUN ./occ config:system:set trusted_domains 1 --value=10.0.2.2 -RUN sed -i "s/localhost/host.docker.internal/" /usr/src/nextcloud/apps/notifications/lib/Controller/PushController.php -ADD overlay /usr/src/nextcloud/ - RUN OC_PASS="user1" ./occ user:add --password-from-env --display-name "User One" user1 RUN OC_PASS="user2" ./occ user:add --password-from-env --display-name "User Two" user2 diff --git a/tool/update.sh b/tool/update.sh index 9a75205f..bd4baad5 100755 --- a/tool/update.sh +++ b/tool/update.sh @@ -24,6 +24,7 @@ elif [ -d "external/nextcloud-$1" ]; then git fetch --tags latest_tag="$(git tag --sort=v:refname | grep -vi "rc" | grep -vi "alpha" | grep -vi "beta" | tail -n 1)" git reset --hard "$latest_tag" + git submodule update ) else echo "$1 not found" From ea990b9566d67a68630a26b6eb2152a0e753b508 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sun, 23 Oct 2022 19:36:40 +0200 Subject: [PATCH 2/4] specs,nextcloud: Fix user status heartbeat --- .../nextcloud/lib/src/nextcloud.openapi.dart | 4 ++-- .../nextcloud/lib/src/nextcloud.openapi.json | 9 +++++++- packages/nextcloud/test/user_status_test.dart | 22 +++++++++---------- specs/user_status.json | 9 +++++++- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.dart b/packages/nextcloud/lib/src/nextcloud.openapi.dart index 9ad0f003..cbb2daf0 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.dart +++ b/packages/nextcloud/lib/src/nextcloud.openapi.dart @@ -3831,7 +3831,7 @@ class UserStatusClient { throw ApiException.fromResponse(response); // coverage:ignore-line } - Future heartbeat({required UserStatusType status}) async { + Future heartbeat({required UserStatusType status}) async { var path = '/ocs/v1.php/apps/user_status/api/v1/heartbeat'; final queryParameters = {}; final headers = {}; @@ -3844,7 +3844,7 @@ class UserStatusClient { body, ); if (response.statusCode == 200) { - return; + return UserStatus.fromJson(json.decode(utf8.decode(response.body)) as Map); } throw ApiException.fromResponse(response); // coverage:ignore-line } diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.json b/packages/nextcloud/lib/src/nextcloud.openapi.json index 81145aa2..b82dda4e 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.json +++ b/packages/nextcloud/lib/src/nextcloud.openapi.json @@ -3706,7 +3706,14 @@ ], "responses": { "200": { - "description": "" + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserStatus" + } + } + } } } } diff --git a/packages/nextcloud/test/user_status_test.dart b/packages/nextcloud/test/user_status_test.dart index 05271727..70e2f220 100644 --- a/packages/nextcloud/test/user_status_test.dart +++ b/packages/nextcloud/test/user_status_test.dart @@ -158,18 +158,16 @@ Future main() async { }); test('Heartbeat', () async { - await client.userStatus.heartbeat(status: UserStatusType.online); - - final response = await client.userStatus.getStatus(); - expect(response.ocs.data.userStatus!.userId, 'user1'); - expect(response.ocs.data.userStatus!.message, null); - expect(response.ocs.data.userStatus!.messageId, null); - expect(response.ocs.data.userStatus!.messageIsPredefined, false); - expect(response.ocs.data.userStatus!.icon, null); - expect(response.ocs.data.userStatus!.clearAt.userStatusClearAt, null); - expect(response.ocs.data.userStatus!.clearAt.int_, null); - expect(response.ocs.data.userStatus!.status, UserStatusType.online); - expect(response.ocs.data.userStatus!.statusIsUserDefined, false); + final response = await client.userStatus.heartbeat(status: UserStatusType.online); + expect(response.userId, 'user1'); + expect(response.message, null); + expect(response.messageId, null); + expect(response.messageIsPredefined, false); + expect(response.icon, null); + expect(response.clearAt.userStatusClearAt, null); + expect(response.clearAt.int_, null); + expect(response.status, UserStatusType.online); + expect(response.statusIsUserDefined, false); }); }); } diff --git a/specs/user_status.json b/specs/user_status.json index c8c44ed9..c5d9659d 100644 --- a/specs/user_status.json +++ b/specs/user_status.json @@ -559,7 +559,14 @@ ], "responses": { "200": { - "description": "" + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserStatus" + } + } + } } } } From a9cff12a8b026b1283f9ea7c1ac83b1ed586e1f1 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sun, 23 Oct 2022 19:39:13 +0200 Subject: [PATCH 3/4] neon: Implement user status heartbeat --- .../integration_test/screenshot_test.dart | 1 + packages/neon/lib/main.dart | 1 + packages/neon/lib/src/blocs/accounts.dart | 4 +- packages/neon/lib/src/blocs/user_status.dart | 74 +++++++------------ 4 files changed, 32 insertions(+), 48 deletions(-) diff --git a/packages/neon/integration_test/screenshot_test.dart b/packages/neon/integration_test/screenshot_test.dart index 3c3d9ffe..2225b0cd 100644 --- a/packages/neon/integration_test/screenshot_test.dart +++ b/packages/neon/integration_test/screenshot_test.dart @@ -115,6 +115,7 @@ Future pumpAppPage( final accountsBloc = AccountsBloc( requestManager, + platform, Storage('accounts', sharedPreferences), sharedPreferences, globalOptions, diff --git a/packages/neon/lib/main.dart b/packages/neon/lib/main.dart index 0d1f930b..4549514b 100644 --- a/packages/neon/lib/main.dart +++ b/packages/neon/lib/main.dart @@ -42,6 +42,7 @@ Future main() async { final accountsBloc = AccountsBloc( requestManager, + platform, Storage('accounts', sharedPreferences), sharedPreferences, globalOptions, diff --git a/packages/neon/lib/src/blocs/accounts.dart b/packages/neon/lib/src/blocs/accounts.dart index e6780b4e..6fc7e150 100644 --- a/packages/neon/lib/src/blocs/accounts.dart +++ b/packages/neon/lib/src/blocs/accounts.dart @@ -29,6 +29,7 @@ abstract class AccountsBlocStates { class AccountsBloc extends $AccountsBloc { AccountsBloc( this._requestManager, + this._platform, this._storage, this._sharedPreferences, this._globalOptions, @@ -156,12 +157,13 @@ class AccountsBloc extends $AccountsBloc { return _userStatusBlocs[account.id] = UserStatusBloc( _requestManager, + _platform, account, - _activeAccountSubject, ); } final RequestManager _requestManager; + final NeonPlatform _platform; final Storage _storage; final SharedPreferences _sharedPreferences; final GlobalOptions _globalOptions; diff --git a/packages/neon/lib/src/blocs/user_status.dart b/packages/neon/lib/src/blocs/user_status.dart index 833ae6d4..2a811e79 100644 --- a/packages/neon/lib/src/blocs/user_status.dart +++ b/packages/neon/lib/src/blocs/user_status.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'package:flutter/foundation.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:rx_bloc/rx_bloc.dart'; import 'package:rxdart/rxdart.dart'; +import 'package:window_manager/window_manager.dart'; part 'user_status.rxb.g.dart'; @@ -19,67 +19,47 @@ abstract class UserStatusBlocStates { class UserStatusBloc extends $UserStatusBloc { UserStatusBloc( this._requestManager, + this._platform, this._account, - this._activeAccountStream, ) { - _activeAccountStreamSubscription = _activeAccountStream.listen((final activeAccount) { - _cancelTimer(); - final thisAccountActive = activeAccount == _account; - _timer = instantPeriodicTimer( - const Duration(minutes: 5), - (final _) async { - if (thisAccountActive) { - await _heartbeat(); - } - _loadUserStatus(); - }, - ); - }); - } - - void _loadUserStatus() { - // TODO: Fix for no user status - _requestManager - .wrapNextcloud( - _account.client.id, - 'user-status', - () async => _account.client.userStatus.getStatus(), - (final response) => response.ocs.data.userStatus, - previousData: _userStatusSubject.valueOrNull?.data, - ) - .listen(_userStatusSubject.add); + _timer = instantPeriodicTimer( + const Duration(minutes: 5), + (final _) async { + await _heartbeat(); + }, + ); } Future _heartbeat() async { - return; - - // TODO: https://github.com/jld3103/nextcloud-neon/issues/10 - // ignore: dead_code - try { - await _account.client.userStatus.heartbeat(status: UserStatusType.online); - } catch (e) { - debugPrint(e.toString()); - } - } - - void _cancelTimer() { - if (_timer != null) { - _timer!.cancel(); - _timer = null; - } + final isAway = + _platform.canUseWindowManager && (!(await windowManager.isFocused()) || !(await windowManager.isVisible())); + _requestManager.wrapWithoutCache( + () async { + try { + return await _account.client.userStatus.heartbeat( + status: isAway ? UserStatusType.away : UserStatusType.online, + ); + } on ApiException catch (e) { + if (e.statusCode == 204) { + return null; + } + rethrow; + } + }, + ).listen(_userStatusSubject.add); } final RequestManager _requestManager; + final NeonPlatform _platform; final Account _account; - final BehaviorSubject _activeAccountStream; late final StreamSubscription _activeAccountStreamSubscription; - Timer? _timer; + late Timer _timer; final _userStatusSubject = BehaviorSubject>(); @override void dispose() { - _cancelTimer(); + _timer.cancel(); unawaited(_activeAccountStreamSubscription.cancel()); unawaited(_userStatusSubject.close()); super.dispose(); From bb57e82358c890e443a87957b8c04103994d0ff4 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Tue, 25 Oct 2022 23:08:56 +0200 Subject: [PATCH 4/4] specs,nextcloud,neon: Implement dark avatars --- .../neon/lib/src/widgets/account_avatar.dart | 20 +++++++--- .../nextcloud/lib/src/nextcloud.openapi.dart | 22 +++++++++++ .../nextcloud/lib/src/nextcloud.openapi.json | 39 +++++++++++++++++++ specs/core.json | 39 +++++++++++++++++++ 4 files changed, 115 insertions(+), 5 deletions(-) diff --git a/packages/neon/lib/src/widgets/account_avatar.dart b/packages/neon/lib/src/widgets/account_avatar.dart index 5afa3d52..52b10e22 100644 --- a/packages/neon/lib/src/widgets/account_avatar.dart +++ b/packages/neon/lib/src/widgets/account_avatar.dart @@ -12,6 +12,7 @@ class AccountAvatar extends StatelessWidget { @override Widget build(final BuildContext context) { + final isDark = Theme.of(context).brightness == Brightness.dark; final size = (kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt(); return Stack( alignment: Alignment.center, @@ -21,11 +22,20 @@ class AccountAvatar extends StatelessWidget { child: ClipOval( child: CachedAPIImage( account: account, - cacheKey: 'avatar-${account.id}-$size', - download: () async => account.client.core.getAvatar( - userId: account.username, - size: size, - ), + cacheKey: 'avatar-${account.id}-${isDark ? 'dark' : 'light'}$size', + download: () async { + if (isDark) { + return account.client.core.getDarkAvatar( + userId: account.username, + size: size, + ); + } else { + return account.client.core.getAvatar( + userId: account.username, + size: size, + ); + } + }, ), ), ), diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.dart b/packages/nextcloud/lib/src/nextcloud.openapi.dart index cbb2daf0..0cf74575 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.dart +++ b/packages/nextcloud/lib/src/nextcloud.openapi.dart @@ -1606,6 +1606,28 @@ class CoreClient { throw ApiException.fromResponse(response); // coverage:ignore-line } + Future getDarkAvatar({ + required String userId, + required int size, + }) async { + var path = '/avatar/{userId}/{size}/dark'; + final queryParameters = {}; + final headers = {}; + Uint8List? body; + path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString())); + path = path.replaceAll('{size}', Uri.encodeQueryComponent(size.toString())); + final response = await rootClient.doRequest( + 'get', + Uri(path: path, queryParameters: queryParameters).toString(), + headers, + body, + ); + if (response.statusCode == 200) { + return response.body; + } + throw ApiException.fromResponse(response); // coverage:ignore-line + } + Future getAvatar({ required String userId, required int size, diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.json b/packages/nextcloud/lib/src/nextcloud.openapi.json index b82dda4e..14170ed3 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.json +++ b/packages/nextcloud/lib/src/nextcloud.openapi.json @@ -2310,6 +2310,45 @@ } } }, + "/avatar/{userId}/{size}/dark": { + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "get": { + "operationId": "get-dark-avatar", + "tags": [ + "core" + ], + "responses": { + "200": { + "description": "", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, "/avatar/{userId}/{size}": { "parameters": [ { diff --git a/specs/core.json b/specs/core.json index 91178778..550f69c4 100644 --- a/specs/core.json +++ b/specs/core.json @@ -1260,6 +1260,45 @@ } } }, + "/avatar/{userId}/{size}/dark": { + "parameters": [ + { + "name": "userId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "size", + "in": "path", + "required": true, + "schema": { + "type": "integer" + } + } + ], + "get": { + "operationId": "get-dark-avatar", + "tags": [ + "core" + ], + "responses": { + "200": { + "description": "", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + } + }, "/avatar/{userId}/{size}": { "parameters": [ {