CalendarObject.php 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
  5. * SPDX-License-Identifier: AGPL-3.0-or-later
  6. */
  7. namespace OCA\DAV\CalDAV\AppCalendar;
  8. use OCP\Calendar\ICalendar;
  9. use OCP\Calendar\ICreateFromString;
  10. use OCP\Constants;
  11. use Sabre\CalDAV\ICalendarObject;
  12. use Sabre\DAV\Exception\Forbidden;
  13. use Sabre\DAV\Exception\NotFound;
  14. use Sabre\DAVACL\IACL;
  15. use Sabre\VObject\Component\VCalendar;
  16. use Sabre\VObject\Property\ICalendar\DateTime;
  17. class CalendarObject implements ICalendarObject, IACL {
  18. public function __construct(
  19. private AppCalendar $calendar,
  20. private ICalendar|ICreateFromString $backend,
  21. private VCalendar $vobject,
  22. ) {
  23. }
  24. public function getOwner() {
  25. return $this->calendar->getOwner();
  26. }
  27. public function getGroup() {
  28. return $this->calendar->getGroup();
  29. }
  30. public function getACL(): array {
  31. $acl = [
  32. [
  33. 'privilege' => '{DAV:}read',
  34. 'principal' => $this->getOwner(),
  35. 'protected' => true,
  36. ]
  37. ];
  38. if ($this->calendar->getPermissions() & Constants::PERMISSION_UPDATE) {
  39. $acl[] = [
  40. 'privilege' => '{DAV:}write-content',
  41. 'principal' => $this->getOwner(),
  42. 'protected' => true,
  43. ];
  44. }
  45. return $acl;
  46. }
  47. public function setACL(array $acl): void {
  48. throw new Forbidden('Setting ACL is not supported on this node');
  49. }
  50. public function getSupportedPrivilegeSet(): ?array {
  51. return null;
  52. }
  53. public function put($data): void {
  54. if ($this->backend instanceof ICreateFromString && $this->calendar->getPermissions() & Constants::PERMISSION_UPDATE) {
  55. if (is_resource($data)) {
  56. $data = stream_get_contents($data) ?: '';
  57. }
  58. $this->backend->createFromString($this->getName(), $data);
  59. } else {
  60. throw new Forbidden('This calendar-object is read-only');
  61. }
  62. }
  63. public function get(): string {
  64. return $this->vobject->serialize();
  65. }
  66. public function getContentType(): string {
  67. return 'text/calendar; charset=utf-8';
  68. }
  69. public function getETag(): ?string {
  70. return null;
  71. }
  72. public function getSize() {
  73. return mb_strlen($this->vobject->serialize());
  74. }
  75. public function delete(): void {
  76. if ($this->backend instanceof ICreateFromString && $this->calendar->getPermissions() & Constants::PERMISSION_DELETE) {
  77. /** @var \Sabre\VObject\Component[] */
  78. $components = $this->vobject->getBaseComponents();
  79. foreach ($components as $key => $component) {
  80. $components[$key]->STATUS = 'CANCELLED';
  81. $components[$key]->SEQUENCE = isset($component->SEQUENCE) ? ((int)$component->SEQUENCE->getValue()) + 1 : 1;
  82. if ($component->name === 'VEVENT') {
  83. $components[$key]->METHOD = 'CANCEL';
  84. }
  85. }
  86. $this->backend->createFromString($this->getName(), (new VCalendar($components))->serialize());
  87. } else {
  88. throw new Forbidden('This calendar-object is read-only');
  89. }
  90. }
  91. public function getName(): string {
  92. // Every object is required to have an UID
  93. $base = $this->vobject->getBaseComponent();
  94. // This should never happen except the app provides invalid calendars (VEvent, VTodo... all require UID to be present)
  95. if ($base === null) {
  96. throw new NotFound('Invalid node');
  97. }
  98. if (isset($base->{'X-FILENAME'})) {
  99. return (string)$base->{'X-FILENAME'};
  100. }
  101. return (string)$base->UID . '.ics';
  102. }
  103. public function setName($name): void {
  104. throw new Forbidden('This calendar-object is read-only');
  105. }
  106. public function getLastModified(): ?int {
  107. $base = $this->vobject->getBaseComponent();
  108. if ($base !== null && $this->vobject->getBaseComponent()->{'LAST-MODIFIED'}) {
  109. /** @var DateTime */
  110. $lastModified = $this->vobject->getBaseComponent()->{'LAST-MODIFIED'};
  111. return $lastModified->getDateTime()->getTimestamp();
  112. }
  113. return null;
  114. }
  115. }