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