mirror of
https://github.com/nextcloud/server.git
synced 2025-02-25 09:20:16 +00:00
feat(ocp): calendar event builder api
Signed-off-by: Richard Steinmetz <richard@steinmetz.cloud>
This commit is contained in:
parent
92acfefb3f
commit
80bc1c2d6c
11 changed files with 485 additions and 27 deletions
.reuse
lib
composer/composer
private/Calendar
public/Calendar
tests
|
@ -334,3 +334,7 @@ License: CC0-1.0
|
|||
Files: apps/theming/fonts/OpenDyslexic-Bold.otf apps/theming/fonts/OpenDyslexic-Regular.otf
|
||||
Copyright: 2012-2019 Abbie Gonzalez <https://abbiecod.es|support@abbiecod.es>, with Reserved Font Name OpenDyslexic.
|
||||
License: OFL-1.1-RFN
|
||||
|
||||
Files: tests/data/ics/event-builder-complete.ics tests/data/ics/event-builder-without-attendees.ics
|
||||
Copyright: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
License: AGPL-3.0-or-later
|
||||
|
|
|
@ -192,6 +192,7 @@ return array(
|
|||
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => $baseDir . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
|
||||
'OCP\\Calendar\\Exceptions\\CalendarException' => $baseDir . '/lib/public/Calendar/Exceptions/CalendarException.php',
|
||||
'OCP\\Calendar\\ICalendar' => $baseDir . '/lib/public/Calendar/ICalendar.php',
|
||||
'OCP\\Calendar\\ICalendarEventBuilder' => $baseDir . '/lib/public/Calendar/ICalendarEventBuilder.php',
|
||||
'OCP\\Calendar\\ICalendarIsShared' => $baseDir . '/lib/public/Calendar/ICalendarIsShared.php',
|
||||
'OCP\\Calendar\\ICalendarIsWritable' => $baseDir . '/lib/public/Calendar/ICalendarIsWritable.php',
|
||||
'OCP\\Calendar\\ICalendarProvider' => $baseDir . '/lib/public/Calendar/ICalendarProvider.php',
|
||||
|
@ -1116,6 +1117,7 @@ return array(
|
|||
'OC\\Broadcast\\Events\\BroadcastEvent' => $baseDir . '/lib/private/Broadcast/Events/BroadcastEvent.php',
|
||||
'OC\\Cache\\CappedMemoryCache' => $baseDir . '/lib/private/Cache/CappedMemoryCache.php',
|
||||
'OC\\Cache\\File' => $baseDir . '/lib/private/Cache/File.php',
|
||||
'OC\\Calendar\\CalendarEventBuilder' => $baseDir . '/lib/private/Calendar/CalendarEventBuilder.php',
|
||||
'OC\\Calendar\\CalendarQuery' => $baseDir . '/lib/private/Calendar/CalendarQuery.php',
|
||||
'OC\\Calendar\\Manager' => $baseDir . '/lib/private/Calendar/Manager.php',
|
||||
'OC\\Calendar\\Resource\\Manager' => $baseDir . '/lib/private/Calendar/Resource/Manager.php',
|
||||
|
|
|
@ -241,6 +241,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OCP\\Calendar\\BackendTemporarilyUnavailableException' => __DIR__ . '/../../..' . '/lib/public/Calendar/BackendTemporarilyUnavailableException.php',
|
||||
'OCP\\Calendar\\Exceptions\\CalendarException' => __DIR__ . '/../../..' . '/lib/public/Calendar/Exceptions/CalendarException.php',
|
||||
'OCP\\Calendar\\ICalendar' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendar.php',
|
||||
'OCP\\Calendar\\ICalendarEventBuilder' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarEventBuilder.php',
|
||||
'OCP\\Calendar\\ICalendarIsShared' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsShared.php',
|
||||
'OCP\\Calendar\\ICalendarIsWritable' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarIsWritable.php',
|
||||
'OCP\\Calendar\\ICalendarProvider' => __DIR__ . '/../../..' . '/lib/public/Calendar/ICalendarProvider.php',
|
||||
|
@ -1165,6 +1166,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
|
|||
'OC\\Broadcast\\Events\\BroadcastEvent' => __DIR__ . '/../../..' . '/lib/private/Broadcast/Events/BroadcastEvent.php',
|
||||
'OC\\Cache\\CappedMemoryCache' => __DIR__ . '/../../..' . '/lib/private/Cache/CappedMemoryCache.php',
|
||||
'OC\\Cache\\File' => __DIR__ . '/../../..' . '/lib/private/Cache/File.php',
|
||||
'OC\\Calendar\\CalendarEventBuilder' => __DIR__ . '/../../..' . '/lib/private/Calendar/CalendarEventBuilder.php',
|
||||
'OC\\Calendar\\CalendarQuery' => __DIR__ . '/../../..' . '/lib/private/Calendar/CalendarQuery.php',
|
||||
'OC\\Calendar\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Manager.php',
|
||||
'OC\\Calendar\\Resource\\Manager' => __DIR__ . '/../../..' . '/lib/private/Calendar/Resource/Manager.php',
|
||||
|
|
132
lib/private/Calendar/CalendarEventBuilder.php
Normal file
132
lib/private/Calendar/CalendarEventBuilder.php
Normal file
|
@ -0,0 +1,132 @@
|
|||
<?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 DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Calendar\ICalendarEventBuilder;
|
||||
use OCP\Calendar\ICreateFromString;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
use Sabre\VObject\Component\VEvent;
|
||||
|
||||
class CalendarEventBuilder implements ICalendarEventBuilder {
|
||||
private ?DateTimeInterface $startDate = null;
|
||||
private ?DateTimeInterface $endDate = null;
|
||||
private ?string $summary = null;
|
||||
private ?string $description = null;
|
||||
private ?string $location = null;
|
||||
private ?array $organizer = null;
|
||||
private array $attendees = [];
|
||||
|
||||
public function __construct(
|
||||
private readonly string $uid,
|
||||
private readonly ITimeFactory $timeFactory,
|
||||
) {
|
||||
}
|
||||
|
||||
public function setStartDate(DateTimeInterface $start): ICalendarEventBuilder {
|
||||
$this->startDate = $start;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEndDate(DateTimeInterface $end): ICalendarEventBuilder {
|
||||
$this->endDate = $end;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSummary(string $summary): ICalendarEventBuilder {
|
||||
$this->summary = $summary;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): ICalendarEventBuilder {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLocation(string $location): ICalendarEventBuilder {
|
||||
$this->location = $location;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOrganizer(string $email, ?string $commonName = null): ICalendarEventBuilder {
|
||||
$this->organizer = [$email, $commonName];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addAttendee(string $email, ?string $commonName = null): ICalendarEventBuilder {
|
||||
$this->attendees[] = [$email, $commonName];
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function toIcs(): string {
|
||||
if ($this->startDate === null) {
|
||||
throw new InvalidArgumentException('Event is missing a start date');
|
||||
}
|
||||
|
||||
if ($this->endDate === null) {
|
||||
throw new InvalidArgumentException('Event is missing an end date');
|
||||
}
|
||||
|
||||
if ($this->summary === null) {
|
||||
throw new InvalidArgumentException('Event is missing a summary');
|
||||
}
|
||||
|
||||
if ($this->organizer === null && $this->attendees !== []) {
|
||||
throw new InvalidArgumentException('Event has attendees but is missing an organizer');
|
||||
}
|
||||
|
||||
$vcalendar = new VCalendar();
|
||||
$props = [
|
||||
'UID' => $this->uid,
|
||||
'DTSTAMP' => $this->timeFactory->now(),
|
||||
'SUMMARY' => $this->summary,
|
||||
'DTSTART' => $this->startDate,
|
||||
'DTEND' => $this->endDate,
|
||||
];
|
||||
if ($this->description !== null) {
|
||||
$props['DESCRIPTION'] = $this->description;
|
||||
}
|
||||
if ($this->location !== null) {
|
||||
$props['LOCATION'] = $this->location;
|
||||
}
|
||||
/** @var VEvent $vevent */
|
||||
$vevent = $vcalendar->add('VEVENT', $props);
|
||||
if ($this->organizer !== null) {
|
||||
self::addAttendeeToVEvent($vevent, 'ORGANIZER', $this->organizer);
|
||||
}
|
||||
foreach ($this->attendees as $attendee) {
|
||||
self::addAttendeeToVEvent($vevent, 'ATTENDEE', $attendee);
|
||||
}
|
||||
return $vcalendar->serialize();
|
||||
}
|
||||
|
||||
public function createInCalendar(ICreateFromString $calendar): string {
|
||||
$fileName = $this->uid . '.ics';
|
||||
$calendar->createFromString($fileName, $this->toIcs());
|
||||
return $fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array{0: string, 1: ?string} $tuple A tuple of [$email, $commonName] where $commonName may be null.
|
||||
*/
|
||||
private static function addAttendeeToVEvent(VEvent $vevent, string $name, array $tuple): void {
|
||||
[$email, $cn] = $tuple;
|
||||
if (!str_starts_with($email, 'mailto:')) {
|
||||
$email = "mailto:$email";
|
||||
}
|
||||
$params = [];
|
||||
if ($cn !== null) {
|
||||
$params['CN'] = $cn;
|
||||
}
|
||||
$vevent->add($name, $email, $params);
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ use OC\AppFramework\Bootstrap\Coordinator;
|
|||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Calendar\Exceptions\CalendarException;
|
||||
use OCP\Calendar\ICalendar;
|
||||
use OCP\Calendar\ICalendarEventBuilder;
|
||||
use OCP\Calendar\ICalendarIsShared;
|
||||
use OCP\Calendar\ICalendarIsWritable;
|
||||
use OCP\Calendar\ICalendarProvider;
|
||||
|
@ -19,6 +20,7 @@ use OCP\Calendar\ICalendarQuery;
|
|||
use OCP\Calendar\ICreateFromString;
|
||||
use OCP\Calendar\IHandleImipMessage;
|
||||
use OCP\Calendar\IManager;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Sabre\VObject\Component\VCalendar;
|
||||
|
@ -45,6 +47,7 @@ class Manager implements IManager {
|
|||
private ContainerInterface $container,
|
||||
private LoggerInterface $logger,
|
||||
private ITimeFactory $timeFactory,
|
||||
private ISecureRandom $random,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -216,21 +219,21 @@ class Manager implements IManager {
|
|||
string $recipient,
|
||||
string $calendarData,
|
||||
): bool {
|
||||
|
||||
|
||||
$userCalendars = $this->getCalendarsForPrincipal($principalUri);
|
||||
if (empty($userCalendars)) {
|
||||
$this->logger->warning('iMip message could not be processed because user has no calendars');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** @var VCalendar $vObject|null */
|
||||
$calendarObject = Reader::read($calendarData);
|
||||
|
||||
|
||||
if (!isset($calendarObject->METHOD) || $calendarObject->METHOD->getValue() !== 'REQUEST') {
|
||||
$this->logger->warning('iMip message contains an incorrect or invalid method');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!isset($calendarObject->VEVENT)) {
|
||||
$this->logger->warning('iMip message contains no event');
|
||||
return false;
|
||||
|
@ -242,12 +245,12 @@ class Manager implements IManager {
|
|||
$this->logger->warning('iMip message event dose not contains a UID');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (!isset($eventObject->ATTENDEE)) {
|
||||
$this->logger->warning('iMip message event dose not contains any attendees');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
foreach ($eventObject->ATTENDEE as $entry) {
|
||||
$address = trim(str_replace('mailto:', '', $entry->getValue()));
|
||||
if ($address === $recipient) {
|
||||
|
@ -259,17 +262,17 @@ class Manager implements IManager {
|
|||
$this->logger->warning('iMip message event does not contain a attendee that matches the recipient');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
foreach ($userCalendars as $calendar) {
|
||||
|
||||
|
||||
if (!$calendar instanceof ICalendarIsWritable && !$calendar instanceof ICalendarIsShared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ($calendar->isDeleted() || !$calendar->isWritable() || $calendar->isShared()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (!empty($calendar->search($recipient, ['ATTENDEE'], ['uid' => $eventObject->UID->getValue()]))) {
|
||||
try {
|
||||
if ($calendar instanceof IHandleImipMessage) {
|
||||
|
@ -282,7 +285,7 @@ class Manager implements IManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$this->logger->warning('iMip message event could not be processed because the no corresponding event was found in any calendar');
|
||||
return false;
|
||||
}
|
||||
|
@ -464,4 +467,9 @@ class Manager implements IManager {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function createEventBuilder(): ICalendarEventBuilder {
|
||||
$uid = $this->random->generate(32, ISecureRandom::CHAR_ALPHANUMERIC);
|
||||
return new CalendarEventBuilder($uid, $this->timeFactory);
|
||||
}
|
||||
}
|
||||
|
|
106
lib/public/Calendar/ICalendarEventBuilder.php
Normal file
106
lib/public/Calendar/ICalendarEventBuilder.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace OCP\Calendar;
|
||||
|
||||
use DateTimeInterface;
|
||||
use InvalidArgumentException;
|
||||
use OCP\Calendar\Exceptions\CalendarException;
|
||||
|
||||
/**
|
||||
* The calendar event builder can be used to conveniently build a calendar event and then serialize
|
||||
* it to a ICS string. The ICS string can be submitted to calendar instances implementing the
|
||||
* \OCP\Calendar\ICreateFromString interface.
|
||||
*
|
||||
* All setters return self to allow chaining method calls.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
interface ICalendarEventBuilder {
|
||||
/**
|
||||
* Set the start date, time and time zone.
|
||||
* This property is required!
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setStartDate(DateTimeInterface $start): self;
|
||||
|
||||
/**
|
||||
* Set the end date, time and time zone.
|
||||
* This property is required!
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setEndDate(DateTimeInterface $end): self;
|
||||
|
||||
/**
|
||||
* Set the event summary or title.
|
||||
* This property is required!
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setSummary(string $summary): self;
|
||||
|
||||
/**
|
||||
* Set the event description.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setDescription(string $description): self;
|
||||
|
||||
/**
|
||||
* Set the event location. It can either be a physical address or a URL.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setLocation(string $location): self;
|
||||
|
||||
/**
|
||||
* Set the event organizer.
|
||||
* This property is required if attendees are added!
|
||||
*
|
||||
* The "mailto:" prefix is optional and will be added automatically if it is missing.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function setOrganizer(string $email, ?string $commonName = null): self;
|
||||
|
||||
/**
|
||||
* Add a new attendee to the event.
|
||||
* Adding at least one attendee requires also setting the organizer!
|
||||
*
|
||||
* The "mailto:" prefix is optional and will be added automatically if it is missing.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function addAttendee(string $email, ?string $commonName = null): self;
|
||||
|
||||
/**
|
||||
* Serialize the built event to an ICS string if all required properties set.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*
|
||||
* @return string The serialized ICS string
|
||||
*
|
||||
* @throws InvalidArgumentException If required properties were not set
|
||||
*/
|
||||
public function toIcs(): string;
|
||||
|
||||
/**
|
||||
* Create the event in the given calendar.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*
|
||||
* @return string The filename of the created event
|
||||
*
|
||||
* @throws InvalidArgumentException If required properties were not set
|
||||
* @throws CalendarException If writing the event to the calendar fails
|
||||
*/
|
||||
public function createInCalendar(ICreateFromString $calendar): string;
|
||||
}
|
|
@ -157,4 +157,12 @@ interface IManager {
|
|||
* @since 25.0.0
|
||||
*/
|
||||
public function handleIMipCancel(string $principalUri, string $sender, ?string $replyTo, string $recipient, string $calendarData): bool;
|
||||
|
||||
/**
|
||||
* Create a new event builder instance. Please have a look at its documentation and the
|
||||
* \OCP\Calendar\ICreateFromString interface on how to use it.
|
||||
*
|
||||
* @since 31.0.0
|
||||
*/
|
||||
public function createEventBuilder(): ICalendarEventBuilder;
|
||||
}
|
||||
|
|
16
tests/data/ics/event-builder-complete.ics
Normal file
16
tests/data/ics/event-builder-complete.ics
Normal file
|
@ -0,0 +1,16 @@
|
|||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Sabre//Sabre VObject 4.5.6//EN
|
||||
CALSCALE:GREGORIAN
|
||||
BEGIN:VEVENT
|
||||
UID:event-uid-123
|
||||
DTSTAMP:20250105T000000Z
|
||||
SUMMARY:My event
|
||||
DTSTART:20250105T170958Z
|
||||
DTEND:20250105T171958Z
|
||||
DESCRIPTION:Foo bar baz
|
||||
ORGANIZER:mailto:organizer@domain.tld
|
||||
ATTENDEE:mailto:attendee1@domain.tld
|
||||
ATTENDEE:mailto:attendee2@domain.tld
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
13
tests/data/ics/event-builder-without-attendees.ics
Normal file
13
tests/data/ics/event-builder-without-attendees.ics
Normal file
|
@ -0,0 +1,13 @@
|
|||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//Sabre//Sabre VObject 4.5.6//EN
|
||||
CALSCALE:GREGORIAN
|
||||
BEGIN:VEVENT
|
||||
UID:event-uid-123
|
||||
DTSTAMP:20250105T000000Z
|
||||
SUMMARY:My event
|
||||
DTSTART:20250105T170958Z
|
||||
DTEND:20250105T171958Z
|
||||
DESCRIPTION:Foo bar baz
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
146
tests/lib/Calendar/CalendarEventBuilderTest.php
Normal file
146
tests/lib/Calendar/CalendarEventBuilderTest.php
Normal file
|
@ -0,0 +1,146 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace Test\Calendar;
|
||||
|
||||
use DateTimeImmutable;
|
||||
use InvalidArgumentException;
|
||||
use OC\Calendar\CalendarEventBuilder;
|
||||
use OCP\AppFramework\Utility\ITimeFactory;
|
||||
use OCP\Calendar\ICreateFromString;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Test\TestCase;
|
||||
|
||||
class CalendarEventBuilderTest extends TestCase {
|
||||
private CalendarEventBuilder $calendarEventBuilder;
|
||||
private ITimeFactory&MockObject $timeFactory;
|
||||
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->timeFactory = $this->createMock(ITimeFactory::class);
|
||||
$this->timeFactory->method('now')
|
||||
->willReturn(new DateTimeImmutable('20250105T000000Z'));
|
||||
|
||||
$this->calendarEventBuilder = new CalendarEventBuilder(
|
||||
'event-uid-123',
|
||||
$this->timeFactory,
|
||||
);
|
||||
}
|
||||
|
||||
public function testToIcs(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('mailto:organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
|
||||
$actual = $this->calendarEventBuilder->toIcs();
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testToIcsWithoutOrganizerAndAttendees(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
|
||||
$expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-without-attendees.ics');
|
||||
$actual = $this->calendarEventBuilder->toIcs();
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testToIcsWithoutMailtoPrefix(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee2@domain.tld');
|
||||
|
||||
$expected = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
|
||||
$actual = $this->calendarEventBuilder->toIcs();
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
public function testCreateInCalendar(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$expectedIcs = file_get_contents(\OC::$SERVERROOT . '/tests/data/ics/event-builder-complete.ics');
|
||||
$calendar = $this->createMock(ICreateFromString::class);
|
||||
$calendar->expects(self::once())
|
||||
->method('createFromString')
|
||||
->with('event-uid-123.ics', $expectedIcs);
|
||||
|
||||
$actual = $this->calendarEventBuilder->createInCalendar($calendar);
|
||||
$this->assertEquals('event-uid-123.ics', $actual);
|
||||
}
|
||||
|
||||
public function testToIcsWithoutStartDate(): void {
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches('/start date/i');
|
||||
$this->calendarEventBuilder->toIcs();
|
||||
}
|
||||
|
||||
public function testToIcsWithoutEndDate(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches('/end date/i');
|
||||
$this->calendarEventBuilder->toIcs();
|
||||
}
|
||||
|
||||
public function testToIcsWithoutSummary(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->setOrganizer('organizer@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches('/summary/i');
|
||||
$this->calendarEventBuilder->toIcs();
|
||||
}
|
||||
|
||||
public function testToIcsWithoutOrganizerWithAttendees(): void {
|
||||
$this->calendarEventBuilder->setStartDate(new DateTimeImmutable('2025-01-05T17:09:58Z'));
|
||||
$this->calendarEventBuilder->setEndDate(new DateTimeImmutable('2025-01-05T17:19:58Z'));
|
||||
$this->calendarEventBuilder->setSummary('My event');
|
||||
$this->calendarEventBuilder->setDescription('Foo bar baz');
|
||||
$this->calendarEventBuilder->addAttendee('attendee1@domain.tld');
|
||||
$this->calendarEventBuilder->addAttendee('mailto:attendee2@domain.tld');
|
||||
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessageMatches('/organizer/i');
|
||||
$this->calendarEventBuilder->toIcs();
|
||||
}
|
||||
}
|
|
@ -14,6 +14,7 @@ use OCP\Calendar\ICalendarIsShared;
|
|||
use OCP\Calendar\ICalendarIsWritable;
|
||||
use OCP\Calendar\ICreateFromString;
|
||||
use OCP\Calendar\IHandleImipMessage;
|
||||
use OCP\Security\ISecureRandom;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
@ -44,6 +45,9 @@ class ManagerTest extends TestCase {
|
|||
/** @var ITimeFactory&MockObject */
|
||||
private $time;
|
||||
|
||||
/** @var ISecureRandom&MockObject */
|
||||
private ISecureRandom $secureRandom;
|
||||
|
||||
private VCalendar $vCalendar1a;
|
||||
|
||||
protected function setUp(): void {
|
||||
|
@ -53,12 +57,14 @@ class ManagerTest extends TestCase {
|
|||
$this->container = $this->createMock(ContainerInterface::class);
|
||||
$this->logger = $this->createMock(LoggerInterface::class);
|
||||
$this->time = $this->createMock(ITimeFactory::class);
|
||||
$this->secureRandom = $this->createMock(ISecureRandom::class);
|
||||
|
||||
$this->manager = new Manager(
|
||||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
);
|
||||
|
||||
// construct calendar with a 1 hour event and same start/end time zones
|
||||
|
@ -260,7 +266,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -291,7 +298,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -321,7 +329,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -352,7 +361,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -384,7 +394,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -416,7 +427,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -448,7 +460,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -491,7 +504,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -534,7 +548,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->onlyMethods(['getCalendarsForPrincipal'])
|
||||
->getMock();
|
||||
|
@ -612,7 +627,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -643,7 +659,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -680,7 +697,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -767,7 +785,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -800,7 +819,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -837,7 +857,8 @@ class ManagerTest extends TestCase {
|
|||
$this->coordinator,
|
||||
$this->container,
|
||||
$this->logger,
|
||||
$this->time
|
||||
$this->time,
|
||||
$this->secureRandom,
|
||||
])
|
||||
->setMethods([
|
||||
'getCalendarsForPrincipal'
|
||||
|
@ -866,7 +887,7 @@ class ManagerTest extends TestCase {
|
|||
$result = $manager->handleIMipCancel($principalUri, $sender, $replyTo, $recipient, $calendarData->serialize());
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
|
||||
private function getVCalendarReply(): Document {
|
||||
$data = <<<EOF
|
||||
BEGIN:VCALENDAR
|
||||
|
|
Loading…
Reference in a new issue