diff options
author | Christoph Wurst <christoph@winzerhof-wurst.at> | 2023-09-27 11:41:20 +0200 |
---|---|---|
committer | Richard Steinmetz <richard@steinmetz.cloud> | 2023-11-09 10:36:11 +0100 |
commit | ab1a1d688df76fb5c10b8bc431b928a2db1675bd (patch) | |
tree | 0147377303eccd1f6d22569fdf604d726ab8ca45 /apps/dav | |
parent | 1acc7c04684a05f024f4c83a8665d4732c2fc5f6 (diff) | |
download | nextcloud-server-ab1a1d688df76fb5c10b8bc431b928a2db1675bd.tar.gz nextcloud-server-ab1a1d688df76fb5c10b8bc431b928a2db1675bd.zip |
feat: Add out-of-office message API
[skipci]
Signed-off-by: Christoph Wurst <christoph@winzerhof-wurst.at>
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
Diffstat (limited to 'apps/dav')
-rw-r--r-- | apps/dav/appinfo/routes.php | 1 | ||||
-rw-r--r-- | apps/dav/composer/composer/autoload_classmap.php | 2 | ||||
-rw-r--r-- | apps/dav/composer/composer/autoload_static.php | 2 | ||||
-rw-r--r-- | apps/dav/lib/Controller/OutOfOfficeController.php | 78 | ||||
-rw-r--r-- | apps/dav/lib/Db/Absence.php | 28 | ||||
-rw-r--r-- | apps/dav/lib/ResponseDefinitions.php | 40 | ||||
-rw-r--r-- | apps/dav/lib/Service/AbsenceService.php | 37 | ||||
-rw-r--r-- | apps/dav/openapi.json | 134 |
8 files changed, 321 insertions, 1 deletions
diff --git a/apps/dav/appinfo/routes.php b/apps/dav/appinfo/routes.php index 3236c0642af..1b2fa0094bf 100644 --- a/apps/dav/appinfo/routes.php +++ b/apps/dav/appinfo/routes.php @@ -35,5 +35,6 @@ return [ ], 'ocs' => [ ['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'], + ['name' => 'out_of_office#getCurrentOutOfOfficeData', 'url' => '/api/v1/outOfOffice/{userId}', 'verb' => 'GET'], ], ]; diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index e0c3e20dc6b..bb424e47787 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -192,6 +192,7 @@ return array( 'OCA\\DAV\\Controller\\BirthdayCalendarController' => $baseDir . '/../lib/Controller/BirthdayCalendarController.php', 'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php', 'OCA\\DAV\\Controller\\InvitationResponseController' => $baseDir . '/../lib/Controller/InvitationResponseController.php', + 'OCA\\DAV\\Controller\\OutOfOfficeController' => $baseDir . '/../lib/Controller/OutOfOfficeController.php', 'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php', 'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php', 'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php', @@ -305,6 +306,7 @@ return array( 'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php', + 'OCA\\DAV\\ResponseDefinitions' => $baseDir . '/../lib/ResponseDefinitions.php', 'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php', 'OCA\\DAV\\Search\\ACalendarSearchProvider' => $baseDir . '/../lib/Search/ACalendarSearchProvider.php', 'OCA\\DAV\\Search\\ContactsSearchProvider' => $baseDir . '/../lib/Search/ContactsSearchProvider.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 9292731af98..ef040e79674 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -207,6 +207,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Controller\\BirthdayCalendarController' => __DIR__ . '/..' . '/../lib/Controller/BirthdayCalendarController.php', 'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php', 'OCA\\DAV\\Controller\\InvitationResponseController' => __DIR__ . '/..' . '/../lib/Controller/InvitationResponseController.php', + 'OCA\\DAV\\Controller\\OutOfOfficeController' => __DIR__ . '/..' . '/../lib/Controller/OutOfOfficeController.php', 'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php', 'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php', 'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php', @@ -320,6 +321,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php', 'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php', + 'OCA\\DAV\\ResponseDefinitions' => __DIR__ . '/..' . '/../lib/ResponseDefinitions.php', 'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php', 'OCA\\DAV\\Search\\ACalendarSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ACalendarSearchProvider.php', 'OCA\\DAV\\Search\\ContactsSearchProvider' => __DIR__ . '/..' . '/../lib/Search/ContactsSearchProvider.php', diff --git a/apps/dav/lib/Controller/OutOfOfficeController.php b/apps/dav/lib/Controller/OutOfOfficeController.php new file mode 100644 index 00000000000..fe4200ee1b5 --- /dev/null +++ b/apps/dav/lib/Controller/OutOfOfficeController.php @@ -0,0 +1,78 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> + * + * @author Richard Steinmetz <richard@steinmetz.cloud> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Controller; + +use OCA\DAV\Db\AbsenceMapper; +use OCA\DAV\ResponseDefinitions; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Http; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCSController; +use OCP\IRequest; + +/** + * @psalm-import-type DAVOutOfOfficeData from ResponseDefinitions + */ +class OutOfOfficeController extends OCSController { + + public function __construct( + string $appName, + IRequest $request, + private AbsenceMapper $absenceMapper, + ) { + parent::__construct($appName, $request); + } + + /** + * Get the currently configured out-of-office data of a user. + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $userId The user id to get out-of-office data for. + * @return DataResponse<Http::STATUS_OK|Http::STATUS_NOT_FOUND, ?DAVOutOfOfficeData, array{}> + * + * 200: Out-of-office data + * 404: No out-of-office data was found + */ + public function getCurrentOutOfOfficeData(string $userId): DataResponse { + try { + $data = $this->absenceMapper->findByUserId($userId); + } catch (DoesNotExistException) { + return new DataResponse(null, Http::STATUS_NOT_FOUND); + } + + return new DataResponse([ + 'id' => $data->getId(), + 'userId' => $data->getUserId(), + 'firstDay' => $data->getFirstDay(), + 'lastDay' => $data->getLastDay(), + 'status' => $data->getStatus(), + 'message' => $data->getMessage(), + ]); + } +} diff --git a/apps/dav/lib/Db/Absence.php b/apps/dav/lib/Db/Absence.php index f705b99ef30..e9ce1d2ea64 100644 --- a/apps/dav/lib/Db/Absence.php +++ b/apps/dav/lib/Db/Absence.php @@ -26,8 +26,13 @@ declare(strict_types=1); namespace OCA\DAV\Db; +use DateTimeImmutable; +use InvalidArgumentException; use JsonSerializable; +use OC\User\OutOfOfficeData; use OCP\AppFramework\Db\Entity; +use OCP\IUser; +use OCP\User\IOutOfOfficeData; /** * @method string getUserId() @@ -43,8 +48,13 @@ use OCP\AppFramework\Db\Entity; */ class Absence extends Entity implements JsonSerializable { protected string $userId = ''; + + /** Inclusive, formatted as YYYY-MM-DD */ protected string $firstDay = ''; + + /** Inclusive, formatted as YYYY-MM-DD */ protected string $lastDay = ''; + protected string $status = ''; protected string $message = ''; @@ -56,6 +66,24 @@ class Absence extends Entity implements JsonSerializable { $this->addType('message', 'string'); } + public function toOutOufOfficeData(IUser $user): IOutOfOfficeData { + if ($user->getUID() !== $this->getUserId()) { + throw new InvalidArgumentException("The user doesn't match the user id of this absence! Expected " . $this->getUserId() . ", got " . $user->getUID()); + } + + //$user = $userManager->get($this->getUserId()); + $startDate = new DateTimeImmutable($this->getFirstDay()); + $endDate = new DateTimeImmutable($this->getLastDay()); + return new OutOfOfficeData( + (string)$this->getId(), + $user, + $startDate->getTimestamp(), + $endDate->getTimestamp(), + $this->getStatus(), + $this->getMessage(), + ); + } + public function jsonSerialize(): array { return [ 'userId' => $this->userId, diff --git a/apps/dav/lib/ResponseDefinitions.php b/apps/dav/lib/ResponseDefinitions.php new file mode 100644 index 00000000000..97bd8e9efe9 --- /dev/null +++ b/apps/dav/lib/ResponseDefinitions.php @@ -0,0 +1,40 @@ +<?php + +declare(strict_types=1); + +/** + * @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud> + * + * @author Richard Steinmetz <richard@steinmetz.cloud> + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV; + +/** + * @psalm-type DAVOutOfOfficeData = array{ + * id: int, + * userId: string, + * firstDay: string, + * lastDay: string, + * status: string, + * message: string, + * } + */ +class ResponseDefinitions { +} diff --git a/apps/dav/lib/Service/AbsenceService.php b/apps/dav/lib/Service/AbsenceService.php index 228007b3af1..69dee1bd8cc 100644 --- a/apps/dav/lib/Service/AbsenceService.php +++ b/apps/dav/lib/Service/AbsenceService.php @@ -26,18 +26,30 @@ declare(strict_types=1); namespace OCA\DAV\Service; +use InvalidArgumentException; use OCA\DAV\Db\Absence; use OCA\DAV\Db\AbsenceMapper; use OCP\AppFramework\Db\DoesNotExistException; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IUserManager; +use OCP\User\Events\OutOfOfficeChangedEvent; +use OCP\User\Events\OutOfOfficeClearedEvent; +use OCP\User\Events\OutOfOfficeScheduledEvent; class AbsenceService { public function __construct( private AbsenceMapper $absenceMapper, + private IEventDispatcher $eventDispatcher, + private IUserManager $userManager, ) { } /** + * @param string $firstDay The first day (inclusive) of the absence formatted as YYYY-MM-DD. + * @param string $lastDay The last day (inclusive) of the absence formatted as YYYY-MM-DD. + * * @throws \OCP\DB\Exception + * @throws InvalidArgumentException If no user with the given user id exists. */ public function createOrUpdateAbsence( string $userId, @@ -58,9 +70,19 @@ class AbsenceService { $absence->setStatus($status); $absence->setMessage($message); + // TODO: this method should probably just take a IUser instance + $user = $this->userManager->get($userId); + if ($user === null) { + throw new InvalidArgumentException("User $userId does not exist"); + } + $eventData = $absence->toOutOufOfficeData($user); + if ($absence->getId() === null) { + $this->eventDispatcher->dispatchTyped(new OutOfOfficeScheduledEvent($eventData)); return $this->absenceMapper->insert($absence); } + + $this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent($eventData)); return $this->absenceMapper->update($absence); } @@ -68,7 +90,20 @@ class AbsenceService { * @throws \OCP\DB\Exception */ public function clearAbsence(string $userId): void { - $this->absenceMapper->deleteByUserId($userId); + try { + $absence = $this->absenceMapper->findByUserId($userId); + } catch (DoesNotExistException $e) { + // Nothing to clear + return; + } + $this->absenceMapper->delete($absence); + // TODO: this method should probably just take a IUser instance + $user = $this->userManager->get($userId); + if ($user === null) { + throw new InvalidArgumentException("User $userId does not exist"); + } + $eventData = $absence->toOutOufOfficeData($user); + $this->eventDispatcher->dispatchTyped(new OutOfOfficeClearedEvent($eventData)); } } diff --git a/apps/dav/openapi.json b/apps/dav/openapi.json index 09da0c19ff3..994d0cde11d 100644 --- a/apps/dav/openapi.json +++ b/apps/dav/openapi.json @@ -65,6 +65,38 @@ "type": "string" } } + }, + "OutOfOfficeData": { + "type": "object", + "required": [ + "id", + "userId", + "firstDay", + "lastDay", + "status", + "message" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "userId": { + "type": "string" + }, + "firstDay": { + "type": "string" + }, + "lastDay": { + "type": "string" + }, + "status": { + "type": "string" + }, + "message": { + "type": "string" + } + } } } }, @@ -186,6 +218,108 @@ } } } + }, + "/ocs/v2.php/apps/dav/api/v1/outOfOffice/{userId}": { + "get": { + "operationId": "out_of_office-get-current-out-of-office-data", + "summary": "Get the currently configured out-of-office data of a user.", + "tags": [ + "out_of_office" + ], + "security": [ + { + "bearer_auth": [] + }, + { + "basic_auth": [] + } + ], + "parameters": [ + { + "name": "userId", + "in": "path", + "description": "The user id to get out-of-office data for.", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "OCS-APIRequest", + "in": "header", + "description": "Required to be true for the API request to pass", + "required": true, + "schema": { + "type": "boolean", + "default": true + } + } + ], + "responses": { + "200": { + "description": "Out-of-office data", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/OutOfOfficeData", + "nullable": true + } + } + } + } + } + } + } + }, + "404": { + "description": "No out-of-office data was found", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": [ + "ocs" + ], + "properties": { + "ocs": { + "type": "object", + "required": [ + "meta", + "data" + ], + "properties": { + "meta": { + "$ref": "#/components/schemas/OCSMeta" + }, + "data": { + "$ref": "#/components/schemas/OutOfOfficeData", + "nullable": true + } + } + } + } + } + } + } + } + } + } } }, "tags": [] |