aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/dav/appinfo/routes.php1
-rw-r--r--apps/dav/composer/composer/autoload_classmap.php2
-rw-r--r--apps/dav/composer/composer/autoload_static.php2
-rw-r--r--apps/dav/lib/Controller/OutOfOfficeController.php78
-rw-r--r--apps/dav/lib/Db/Absence.php28
-rw-r--r--apps/dav/lib/ResponseDefinitions.php40
-rw-r--r--apps/dav/lib/Service/AbsenceService.php37
-rw-r--r--apps/dav/openapi.json134
-rw-r--r--lib/composer/composer/autoload_classmap.php7
-rw-r--r--lib/composer/composer/autoload_static.php7
-rw-r--r--lib/private/User/AvailabilityCoordinator.php111
-rw-r--r--lib/private/User/OutOfOfficeData.php63
-rw-r--r--lib/public/User/Events/OutOfOfficeChangedEvent.php50
-rw-r--r--lib/public/User/Events/OutOfOfficeClearedEvent.php50
-rw-r--r--lib/public/User/Events/OutOfOfficeScheduledEvent.php50
-rw-r--r--lib/public/User/IAvailabilityCoordinator.php42
-rw-r--r--lib/public/User/IOutOfOfficeData.php77
-rw-r--r--tests/lib/User/AvailabilityCoordinatorTest.php164
18 files changed, 942 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": []
diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php
index 1a55209c286..9757095dc04 100644
--- a/lib/composer/composer/autoload_classmap.php
+++ b/lib/composer/composer/autoload_classmap.php
@@ -731,6 +731,9 @@ return array(
'OCP\\User\\Events\\BeforeUserLoggedInEvent' => $baseDir . '/lib/public/User/Events/BeforeUserLoggedInEvent.php',
'OCP\\User\\Events\\BeforeUserLoggedInWithCookieEvent' => $baseDir . '/lib/public/User/Events/BeforeUserLoggedInWithCookieEvent.php',
'OCP\\User\\Events\\BeforeUserLoggedOutEvent' => $baseDir . '/lib/public/User/Events/BeforeUserLoggedOutEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeChangedEvent' => $baseDir . '/lib/public/User/Events/OutOfOfficeChangedEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeClearedEvent' => $baseDir . '/lib/public/User/Events/OutOfOfficeClearedEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeScheduledEvent' => $baseDir . '/lib/public/User/Events/OutOfOfficeScheduledEvent.php',
'OCP\\User\\Events\\PasswordUpdatedEvent' => $baseDir . '/lib/public/User/Events/PasswordUpdatedEvent.php',
'OCP\\User\\Events\\PostLoginEvent' => $baseDir . '/lib/public/User/Events/PostLoginEvent.php',
'OCP\\User\\Events\\UserChangedEvent' => $baseDir . '/lib/public/User/Events/UserChangedEvent.php',
@@ -742,6 +745,8 @@ return array(
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => $baseDir . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
'OCP\\User\\Events\\UserLoggedOutEvent' => $baseDir . '/lib/public/User/Events/UserLoggedOutEvent.php',
'OCP\\User\\GetQuotaEvent' => $baseDir . '/lib/public/User/GetQuotaEvent.php',
+ 'OCP\\User\\IAvailabilityCoordinator' => $baseDir . '/lib/public/User/IAvailabilityCoordinator.php',
+ 'OCP\\User\\IOutOfOfficeData' => $baseDir . '/lib/public/User/IOutOfOfficeData.php',
'OCP\\Util' => $baseDir . '/lib/public/Util.php',
'OCP\\WorkflowEngine\\EntityContext\\IContextPortation' => $baseDir . '/lib/public/WorkflowEngine/EntityContext/IContextPortation.php',
'OCP\\WorkflowEngine\\EntityContext\\IDisplayName' => $baseDir . '/lib/public/WorkflowEngine/EntityContext/IDisplayName.php',
@@ -1762,6 +1767,7 @@ return array(
'OC\\Updater\\VersionCheck' => $baseDir . '/lib/private/Updater/VersionCheck.php',
'OC\\UserStatus\\ISettableProvider' => $baseDir . '/lib/private/UserStatus/ISettableProvider.php',
'OC\\UserStatus\\Manager' => $baseDir . '/lib/private/UserStatus/Manager.php',
+ 'OC\\User\\AvailabilityCoordinator' => $baseDir . '/lib/private/User/AvailabilityCoordinator.php',
'OC\\User\\Backend' => $baseDir . '/lib/private/User/Backend.php',
'OC\\User\\Database' => $baseDir . '/lib/private/User/Database.php',
'OC\\User\\DisplayNameCache' => $baseDir . '/lib/private/User/DisplayNameCache.php',
@@ -1771,6 +1777,7 @@ return array(
'OC\\User\\LoginException' => $baseDir . '/lib/private/User/LoginException.php',
'OC\\User\\Manager' => $baseDir . '/lib/private/User/Manager.php',
'OC\\User\\NoUserException' => $baseDir . '/lib/private/User/NoUserException.php',
+ 'OC\\User\\OutOfOfficeData' => $baseDir . '/lib/private/User/OutOfOfficeData.php',
'OC\\User\\Session' => $baseDir . '/lib/private/User/Session.php',
'OC\\User\\User' => $baseDir . '/lib/private/User/User.php',
'OC_API' => $baseDir . '/lib/private/legacy/OC_API.php',
diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php
index bebf5be91bd..a0492513f8c 100644
--- a/lib/composer/composer/autoload_static.php
+++ b/lib/composer/composer/autoload_static.php
@@ -764,6 +764,9 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\User\\Events\\BeforeUserLoggedInEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/BeforeUserLoggedInEvent.php',
'OCP\\User\\Events\\BeforeUserLoggedInWithCookieEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/BeforeUserLoggedInWithCookieEvent.php',
'OCP\\User\\Events\\BeforeUserLoggedOutEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/BeforeUserLoggedOutEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeChangedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/OutOfOfficeChangedEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeClearedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/OutOfOfficeClearedEvent.php',
+ 'OCP\\User\\Events\\OutOfOfficeScheduledEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/OutOfOfficeScheduledEvent.php',
'OCP\\User\\Events\\PasswordUpdatedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/PasswordUpdatedEvent.php',
'OCP\\User\\Events\\PostLoginEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/PostLoginEvent.php',
'OCP\\User\\Events\\UserChangedEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserChangedEvent.php',
@@ -775,6 +778,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OCP\\User\\Events\\UserLoggedInWithCookieEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedInWithCookieEvent.php',
'OCP\\User\\Events\\UserLoggedOutEvent' => __DIR__ . '/../../..' . '/lib/public/User/Events/UserLoggedOutEvent.php',
'OCP\\User\\GetQuotaEvent' => __DIR__ . '/../../..' . '/lib/public/User/GetQuotaEvent.php',
+ 'OCP\\User\\IAvailabilityCoordinator' => __DIR__ . '/../../..' . '/lib/public/User/IAvailabilityCoordinator.php',
+ 'OCP\\User\\IOutOfOfficeData' => __DIR__ . '/../../..' . '/lib/public/User/IOutOfOfficeData.php',
'OCP\\Util' => __DIR__ . '/../../..' . '/lib/public/Util.php',
'OCP\\WorkflowEngine\\EntityContext\\IContextPortation' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/EntityContext/IContextPortation.php',
'OCP\\WorkflowEngine\\EntityContext\\IDisplayName' => __DIR__ . '/../../..' . '/lib/public/WorkflowEngine/EntityContext/IDisplayName.php',
@@ -1795,6 +1800,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\Updater\\VersionCheck' => __DIR__ . '/../../..' . '/lib/private/Updater/VersionCheck.php',
'OC\\UserStatus\\ISettableProvider' => __DIR__ . '/../../..' . '/lib/private/UserStatus/ISettableProvider.php',
'OC\\UserStatus\\Manager' => __DIR__ . '/../../..' . '/lib/private/UserStatus/Manager.php',
+ 'OC\\User\\AvailabilityCoordinator' => __DIR__ . '/../../..' . '/lib/private/User/AvailabilityCoordinator.php',
'OC\\User\\Backend' => __DIR__ . '/../../..' . '/lib/private/User/Backend.php',
'OC\\User\\Database' => __DIR__ . '/../../..' . '/lib/private/User/Database.php',
'OC\\User\\DisplayNameCache' => __DIR__ . '/../../..' . '/lib/private/User/DisplayNameCache.php',
@@ -1804,6 +1810,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
'OC\\User\\LoginException' => __DIR__ . '/../../..' . '/lib/private/User/LoginException.php',
'OC\\User\\Manager' => __DIR__ . '/../../..' . '/lib/private/User/Manager.php',
'OC\\User\\NoUserException' => __DIR__ . '/../../..' . '/lib/private/User/NoUserException.php',
+ 'OC\\User\\OutOfOfficeData' => __DIR__ . '/../../..' . '/lib/private/User/OutOfOfficeData.php',
'OC\\User\\Session' => __DIR__ . '/../../..' . '/lib/private/User/Session.php',
'OC\\User\\User' => __DIR__ . '/../../..' . '/lib/private/User/User.php',
'OC_API' => __DIR__ . '/../../..' . '/lib/private/legacy/OC_API.php',
diff --git a/lib/private/User/AvailabilityCoordinator.php b/lib/private/User/AvailabilityCoordinator.php
new file mode 100644
index 00000000000..fe0db92fd0f
--- /dev/null
+++ b/lib/private/User/AvailabilityCoordinator.php
@@ -0,0 +1,111 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ * @author Richard Steinmetz <richard@steinmetz.cloud>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\User;
+
+use JsonException;
+use OCA\DAV\Db\AbsenceMapper;
+use OCP\AppFramework\Db\DoesNotExistException;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IUser;
+use OCP\User\IAvailabilityCoordinator;
+use OCP\User\IOutOfOfficeData;
+use Psr\Log\LoggerInterface;
+
+class AvailabilityCoordinator implements IAvailabilityCoordinator {
+ private ICache $cache;
+
+ public function __construct(
+ ICacheFactory $cacheFactory,
+ private AbsenceMapper $absenceMapper,
+ private LoggerInterface $logger,
+ ) {
+ $this->cache = $cacheFactory->createLocal('OutOfOfficeData');
+ }
+
+ private function getCachedOutOfOfficeData(IUser $user): ?OutOfOfficeData {
+ $cachedString = $this->cache->get($user->getUID());
+ if ($cachedString === null) {
+ return null;
+ }
+
+ try {
+ $cachedData = json_decode($cachedString, true, 10, JSON_THROW_ON_ERROR);
+ } catch (JsonException $e) {
+ $this->logger->error('Failed to deserialize cached out-of-office data: ' . $e->getMessage(), [
+ 'exception' => $e,
+ 'json' => $cachedString,
+ ]);
+ return null;
+ }
+
+ return new OutOfOfficeData(
+ $cachedData['id'],
+ $user,
+ $cachedData['startDate'],
+ $cachedData['endDate'],
+ $cachedData['shortMessage'],
+ $cachedData['message'],
+ );
+ }
+
+ private function setCachedOutOfOfficeData(IOutOfOfficeData $data): void {
+ try {
+ $cachedString = json_encode([
+ 'id' => $data->getId(),
+ 'startDate' => $data->getStartDate(),
+ 'endDate' => $data->getEndDate(),
+ 'shortMessage' => $data->getShortMessage(),
+ 'message' => $data->getMessage(),
+ ], JSON_THROW_ON_ERROR);
+ } catch (JsonException $e) {
+ $this->logger->error('Failed to serialize out-of-office data: ' . $e->getMessage(), [
+ 'exception' => $e,
+ ]);
+ return;
+ }
+
+ $this->cache->set($data->getUser()->getUID(), $cachedString, 300);
+ }
+
+ public function getCurrentOutOfOfficeData(IUser $user): ?IOutOfOfficeData {
+ $cachedData = $this->getCachedOutOfOfficeData($user);
+ if ($cachedData !== null) {
+ return $cachedData;
+ }
+
+ try {
+ $absenceData = $this->absenceMapper->findByUserId($user->getUID());
+ } catch (DoesNotExistException $e) {
+ return null;
+ }
+
+ $data = $absenceData->toOutOufOfficeData($user);
+ $this->setCachedOutOfOfficeData($data);
+ return $data;
+ }
+}
diff --git a/lib/private/User/OutOfOfficeData.php b/lib/private/User/OutOfOfficeData.php
new file mode 100644
index 00000000000..12b7e03a0ae
--- /dev/null
+++ b/lib/private/User/OutOfOfficeData.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OC\User;
+
+use OCP\IUser;
+use OCP\User\IOutOfOfficeData;
+
+class OutOfOfficeData implements IOutOfOfficeData {
+ public function __construct(private string $id,
+ private IUser $user,
+ private int $startDate,
+ private int $endDate,
+ private string $shortMessage,
+ private string $message) {
+ }
+
+ public function getId(): string {
+ return $this->id;
+ }
+
+ public function getUser(): IUser {
+ return $this->user;
+ }
+
+ public function getStartDate(): int {
+ return $this->startDate;
+ }
+
+ public function getEndDate(): int {
+ return $this->endDate;
+ }
+
+ public function getShortMessage(): string {
+ return $this->shortMessage;
+ }
+
+ public function getMessage(): string {
+ return $this->message;
+ }
+}
diff --git a/lib/public/User/Events/OutOfOfficeChangedEvent.php b/lib/public/User/Events/OutOfOfficeChangedEvent.php
new file mode 100644
index 00000000000..5e5753b7202
--- /dev/null
+++ b/lib/public/User/Events/OutOfOfficeChangedEvent.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\User\Events;
+
+use OCP\EventDispatcher\Event;
+use OCP\User\IOutOfOfficeData;
+
+/**
+ * Emitted when a user's out-of-office period has changed
+ *
+ * @since 28.0.0
+ */
+class OutOfOfficeChangedEvent extends Event {
+ /**
+ * @since 28.0.0
+ */
+ public function __construct(private IOutOfOfficeData $data) {
+ parent::__construct();
+ }
+
+ /**
+ * @since 28.0.0
+ */
+ public function getData(): IOutOfOfficeData {
+ return $this->data;
+ }
+}
diff --git a/lib/public/User/Events/OutOfOfficeClearedEvent.php b/lib/public/User/Events/OutOfOfficeClearedEvent.php
new file mode 100644
index 00000000000..48a77c77023
--- /dev/null
+++ b/lib/public/User/Events/OutOfOfficeClearedEvent.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\User\Events;
+
+use OCP\EventDispatcher\Event;
+use OCP\User\IOutOfOfficeData;
+
+/**
+ * Emitted when a user's out-of-office period is cleared
+ *
+ * @since 28.0.0
+ */
+class OutOfOfficeClearedEvent extends Event {
+ /**
+ * @since 28.0.0
+ */
+ public function __construct(private IOutOfOfficeData $data) {
+ parent::__construct();
+ }
+
+ /**
+ * @since 28.0.0
+ */
+ public function getData(): IOutOfOfficeData {
+ return $this->data;
+ }
+}
diff --git a/lib/public/User/Events/OutOfOfficeScheduledEvent.php b/lib/public/User/Events/OutOfOfficeScheduledEvent.php
new file mode 100644
index 00000000000..2bcbec63478
--- /dev/null
+++ b/lib/public/User/Events/OutOfOfficeScheduledEvent.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\User\Events;
+
+use OCP\EventDispatcher\Event;
+use OCP\User\IOutOfOfficeData;
+
+/**
+ * Emitted when a user's out-of-office period is scheduled
+ *
+ * @since 28.0.0
+ */
+class OutOfOfficeScheduledEvent extends Event {
+ /**
+ * @since 28.0.0
+ */
+ public function __construct(private IOutOfOfficeData $data) {
+ parent::__construct();
+ }
+
+ /**
+ * @since 28.0.0
+ */
+ public function getData(): IOutOfOfficeData {
+ return $this->data;
+ }
+}
diff --git a/lib/public/User/IAvailabilityCoordinator.php b/lib/public/User/IAvailabilityCoordinator.php
new file mode 100644
index 00000000000..113e3491714
--- /dev/null
+++ b/lib/public/User/IAvailabilityCoordinator.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\User;
+
+use OCP\IUser;
+
+/**
+ * Coordinator for availability and out-of-office messages
+ *
+ * @since 28.0.0
+ */
+interface IAvailabilityCoordinator {
+ /**
+ * Get the user's out-of-office message, if any
+ *
+ * @since 28.0.0
+ */
+ public function getCurrentOutOfOfficeData(IUser $user): ?IOutOfOfficeData;
+}
diff --git a/lib/public/User/IOutOfOfficeData.php b/lib/public/User/IOutOfOfficeData.php
new file mode 100644
index 00000000000..03444449d58
--- /dev/null
+++ b/lib/public/User/IOutOfOfficeData.php
@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * @copyright 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @author 2023 Christoph Wurst <christoph@winzerhof-wurst.at>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ */
+
+namespace OCP\User;
+
+use OCP\IUser;
+
+/**
+ * DTO to hold out-of-office information of a user
+ *
+ * @since 28.0.0
+ */
+interface IOutOfOfficeData {
+ /**
+ * Get the unique token assigned to the current out-of-office event
+ *
+ * @since 28.0.0
+ */
+ public function getId(): string;
+
+ /**
+ * @since 28.0.0
+ */
+ public function getUser(): IUser;
+
+ /**
+ * Get the accurate out-of-office start date
+ *
+ * This event is not guaranteed to be emitted exactly at start date
+ *
+ * @since 28.0.0
+ */
+ public function getStartDate(): int;
+
+ /**
+ * Get the (preliminary) out-of-office end date
+ *
+ * @since 28.0.0
+ */
+ public function getEndDate(): int;
+
+ /**
+ * Get the short summary text displayed in the user status and similar
+ *
+ * @since 28.0.0
+ */
+ public function getShortMessage(): string;
+
+ /**
+ * Get the long out-of-office message for auto responders and similar
+ *
+ * @since 28.0.0
+ */
+ public function getMessage(): string;
+}
diff --git a/tests/lib/User/AvailabilityCoordinatorTest.php b/tests/lib/User/AvailabilityCoordinatorTest.php
new file mode 100644
index 00000000000..8e847f7e5d5
--- /dev/null
+++ b/tests/lib/User/AvailabilityCoordinatorTest.php
@@ -0,0 +1,164 @@
+<?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 Test\User;
+
+use OC\User\AvailabilityCoordinator;
+use OC\User\OutOfOfficeData;
+use OCA\DAV\Db\Absence;
+use OCA\DAV\Db\AbsenceMapper;
+use OCP\ICache;
+use OCP\ICacheFactory;
+use OCP\IUser;
+use Psr\Log\LoggerInterface;
+use Test\TestCase;
+
+class AvailabilityCoordinatorTest extends TestCase {
+ private AvailabilityCoordinator $availabilityCoordinator;
+ private ICacheFactory $cacheFactory;
+ private ICache $cache;
+ private AbsenceMapper $absenceMapper;
+ private LoggerInterface $logger;
+
+ protected function setUp(): void {
+ parent::setUp();
+
+ $this->cacheFactory = $this->createMock(ICacheFactory::class);
+ $this->cache = $this->createMock(ICache::class);
+ $this->absenceMapper = $this->createMock(AbsenceMapper::class);
+ $this->logger = $this->createMock(LoggerInterface::class);
+
+ $this->cacheFactory->expects(self::once())
+ ->method('createLocal')
+ ->willReturn($this->cache);
+
+ $this->availabilityCoordinator = new AvailabilityCoordinator(
+ $this->cacheFactory,
+ $this->absenceMapper,
+ $this->logger,
+ );
+ }
+
+ public function testGetOutOfOfficeData(): void {
+ $absence = new Absence();
+ $absence->setId(420);
+ $absence->setUserId('user');
+ $absence->setFirstDay('2023-10-01');
+ $absence->setLastDay('2023-10-08');
+ $absence->setStatus('Vacation');
+ $absence->setMessage('On vacation');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->cache->expects(self::once())
+ ->method('get')
+ ->with('user')
+ ->willReturn(null);
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->cache->expects(self::once())
+ ->method('set')
+ ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}', 300);
+
+ $expected = new OutOfOfficeData(
+ '420',
+ $user,
+ 1696118400,
+ 1696723200,
+ 'Vacation',
+ 'On vacation',
+ );
+ $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user);
+ self::assertEquals($expected, $actual);
+ }
+
+ public function testGetOutOfOfficeDataWithCachedData(): void {
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->cache->expects(self::once())
+ ->method('get')
+ ->with('user')
+ ->willReturn('{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}');
+ $this->absenceMapper->expects(self::never())
+ ->method('findByUserId');
+ $this->cache->expects(self::never())
+ ->method('set');
+
+ $expected = new OutOfOfficeData(
+ '420',
+ $user,
+ 1696118400,
+ 1696723200,
+ 'Vacation',
+ 'On vacation',
+ );
+ $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user);
+ self::assertEquals($expected, $actual);
+ }
+
+ public function testGetOutOfOfficeDataWithInvalidCachedData(): void {
+ $absence = new Absence();
+ $absence->setId(420);
+ $absence->setUserId('user');
+ $absence->setFirstDay('2023-10-01');
+ $absence->setLastDay('2023-10-08');
+ $absence->setStatus('Vacation');
+ $absence->setMessage('On vacation');
+
+ $user = $this->createMock(IUser::class);
+ $user->method('getUID')
+ ->willReturn('user');
+
+ $this->cache->expects(self::once())
+ ->method('get')
+ ->with('user')
+ ->willReturn('{"id":"420",}');
+ $this->absenceMapper->expects(self::once())
+ ->method('findByUserId')
+ ->with('user')
+ ->willReturn($absence);
+ $this->cache->expects(self::once())
+ ->method('set')
+ ->with('user', '{"id":"420","startDate":1696118400,"endDate":1696723200,"shortMessage":"Vacation","message":"On vacation"}', 300);
+
+ $expected = new OutOfOfficeData(
+ '420',
+ $user,
+ 1696118400,
+ 1696723200,
+ 'Vacation',
+ 'On vacation',
+ );
+ $actual = $this->availabilityCoordinator->getCurrentOutOfOfficeData($user);
+ self::assertEquals($expected, $actual);
+ }
+}