]> git.mxchange.org Git - friendica-addons.git/blob - dav/common/dav_caldav_backend_common.inc.php
Adding and removing calendars
[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                 foreach ($vObject->getComponents() as $component) {
134                         if ($component->name !== 'VTIMEZONE') {
135                                 $componentType = $component->name;
136                                 break;
137                         }
138                 }
139                 if (!$componentType) {
140                         throw new Sabre_DAV_Exception_BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
141                 }
142                 if ($componentType === 'VEVENT') {
143                         /** @var Sabre_VObject_Component_VEvent $component */
144                         /** @var Sabre_VObject_Property_DateTime $dtstart  */
145                         $dtstart        = $component->__get("DTSTART");
146                         $firstOccurence = $dtstart->getDateTime()->getTimeStamp();
147                         // Finding the last occurence is a bit harder
148                         if (!$component->__get("RRULE")) {
149                                 $lastOccurence = self::getDtEndTimeStamp($component);
150                         } else {
151                                 $it      = new Sabre_VObject_RecurrenceIterator($vObject, (string)$component->__get("UID"));
152                                 $maxDate = new DateTime(CALDAV_MAX_YEAR . "-01-01");
153                                 if ($it->isInfinite()) {
154                                         $lastOccurence = $maxDate->getTimeStamp();
155                                 } else {
156                                         $end = $it->getDtEnd();
157                                         while ($it->valid() && $end < $maxDate) {
158                                                 $end = $it->getDtEnd();
159                                                 $it->next();
160
161                                         }
162                                         $lastOccurence = $end->getTimeStamp();
163                                 }
164
165                         }
166                 }
167
168                 return array(
169                         'etag'           => md5($calendarData),
170                         'size'           => strlen($calendarData),
171                         'componentType'  => $componentType,
172                         'firstOccurence' => $firstOccurence,
173                         'lastOccurence'  => $lastOccurence,
174                 );
175
176         }
177
178         /**
179          * Updates properties for a calendar.
180          *
181          * The mutations array uses the propertyName in clark-notation as key,
182          * and the array value for the property value. In the case a property
183          * should be deleted, the property value will be null.
184          *
185          * This method must be atomic. If one property cannot be changed, the
186          * entire operation must fail.
187          *
188          * If the operation was successful, true can be returned.
189          * If the operation failed, false can be returned.
190          *
191          * Deletion of a non-existent property is always successful.
192          *
193          * Lastly, it is optional to return detailed information about any
194          * failures. In this case an array should be returned with the following
195          * structure:
196          *
197          * array(
198          *   403 => array(
199          *      '{DAV:}displayname' => null,
200          *   ),
201          *   424 => array(
202          *      '{DAV:}owner' => null,
203          *   )
204          * )
205          *
206          * In this example it was forbidden to update {DAV:}displayname.
207          * (403 Forbidden), which in turn also caused {DAV:}owner to fail
208          * (424 Failed Dependency) because the request needs to be atomic.
209          *
210          * @param mixed $calendarId
211          * @param array $mutations
212          * @return bool|array
213          */
214         public function updateCalendar($calendarId, array $mutations)
215         {
216
217                 $newValues = array();
218                 $result    = array(
219                         200 => array(), // Ok
220                         403 => array(), // Forbidden
221                         424 => array(), // Failed Dependency
222                 );
223
224                 $hasError = false;
225
226                 foreach ($mutations as $propertyName=> $propertyValue) {
227
228                         // We don't know about this property.
229                         if (!isset($this->propertyMap[$propertyName])) {
230                                 $hasError                   = true;
231                                 $result[403][$propertyName] = null;
232                                 unset($mutations[$propertyName]);
233                                 continue;
234                         }
235
236                         $fieldName             = $this->propertyMap[$propertyName];
237                         $newValues[$fieldName] = $propertyValue;
238
239                 }
240
241                 // If there were any errors we need to fail the request
242                 if ($hasError) {
243                         // Properties has the remaining properties
244                         foreach ($mutations as $propertyName=> $propertyValue) {
245                                 $result[424][$propertyName] = null;
246                         }
247
248                         // Removing unused statuscodes for cleanliness
249                         foreach ($result as $status=> $properties) {
250                                 if (is_array($properties) && count($properties) === 0) unset($result[$status]);
251                         }
252
253                         return $result;
254
255                 }
256
257                 $this->increaseCalendarCtag($calendarId);
258
259                 $valuesSql = array();
260                 foreach ($newValues as $fieldName=> $value) $valuesSql[] = "`" . $fieldName . "` = '" . dbesc($value) . "'";
261                 if (count($valuesSql) > 0) {
262                         q("UPDATE %s%scalendars SET " . implode(", ", $valuesSql) . " WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
263                 }
264
265                 return true;
266
267         }
268
269         /**
270          * @param int $calendarId
271          */
272         protected function increaseCalendarCtag($calendarId)
273         {
274                 q("UPDATE %s%scalendars SET `ctag` = `ctag` + 1 WHERE `id` = '%d'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
275                 self::$calendarObjectCache = array();
276         }
277
278         /**
279          * @abstract
280          * @param int $calendar_id
281          * @param int $calendarobject_id
282          * @return string
283          */
284         abstract function getItemDetailRedirect($calendar_id, $calendarobject_id);
285
286 }