Skip to content

Commit 1f3ef66

Browse files
committed
feat(dav): dispatch out-of-office started and ended events
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
1 parent d14d881 commit 1f3ef66

File tree

11 files changed

+428
-34
lines changed

11 files changed

+428
-34
lines changed

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => $baseDir . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
1919
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => $baseDir . '/../lib/BackgroundJob/EventReminderJob.php',
2020
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
21+
'OCA\\DAV\\BackgroundJob\\OutOfOfficeEventDispatcherJob' => $baseDir . '/../lib/BackgroundJob/OutOfOfficeEventDispatcherJob.php',
2122
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => $baseDir . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
2223
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => $baseDir . '/../lib/BackgroundJob/RefreshWebcalJob.php',
2324
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => $baseDir . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class ComposerStaticInitDAV
3333
'OCA\\DAV\\BackgroundJob\\CleanupInvitationTokenJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupInvitationTokenJob.php',
3434
'OCA\\DAV\\BackgroundJob\\EventReminderJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/EventReminderJob.php',
3535
'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php',
36+
'OCA\\DAV\\BackgroundJob\\OutOfOfficeEventDispatcherJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/OutOfOfficeEventDispatcherJob.php',
3637
'OCA\\DAV\\BackgroundJob\\PruneOutdatedSyncTokensJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/PruneOutdatedSyncTokensJob.php',
3738
'OCA\\DAV\\BackgroundJob\\RefreshWebcalJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/RefreshWebcalJob.php',
3839
'OCA\\DAV\\BackgroundJob\\RegisterRegenerateBirthdayCalendars' => __DIR__ . '/..' . '/../lib/BackgroundJob/RegisterRegenerateBirthdayCalendars.php',
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* @copyright Copyright (c) 2023 Richard Steinmetz <richard@steinmetz.cloud>
7+
*
8+
* @author Richard Steinmetz <richard@steinmetz.cloud>
9+
*
10+
* @license AGPL-3.0-or-later
11+
*
12+
* This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU General Public License as published by
14+
* the Free Software Foundation, either version 3 of the License, or
15+
* (at your option) any later version.
16+
*
17+
* This program is distributed in the hope that it will be useful,
18+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
19+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20+
* GNU General Public License for more details.
21+
*
22+
* You should have received a copy of the GNU General Public License
23+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24+
*
25+
*/
26+
27+
namespace OCA\DAV\BackgroundJob;
28+
29+
use OCA\DAV\Db\AbsenceMapper;
30+
use OCP\AppFramework\Db\DoesNotExistException;
31+
use OCP\AppFramework\Utility\ITimeFactory;
32+
use OCP\BackgroundJob\QueuedJob;
33+
use OCP\EventDispatcher\IEventDispatcher;
34+
use OCP\IUserManager;
35+
use OCP\User\Events\OutOfOfficeEndedEvent;
36+
use OCP\User\Events\OutOfOfficeStartedEvent;
37+
use Psr\Log\LoggerInterface;
38+
39+
class OutOfOfficeEventDispatcherJob extends QueuedJob {
40+
public const EVENT_START = 'start';
41+
public const EVENT_END = 'end';
42+
43+
public function __construct(
44+
ITimeFactory $time,
45+
private AbsenceMapper $absenceMapper,
46+
private LoggerInterface $logger,
47+
private IEventDispatcher $eventDispatcher,
48+
private IUserManager $userManager,
49+
) {
50+
parent::__construct($time);
51+
}
52+
53+
public function run($argument) {
54+
$id = $argument['id'];
55+
$event = $argument['event'];
56+
57+
try {
58+
$absence = $this->absenceMapper->findById($id);
59+
} catch (DoesNotExistException | \OCP\DB\Exception $e) {
60+
$this->logger->error('Failed to dispatch out-of-office event: ' . $e->getMessage(), [
61+
'exception' => $e,
62+
'argument' => $argument,
63+
]);
64+
return;
65+
}
66+
67+
$userId = $absence->getUserId();
68+
$user = $this->userManager->get($userId);
69+
if ($user === null) {
70+
$this->logger->error("Failed to dispatch out-of-office event: User $userId does not exist", [
71+
'argument' => $argument,
72+
]);
73+
return;
74+
}
75+
76+
$data = $absence->toOutOufOfficeData($user);
77+
if ($event === self::EVENT_START) {
78+
$this->eventDispatcher->dispatchTyped(new OutOfOfficeStartedEvent($data));
79+
} elseif ($event === self::EVENT_END) {
80+
$this->eventDispatcher->dispatchTyped(new OutOfOfficeEndedEvent($data));
81+
} else {
82+
$this->logger->error("Invalid out-of-office event: $event", [
83+
'argument' => $argument,
84+
]);
85+
}
86+
}
87+
}

