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