CalendarEventBuilder.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OC\Calendar;
  8. use DateTimeInterface;
  9. use InvalidArgumentException;
  10. use OCP\AppFramework\Utility\ITimeFactory;
  11. use OCP\Calendar\ICalendarEventBuilder;
  12. use OCP\Calendar\ICreateFromString;
  13. use Sabre\VObject\Component\VCalendar;
  14. use Sabre\VObject\Component\VEvent;
  15. class CalendarEventBuilder implements ICalendarEventBuilder {
  16. private ?DateTimeInterface $startDate = null;
  17. private ?DateTimeInterface $endDate = null;
  18. private ?string $summary = null;
  19. private ?string $description = null;
  20. private ?string $location = null;
  21. private ?array $organizer = null;
  22. private array $attendees = [];
  23. public function __construct(
  24. private readonly string $uid,
  25. private readonly ITimeFactory $timeFactory,
  26. ) {
  27. }
  28. public function setStartDate(DateTimeInterface $start): ICalendarEventBuilder {
  29. $this->startDate = $start;
  30. return $this;
  31. }
  32. public function setEndDate(DateTimeInterface $end): ICalendarEventBuilder {
  33. $this->endDate = $end;
  34. return $this;
  35. }
  36. public function setSummary(string $summary): ICalendarEventBuilder {
  37. $this->summary = $summary;
  38. return $this;
  39. }
  40. public function setDescription(string $description): ICalendarEventBuilder {
  41. $this->description = $description;
  42. return $this;
  43. }
  44. public function setLocation(string $location): ICalendarEventBuilder {
  45. $this->location = $location;
  46. return $this;
  47. }
  48. public function setOrganizer(string $email, ?string $commonName = null): ICalendarEventBuilder {
  49. $this->organizer = [$email, $commonName];
  50. return $this;
  51. }
  52. public function addAttendee(string $email, ?string $commonName = null): ICalendarEventBuilder {
  53. $this->attendees[] = [$email, $commonName];
  54. return $this;
  55. }
  56. public function toIcs(): string {
  57. if ($this->startDate === null) {
  58. throw new InvalidArgumentException('Event is missing a start date');
  59. }
  60. if ($this->endDate === null) {
  61. throw new InvalidArgumentException('Event is missing an end date');
  62. }
  63. if ($this->summary === null) {
  64. throw new InvalidArgumentException('Event is missing a summary');
  65. }
  66. if ($this->organizer === null && $this->attendees !== []) {
  67. throw new InvalidArgumentException('Event has attendees but is missing an organizer');
  68. }
  69. $vcalendar = new VCalendar();
  70. $props = [
  71. 'UID' => $this->uid,
  72. 'DTSTAMP' => $this->timeFactory->now(),
  73. 'SUMMARY' => $this->summary,
  74. 'DTSTART' => $this->startDate,
  75. 'DTEND' => $this->endDate,
  76. ];
  77. if ($this->description !== null) {
  78. $props['DESCRIPTION'] = $this->description;
  79. }
  80. if ($this->location !== null) {
  81. $props['LOCATION'] = $this->location;
  82. }
  83. /** @var VEvent $vevent */
  84. $vevent = $vcalendar->add('VEVENT', $props);
  85. if ($this->organizer !== null) {
  86. self::addAttendeeToVEvent($vevent, 'ORGANIZER', $this->organizer);
  87. }
  88. foreach ($this->attendees as $attendee) {
  89. self::addAttendeeToVEvent($vevent, 'ATTENDEE', $attendee);
  90. }
  91. return $vcalendar->serialize();
  92. }
  93. public function createInCalendar(ICreateFromString $calendar): string {
  94. $fileName = $this->uid . '.ics';
  95. $calendar->createFromString($fileName, $this->toIcs());
  96. return $fileName;
  97. }
  98. /**
  99. * @param array{0: string, 1: ?string} $tuple A tuple of [$email, $commonName] where $commonName may be null.
  100. */
  101. private static function addAttendeeToVEvent(VEvent $vevent, string $name, array $tuple): void {
  102. [$email, $cn] = $tuple;
  103. if (!str_starts_with($email, 'mailto:')) {
  104. $email = "mailto:$email";
  105. }
  106. $params = [];
  107. if ($cn !== null) {
  108. $params['CN'] = $cn;
  109. }
  110. $vevent->add($name, $email, $params);
  111. }
  112. }