apps/dav/lib/Controller/AvailabilitySettingsController.php

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
use OCP\AppFramework\Http\JSONResponse;
3636
use OCP\AppFramework\Http\Response;
3737
use OCP\IRequest;
38+
use OCP\IUserSession;
3839

3940
class AvailabilitySettingsController extends Controller {
4041
public function __construct(
4142
IRequest $request,
42-
private ?string $userId,
43+
private ?IUserSession $userSession,
4344
private AbsenceService $absenceService,
4445
) {
4546
parent::__construct(Application::APP_ID, $request);
@@ -56,8 +57,8 @@ public function updateAbsence(
5657
string $status,
5758
string $message,
5859
): Response {
59-
$userId = $this->userId;
60-
if ($userId === null) {
60+
$user = $this->userSession->getUser();
61+
if ($user === null) {
6162
return new JSONResponse([], Http::STATUS_FORBIDDEN);
6263
}
6364

@@ -68,7 +69,7 @@ public function updateAbsence(
6869
}
6970

7071
$absence = $this->absenceService->createOrUpdateAbsence(
71-
$userId,
72+
$user,
7273
$firstDay,
7374
$lastDay,
7475
$status,
@@ -82,12 +83,12 @@ public function updateAbsence(
8283
*/
8384
#[NoAdminRequired]
8485
public function clearAbsence(): Response {
85-
$userId = $this->userId;
86-
if ($userId === null) {
86+
$user = $this->userSession->getUser();
87+
if ($user === null) {
8788
return new JSONResponse([], Http::STATUS_FORBIDDEN);
8889
}
8990

90-
$this->absenceService->clearAbsence($userId);
91+
$this->absenceService->clearAbsence($user);
9192
return new JSONResponse([]);
9293
}
9394

apps/dav/lib/Db/AbsenceMapper.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,31 @@ public function __construct(IDBConnection $db) {
4040
parent::__construct($db, 'dav_absence', Absence::class);
4141
}
4242

43+
/**
44+
* @throws DoesNotExistException
45+
* @throws \OCP\DB\Exception
46+
*/
47+
public function findById(int $id): Absence {
48+
$qb = $this->db->getQueryBuilder();
49+
$qb->select('*')
50+
->from($this->getTableName())
51+
->where($qb->expr()->eq(
52+
'id',
53+
$qb->createNamedParameter($id, IQueryBuilder::PARAM_INT),
54+
IQueryBuilder::PARAM_INT),
55+
);
56+
try {
57+
return $this->findEntity($qb);
58+
} catch (MultipleObjectsReturnedException $e) {
59+
// Won't happen as id is the primary key
60+
throw new \RuntimeException(
61+
'The impossible has happened! The query returned multiple absence settings for one user.',
62+
0,
63+
$e,
64+
);
65+
}
66+
}
67+
4368
/**
4469
* @throws DoesNotExistException
4570
* @throws \OCP\DB\Exception

apps/dav/lib/Service/AbsenceService.php

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@
2727
namespace OCA\DAV\Service;
2828

2929
use InvalidArgumentException;
30+
use OCA\DAV\BackgroundJob\OutOfOfficeEventDispatcherJob;
3031
use OCA\DAV\Db\Absence;
3132
use OCA\DAV\Db\AbsenceMapper;
3233
use OCP\AppFramework\Db\DoesNotExistException;
34+
use OCP\BackgroundJob\IJobList;
3335
use OCP\EventDispatcher\IEventDispatcher;
34-
use OCP\IUserManager;
36+
use OCP\IUser;
3537
use OCP\User\Events\OutOfOfficeChangedEvent;
3638
use OCP\User\Events\OutOfOfficeClearedEvent;
3739
use OCP\User\Events\OutOfOfficeScheduledEvent;
@@ -40,7 +42,7 @@ class AbsenceService {
4042
public function __construct(
4143
private AbsenceMapper $absenceMapper,
4244
private IEventDispatcher $eventDispatcher,
43-
private IUserManager $userManager,
45+
private IJobList $jobList,
4446
) {
4547
}
4648

@@ -52,60 +54,66 @@ public function __construct(
5254
* @throws InvalidArgumentException If no user with the given user id exists.
5355
*/
5456
public function createOrUpdateAbsence(
55-
string $userId,
57+
IUser $user,
5658
string $firstDay,
5759
string $lastDay,
5860
string $status,
5961
string $message,
6062
): Absence {
6163
try {
62-
$absence = $this->absenceMapper->findByUserId($userId);
64+
$absence = $this->absenceMapper->findByUserId($user->getUID());
6365
} catch (DoesNotExistException) {
6466
$absence = new Absence();
6567
}
6668

67-
$absence->setUserId($userId);
69+
$absence->setUserId($user->getUID());
6870
$absence->setFirstDay($firstDay);
6971
$absence->setLastDay($lastDay);
7072
$absence->setStatus($status);
7173
$absence->setMessage($message);
7274

73-
// TODO: this method should probably just take a IUser instance
74-
$user = $this->userManager->get($userId);
75-
if ($user === null) {
76-
throw new InvalidArgumentException("User $userId does not exist");
77-
}
78-
7975
if ($absence->getId() === null) {
80-
$persistedAbsence = $this->absenceMapper->insert($absence);
81-
$this->eventDispatcher->dispatchTyped(new OutOfOfficeScheduledEvent(
82-
$persistedAbsence->toOutOufOfficeData($user)
83-
));
84-
return $persistedAbsence;
76+
$absence = $this->absenceMapper->insert($absence);
77+
$eventData = $absence->toOutOufOfficeData($user);
78+
$this->eventDispatcher->dispatchTyped(new OutOfOfficeScheduledEvent($eventData));
79+
} else {
80+
$absence = $this->absenceMapper->update($absence);
81+
$eventData = $absence->toOutOufOfficeData($user);
82+
$this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent($eventData));
8583
}
8684

87-
$this->eventDispatcher->dispatchTyped(new OutOfOfficeChangedEvent(
88-
$absence->toOutOufOfficeData($user)
89-
));
90-
return $this->absenceMapper->update($absence);
85+
$this->jobList->scheduleAfter(
86+
OutOfOfficeEventDispatcherJob::class,
87+
$eventData->getStartDate(),
88+
[
89+
'id' => $absence->getId(),
90+
'event' => OutOfOfficeEventDispatcherJob::EVENT_START,
91+
],
92+
);
93+
$this->jobList->scheduleAfter(
94+
OutOfOfficeEventDispatcherJob::class,
95+
$eventData->getEndDate(),
96+
[
97+
'id' => $absence->getId(),
98+
'event' => OutOfOfficeEventDispatcherJob::EVENT_END,
99+
],
100+
);
101+
102+
return $absence;
91103
}
92104

93105
/**
94106
* @throws \OCP\DB\Exception
95107
*/
96-
public function clearAbsence(string $userId): void {
108+
public function clearAbsence(IUser $user): void {
97109
try {
98-
$absence = $this->absenceMapper->findByUserId($userId);
110+
$absence = $this->absenceMapper->findByUserId($user->getUID());
99111
} catch (DoesNotExistException $e) {
100112
// Nothing to clear
101113
return;
102114
}
103115
$this->absenceMapper->delete($absence);
104-
// TODO: this method should probably just take a IUser instance
105-
$user = $this->userManager->get($userId);
106-
if ($user === null) {
107-
throw new InvalidArgumentException("User $userId does not exist");
108-
}
116+
$this->jobList->remove(OutOfOfficeEventDispatcherJob::class);
109117
$eventData = $absence->toOutOufOfficeData($user);
110118
$this->eventDispatcher->dispatchTyped(new OutOfOfficeClearedEvent($eventData));
111119
}

0 commit comments

Comments
 (0)