]> git.mxchange.org Git - friendica-addons.git/blob - dav/common/dav_caldav_backend_common.inc.php
Heavily refactored, including multiple calendars per user and recurring events. Not...
[friendica-addons.git] / dav / common / dav_caldav_backend_common.inc.php
1 <?php
2
3 abstract class Sabre_CalDAV_Backend_Common extends Sabre_CalDAV_Backend_Abstract
4 {
5         /**
6          * @var array
7          */
8         protected $propertyMap = array(
9                 '{DAV:}displayname'                                   => 'displayname',
10                 '{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
11                 '{urn:ietf:params:xml:ns:caldav}calendar-timezone'    => 'timezone',
12                 '{http://apple.com/ns/ical/}calendar-order'           => 'calendarorder',
13                 '{http://apple.com/ns/ical/}calendar-color'           => 'calendarcolor',
14         );
15
16
17         /**
18          * @abstract
19          * @return int
20          */
21         abstract public function getNamespace();
22
23
24         /**
25          * @param int $calendarId
26          * @param string $sd
27          * @param string $ed
28          * @param string $base_path
29          * @return array
30          */
31         abstract public function listItemsByRange($calendarId, $sd, $ed, $base_path);
32
33
34         /**
35          * @var array
36          */
37         static private $calendarCache = array();
38
39         /**
40          * @var array
41          */
42         static private $calendarObjectCache = array();
43
44         /**
45          * @static
46          * @param int $calendarId
47          * @return array
48          */
49         static public function loadCalendarById($calendarId)
50         {
51                 if (!isset(self::$calendarCache[$calendarId])) {
52                         $c                                = q("SELECT * FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
53                         self::$calendarCache[$calendarId] = $c[0];
54                 }
55                 return self::$calendarCache[$calendarId];
56         }
57
58         /**
59          * @static
60          * @param int $obj_id
61          * @return array
62          */
63         static public function loadCalendarobjectById($obj_id)
64         {
65                 if (!isset(self::$calendarObjectCache[$obj_id])) {
66                         $o                                  = q("SELECT * FROM %s%scalendarobjects WHERE `id` = %d",
67                                 CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($obj_id)
68                         );
69                         self::$calendarObjectCache[$obj_id] = $o[0];
70                 }
71                 return self::$calendarObjectCache[$obj_id];
72         }
73
74
75         /**
76          * @static
77          * @param Sabre_VObject_Component_VEvent $component
78          * @return int
79          */
80         public static function getDtEndTimeStamp(&$component)
81         {
82                 /** @var Sabre_VObject_Property_DateTime $dtstart */
83                 $dtstart = $component->__get("DTSTART");
84                 if ($component->__get("DTEND")) {
85                         /** @var Sabre_VObject_Property_DateTime $dtend */
86                         $dtend = $component->__get("DTEND");
87                         return $dtend->getDateTime()->getTimeStamp();
88                 } elseif ($component->__get("DURATION")) {
89                         $endDate = clone $dtstart->getDateTime();
90                         $endDate->add(Sabre_VObject_DateTimeParser::parse($component->__get("DURATION")->value));
91                         return $endDate->getTimeStamp();
92                 } elseif ($dtstart->getDateType() === Sabre_VObject_Property_DateTime::DATE) {
93                         $endDate = clone $dtstart->getDateTime();
94                         $endDate->modify('+1 day');
95                         return $endDate->getTimeStamp();
96                 } else {
97                         return $dtstart->getDateTime()->getTimeStamp() + 3600;
98                 }
99
100         }
101
102
103         /**
104          * Parses some information from calendar objects, used for optimized
105          * calendar-queries.
106          *
107          * Returns an array with the following keys:
108          *   * etag
109          *   * size
110          *   * componentType
111          *   * firstOccurence
112          *   * lastOccurence
113          *
114          * @param string $calendarData
115          * @throws Sabre_DAV_Exception_BadRequest
116          * @return array
117          */
118         protected function getDenormalizedData($calendarData)
119         {
120                 /** @var Sabre_VObject_Component_VEvent $vObject */
121                 $vObject        = Sabre_VObject_Reader::read($calendarData);
122                 $componentType  = null;
123                 $component      = null;
124                 $firstOccurence = null;
125                 $lastOccurence  = null;
126                 foreach ($vObject->getComponents() as $component) {
127                         if ($component->name !== 'VTIMEZONE') {
128                                 $componentType = $component->name;
129                                 break;
130                         }
131                 }
132                 if (!$componentType) {
133                         throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
134                 }
135                 if ($componentType === 'VEVENT') {
136                         /** @var Sabre_VObject_Component_VEvent $component */
137                         /** @var Sabre_VObject_Property_DateTime $dtstart  */
138                         $dtstart        = $component->__get("DTSTART");
139                         $firstOccurence = $dtstart->getDateTime()->getTimeStamp();
140                         // Finding the last occurence is a bit harder
141                         if (!$component->__get("RRULE")) {
142                                 $lastOccurence = self::getDtEndTimeStamp($component);
143                         } else {
144                                 $it      = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->__get("UID"));
145                                 $maxDate = new DateTime(CALDAV_MAX_YEAR . "-01-01");
146                                 if ($it->isInfinite()) {
147                                         $lastOccurence = $maxDate->getTimeStamp();
148                                 } else {
149                                         $end = $it->getDtEnd();
150                                         while ($it->valid() && $end < $maxDate) {
151                                                 $end = $it->getDtEnd();
152                                                 $it->next();
153
154                                         }
155                                         $lastOccurence = $end->getTimeStamp();
156                                 }
157
158                         }
159                 }
160
161                 return array(
162                         'etag'           => md5($calendarData),
163                         'size'           => strlen($calendarData),
164                         'componentType'  => $componentType,
165                         'firstOccurence' => $firstOccurence,
166                         'lastOccurence'  => $lastOccurence,
167                 );
168
169         }
170
171         /**
172          * Updates properties for a calendar.
173          *
174          * The mutations array uses the propertyName in clark-notation as key,
175          * and the array value for the property value. In the case a property
176          * should be deleted, the property value will be null.
177          *
178          * This method must be atomic. If one property cannot be changed, the
179          * entire operation must fail.
180          *
181          * If the operation was successful, true can be returned.
182          * If the operation failed, false can be returned.
183          *
184          * Deletion of a non-existent property is always successful.
185          *
186          * Lastly, it is optional to return detailed information about any
187          * failures. In this case an array should be returned with the following
188          * structure:
189          *
190          * array(
191          *   403 => array(
192          *      '{DAV:}displayname' => null,
193          *   ),
194          *   424 => array(
195          *      '{DAV:}owner' => null,
196          *   )
197          * )
198          *
199          * In this example it was forbidden to update {DAV:}displayname.
200          * (403 Forbidden), which in turn also caused {DAV:}owner to fail
201          * (424 Failed Dependency) because the request needs to be atomic.
202          *
203          * @param mixed $calendarId
204          * @param array $mutations
205          * @return bool|array
206          */
207         public function updateCalendar($calendarId, array $mutations)
208         {
209
210                 $newValues = array();
211                 $result    = array(
212                         200 => array(), // Ok
213                         403 => array(), // Forbidden
214                         424 => array(), // Failed Dependency
215                 );
216
217                 $hasError = false;
218
219                 foreach ($mutations as $propertyName=> $propertyValue) {
220
221                         // We don't know about this property.
222                         if (!isset($this->propertyMap[$propertyName])) {
223                                 $hasError                   = true;
224                                 $result[403][$propertyName] = null;
225                                 unset($mutations[$propertyName]);
226                                 continue;
227                         }
228
229                         $fieldName             = $this->propertyMap[$propertyName];
230                         $newValues[$fieldName] = $propertyValue;
231
232                 }
233
234                 // If there were any errors we need to fail the request
235                 if ($hasError) {
236                         // Properties has the remaining properties
237                         foreach ($mutations as $propertyName=> $propertyValue) {
238                                 $result[424][$propertyName] = null;
239                         }
240
241                         // Removing unused statuscodes for cleanliness
242                         foreach ($result as $status=> $properties) {
243                                 if (is_array($properties) && count($properties) === 0) unset($result[$status]);
244                         }
245
246                         return $result;
247
248                 }
249
250                 $this->increaseCalendarCtag($calendarId);
251
252                 $valuesSql = array();
253                 foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
254                 if (count($valuesSql) > 0) {
255                         q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
256                 }
257
258                 return true;
259
260         }
261
262         /**
263          * @param int $calendarId
264          */
265         protected function increaseCalendarCtag($calendarId)
266         {
267                 q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
268                 self::$calendarObjectCache = array();
269         }
270
271         /**
272          * @abstract
273          * @param int $calendar_id
274          * @param int $calendarobject_id
275          * @return string
276          */
277         abstract function getItemDetailRedirect($calendar_id, $calendarobject_id);
278
279 }