diff options
author | Richard Steinmetz <richard@steinmetz.cloud> | 2025-01-13 16:16:35 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-01-13 16:16:35 +0100 |
commit | a6bcaf7e7a51364dd56824b9d35cc8e2c9d3f621 (patch) | |
tree | 8d48ce55f12376c37cf49812441442cf0d6cf636 /lib/private | |
parent | 87d7bbf1ca5680af5da0ecbaa04c8ff92e377d08 (diff) | |
parent | 3dbdf3266c888e5a7809334b8b07ae64ab0314ef (diff) | |
download | nextcloud-server-a6bcaf7e7a51364dd56824b9d35cc8e2c9d3f621.tar.gz nextcloud-server-a6bcaf7e7a51364dd56824b9d35cc8e2c9d3f621.zip |
Merge pull request #50100 from nextcloud/feat/ocp/attendee-availability-api
feat(ocp): add calendar api to retrieve availability of attendees
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/Calendar/AvailabilityResult.php | 28 | ||||
-rw-r--r-- | lib/private/Calendar/Manager.php | 93 |
2 files changed, 121 insertions, 0 deletions
diff --git a/lib/private/Calendar/AvailabilityResult.php b/lib/private/Calendar/AvailabilityResult.php new file mode 100644 index 00000000000..8031758f64e --- /dev/null +++ b/lib/private/Calendar/AvailabilityResult.php @@ -0,0 +1,28 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Calendar; + +use OCP\Calendar\IAvailabilityResult; + +class AvailabilityResult implements IAvailabilityResult { + public function __construct( + private readonly string $attendee, + private readonly bool $available, + ) { + } + + public function getAttendeeEmail(): string { + return $this->attendee; + } + + public function isAvailable(): bool { + return $this->available; + } +} diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index 3469193a364..e86e0e1d410 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -8,7 +8,10 @@ declare(strict_types=1); */ namespace OC\Calendar; +use DateTimeInterface; use OC\AppFramework\Bootstrap\Coordinator; +use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin; +use OCA\DAV\ServerFactory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Calendar\Exceptions\CalendarException; use OCP\Calendar\ICalendar; @@ -20,11 +23,16 @@ use OCP\Calendar\ICalendarQuery; use OCP\Calendar\ICreateFromString; use OCP\Calendar\IHandleImipMessage; use OCP\Calendar\IManager; +use OCP\IUser; +use OCP\IUserManager; use OCP\Security\ISecureRandom; use Psr\Container\ContainerInterface; use Psr\Log\LoggerInterface; +use Sabre\HTTP\Request; +use Sabre\HTTP\Response; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VEvent; +use Sabre\VObject\Component\VFreeBusy; use Sabre\VObject\Property\VCard\DateTime; use Sabre\VObject\Reader; use Throwable; @@ -48,6 +56,8 @@ class Manager implements IManager { private LoggerInterface $logger, private ITimeFactory $timeFactory, private ISecureRandom $random, + private IUserManager $userManager, + private ServerFactory $serverFactory, ) { } @@ -472,4 +482,87 @@ class Manager implements IManager { $uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC); return new CalendarEventBuilder($uid, $this->timeFactory); } + + public function checkAvailability( + DateTimeInterface $start, + DateTimeInterface $end, + IUser $organizer, + array $attendees, + ): array { + $organizerMailto = 'mailto:' . $organizer->getEMailAddress(); + $request = new VCalendar(); + $request->METHOD = 'REQUEST'; + $request->add('VFREEBUSY', [ + 'DTSTART' => $start, + 'DTEND' => $end, + 'ORGANIZER' => $organizerMailto, + 'ATTENDEE' => $organizerMailto, + ]); + + $mailtoLen = strlen('mailto:'); + foreach ($attendees as $attendee) { + if (str_starts_with($attendee, 'mailto:')) { + $attendee = substr($attendee, $mailtoLen); + } + + $attendeeUsers = $this->userManager->getByEmail($attendee); + if ($attendeeUsers === []) { + continue; + } + + $request->VFREEBUSY->add('ATTENDEE', "mailto:$attendee"); + } + + $organizerUid = $organizer->getUID(); + $server = $this->serverFactory->createAttendeeAvailabilityServer(); + /** @var CustomPrincipalPlugin $plugin */ + $plugin = $server->getPlugin('auth'); + $plugin->setCurrentPrincipal("principals/users/$organizerUid"); + + $request = new Request( + 'POST', + "/calendars/$organizerUid/outbox/", + [ + 'Content-Type' => 'text/calendar', + 'Depth' => 0, + ], + $request->serialize(), + ); + $response = new Response(); + $server->invokeMethod($request, $response, false); + + $xmlService = new \Sabre\Xml\Service(); + $xmlService->elementMap = [ + '{urn:ietf:params:xml:ns:caldav}response' => 'Sabre\Xml\Deserializer\keyValue', + '{urn:ietf:params:xml:ns:caldav}recipient' => 'Sabre\Xml\Deserializer\keyValue', + ]; + $parsedResponse = $xmlService->parse($response->getBodyAsString()); + + $result = []; + foreach ($parsedResponse as $freeBusyResponse) { + $freeBusyResponse = $freeBusyResponse['value']; + if ($freeBusyResponse['{urn:ietf:params:xml:ns:caldav}request-status'] !== '2.0;Success') { + continue; + } + + $freeBusyResponseData = \Sabre\VObject\Reader::read( + $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}calendar-data'] + ); + + $attendee = substr( + $freeBusyResponse['{urn:ietf:params:xml:ns:caldav}recipient']['{DAV:}href'], + $mailtoLen, + ); + + $vFreeBusy = $freeBusyResponseData->VFREEBUSY; + if (!($vFreeBusy instanceof VFreeBusy)) { + continue; + } + + // TODO: actually check values of FREEBUSY properties to find a free slot + $result[] = new AvailabilityResult($attendee, $vFreeBusy->isFree($start, $end)); + } + + return $result; + } } |