]> git.mxchange.org Git - friendica-addons.git/blob - dav/common/dav_caldav_backend_private.inc.php
Merge branch 'master' of git://github.com/friendica/friendica-addons
[friendica-addons.git] / dav / common / dav_caldav_backend_private.inc.php
1 <?php
2
3 class Sabre_CalDAV_Backend_Private extends Sabre_CalDAV_Backend_Common
4 {
5
6
7         /**
8          * @var null|Sabre_CalDAV_Backend_Private
9          */
10         private static $instance = null;
11
12         /**
13          * @static
14          * @return Sabre_CalDAV_Backend_Private
15          */
16         public static function getInstance()
17         {
18                 if (self::$instance == null) {
19                         self::$instance = new Sabre_CalDAV_Backend_Private();
20                 }
21                 return self::$instance;
22         }
23
24
25         /**
26          * @return int
27          */
28         public function getNamespace()
29         {
30                 return CALDAV_NAMESPACE_PRIVATE;
31         }
32
33         /**
34          * @static
35          * @return string
36          */
37         public static function getBackendTypeName() {
38                 return t("Private Events");
39         }
40
41         /**
42          * @obsolete
43          * @param array $calendar
44          * @param int $user
45          * @return array
46          */
47         public function getPermissionsCalendar($calendar, $user)
48         {
49                 if ($calendar["namespace"] == CALDAV_NAMESPACE_PRIVATE && $user == $calendar["namespace_id"]) return array("read"=> true, "write"=> true);
50                 return array("read"=> false, "write"=> false);
51         }
52
53         /**
54          * @obsolete
55          * @param array $calendar
56          * @param int $user
57          * @param string $calendarobject_id
58          * @param null|array $item_arr
59          * @return array
60          */
61         public function getPermissionsItem($calendar, $user, $calendarobject_id, $item_arr = null)
62         {
63                 return $this->getPermissionsCalendar($calendar, $user);
64         }
65
66
67         /**
68          * @param array $row
69          * @param array $calendar
70          * @param string $base_path
71          * @return array
72          */
73         private function jqcal2wdcal($row, $calendar, $base_path)
74         {
75                 $not      = q("SELECT COUNT(*) num FROM %s%snotifications WHERE `calendar_id` = %d AND `calendarobject_id` = %d",
76                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($row["calendar_id"]), IntVal($row["calendarobject_id"])
77                 );
78                 $editable = $this->getPermissionsItem($calendar["namespace_id"], $row["calendarobject_id"], $row);
79
80                 $end = wdcal_mySql2PhpTime($row["EndTime"]);
81                 if ($row["IsAllDayEvent"]) $end -= 1;
82
83                 return array(
84                         "jq_id"             => $row["id"],
85                         "ev_id"             => $row["calendarobject_id"],
86                         "summary"           => escape_tags($row["Summary"]),
87                         "start"             => wdcal_mySql2PhpTime($row["StartTime"]),
88                         "end"               => $end,
89                         "is_allday"         => $row["IsAllDayEvent"],
90                         "is_moredays"       => 0,
91                         "is_recurring"      => $row["IsRecurring"],
92                         "color"             => (is_null($row["Color"]) || $row["Color"] == "" ? $calendar["calendarcolor"] : $row["Color"]),
93                         "is_editable"       => ($editable ? 1 : 0),
94                         "is_editable_quick" => ($editable && !$row["IsRecurring"] ? 1 : 0),
95                         "location"          => "Loc.",
96                         "attendees"         => '',
97                         "has_notification"  => ($not[0]["num"] > 0 ? 1 : 0),
98                         "url_detail"        => $base_path . $row["calendarobject_id"] . "/",
99                         "url_edit"          => $base_path . $row["calendarobject_id"] . "/edit/",
100                         "special_type"      => "",
101                 );
102         }
103
104         /**
105          * @param int $calendarId
106          * @param string $sd
107          * @param string $ed
108          * @param string $base_path
109          * @return array
110          */
111         public function listItemsByRange($calendarId, $sd, $ed, $base_path)
112         {
113                 $calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
114                 $von      = wdcal_php2MySqlTime($sd);
115                 $bis      = wdcal_php2MySqlTime($ed);
116                 $timezoneOffset = date("P");
117
118                 // @TODO Events, die früher angefangen haben, aber noch andauern
119                 $evs = q("SELECT *, CONVERT_TZ(`StartTime`, @@session.time_zone, '$timezoneOffset') StartTime, CONVERT_TZ(`EndTime`, @@session.time_zone, '$timezoneOffset') EndTime
120                         FROM %s%sjqcalendar WHERE `calendar_id` = %d AND `StartTime` between '%s' and '%s'",
121                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($von), dbesc($bis));
122
123                 $events = array();
124                 foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/");
125
126                 return $events;
127         }
128
129
130         /**
131          * @param int $calendar_id
132          * @param int $calendarobject_id
133          * @return string
134          */
135         public function getItemDetailRedirect($calendar_id, $calendarobject_id)
136         {
137                 return "/dav/wdcal/$calendar_id/$calendarobject_id/edit/";
138         }
139
140         /**
141          * Returns a list of calendars for a principal.
142          *
143          * Every project is an array with the following keys:
144          *  * id, a unique id that will be used by other functions to modify the
145          *    calendar. This can be the same as the uri or a database key.
146          *  * uri, which the basename of the uri with which the calendar is
147          *    accessed.
148          *  * principaluri. The owner of the calendar. Almost always the same as
149          *    principalUri passed to this method.
150          *
151          * Furthermore it can contain webdav properties in clark notation. A very
152          * common one is '{DAV:}displayname'.
153          *
154          * @param string $principalUri
155          * @throws DAVVersionMismatchException
156          * @return array
157          */
158         public function getCalendarsForUser($principalUri)
159         {
160                 $n = dav_compat_principal2namespace($principalUri);
161                 if ($n["namespace"] != $this->getNamespace()) return array();
162
163                 $cals = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, $this->getNamespace(), IntVal($n["namespace_id"]));
164                 $ret  = array();
165                 foreach ($cals as $cal) {
166                         if (!isset($cal["uri"])) throw new DAVVersionMismatchException();
167                         if (in_array($cal["uri"], $GLOBALS["CALDAV_PRIVATE_SYSTEM_CALENDARS"])) continue;
168
169                         $dat = array(
170                                 "id"                                                      => $cal["id"],
171                                 "uri"                                                     => $cal["uri"],
172                                 "principaluri"                                            => $principalUri,
173                                 '{' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}getctag' => $cal['ctag'] ? $cal['ctag'] : '0',
174                                 "calendar_class"                                          => "Sabre_CalDAV_Calendar",
175                         );
176                         foreach ($this->propertyMap as $key=> $field) $dat[$key] = $cal[$field];
177
178                         $ret[] = $dat;
179                 }
180
181                 return $ret;
182         }
183
184
185         /**
186          * Creates a new calendar for a principal.
187          *
188          * If the creation was a success, an id must be returned that can be used to reference
189          * this calendar in other methods, such as updateCalendar.
190          *
191          * @param string $principalUri
192          * @param string $calendarUri
193          * @param array $properties
194          * @throws Sabre_DAV_Exception
195          * @return string|void
196          */
197         public function createCalendar($principalUri, $calendarUri, array $properties)
198         {
199
200                 $uid = dav_compat_principal2uid($principalUri);
201
202                 $r = q("SELECT * FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, dbesc($calendarUri));
203                 if (count($r) > 0) throw new Sabre_DAV_Exception("A calendar with this URI already exists");
204
205                 $keys = array("`namespace`", "`namespace_id`", "`ctag`", "`uri`");
206                 $vals = array(CALDAV_NAMESPACE_PRIVATE, IntVal($uid), 1, "'" . dbesc($calendarUri) . "'");
207
208                 // Default value
209                 $sccs       = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set';
210                 $has_vevent = $has_vtodo = 1;
211                 if (isset($properties[$sccs])) {
212                         if (!($properties[$sccs] instanceof Sabre_CalDAV_Property_SupportedCalendarComponentSet)) {
213                                 throw new Sabre_DAV_Exception('The ' . $sccs . ' property must be of type: Sabre_CalDAV_Property_SupportedCalendarComponentSet');
214                         }
215                         $v          = $properties[$sccs]->getValue();
216                         $has_vevent = $has_vtodo = 0;
217                         foreach ($v as $w) {
218                                 if (mb_strtolower($w) == "vevent") $has_vevent = 1;
219                                 if (mb_strtolower($w) == "vtodo") $has_vtodo = 1;
220                         }
221                 }
222                 $keys[] = "`has_vevent`";
223                 $keys[] = "`has_vtodo`";
224                 $vals[] = $has_vevent;
225                 $vals[] = $has_vtodo;
226
227                 foreach ($this->propertyMap as $xmlName=> $dbName) {
228                         if (isset($properties[$xmlName])) {
229                                 $keys[] = "`$dbName`";
230                                 $vals[] = "'" . dbesc($properties[$xmlName]) . "'";
231                         }
232                 }
233
234                 $sql = sprintf("INSERT INTO %s%scalendars (" . implode(', ', $keys) . ") VALUES (" . implode(', ', $vals) . ")", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
235
236                 q($sql);
237
238                 $x = q("SELECT id FROM %s%scalendars WHERE `namespace` = %d AND `namespace_id` = %d AND `uri` = '%s'",
239                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, CALDAV_NAMESPACE_PRIVATE, $uid, $calendarUri
240                 );
241                 return $x[0]["id"];
242
243         }
244
245         /**
246          * Updates properties for a calendar.
247          *
248          * The mutations array uses the propertyName in clark-notation as key,
249          * and the array value for the property value. In the case a property
250          * should be deleted, the property value will be null.
251          *
252          * This method must be atomic. If one property cannot be changed, the
253          * entire operation must fail.
254          *
255          * If the operation was successful, true can be returned.
256          * If the operation failed, false can be returned.
257          *
258          * Deletion of a non-existent property is always successful.
259          *
260          * Lastly, it is optional to return detailed information about any
261          * failures. In this case an array should be returned with the following
262          * structure:
263          *
264          * array(
265          *   403 => array(
266          *      '{DAV:}displayname' => null,
267          *   ),
268          *   424 => array(
269          *      '{DAV:}owner' => null,
270          *   )
271          * )
272          *
273          * In this example it was forbidden to update {DAV:}displayname.
274          * (403 Forbidden), which in turn also caused {DAV:}owner to fail
275          * (424 Failed Dependency) because the request needs to be atomic.
276          *
277          * @param string $calendarId
278          * @param array $mutations
279          * @return bool|array
280          */
281         public function updateCalendar($calendarId, array $mutations)
282         {
283
284                 $newValues = array();
285                 $result    = array(
286                         200 => array(), // Ok
287                         403 => array(), // Forbidden
288                         424 => array(), // Failed Dependency
289                 );
290
291                 $hasError = false;
292
293                 foreach ($mutations as $propertyName=> $propertyValue) {
294
295                         // We don't know about this property.
296                         if (!isset($this->propertyMap[$propertyName])) {
297                                 $hasError                   = true;
298                                 $result[403][$propertyName] = null;
299                                 unset($mutations[$propertyName]);
300                                 continue;
301                         }
302
303                         $fieldName             = $this->propertyMap[$propertyName];
304                         $newValues[$fieldName] = $propertyValue;
305
306                 }
307
308                 // If there were any errors we need to fail the request
309                 if ($hasError) {
310                         // Properties has the remaining properties
311                         foreach ($mutations as $propertyName=> $propertyValue) {
312                                 $result[424][$propertyName] = null;
313                         }
314
315                         // Removing unused statuscodes for cleanliness
316                         foreach ($result as $status=> $properties) {
317                                 if (is_array($properties) && count($properties) === 0) unset($result[$status]);
318                         }
319
320                         return $result;
321
322                 }
323
324                 $sql = "`ctag` = `ctag` + 1";
325                 foreach ($newValues as $key=> $val) $sql .= ", `" . $key . "` = '" . dbesc($val) . "'";
326
327                 $sql = sprintf("UPDATE %s%scalendars SET $sql WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
328
329                 q($sql);
330
331                 return true;
332
333         }
334
335
336         /**
337          * Delete a calendar and all it's objects
338          *
339          * @param string $calendarId
340          * @return void
341          */
342         public function deleteCalendar($calendarId)
343         {
344                 q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
345                 q("DELETE FROM %s%scalendars WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
346
347         }
348
349
350         /**
351          * Returns all calendar objects within a calendar.
352          *
353          * Every item contains an array with the following keys:
354          *   * id - unique identifier which will be used for subsequent updates
355          *   * calendardata - The iCalendar-compatible calendar data
356          *   * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
357          *   * lastmodified - a timestamp of the last modification time
358          *   * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
359          *   '  "abcdef"')
360          *   * calendarid - The calendarid as it was passed to this function.
361          *   * size - The size of the calendar objects, in bytes.
362          *
363          * Note that the etag is optional, but it's highly encouraged to return for
364          * speed reasons.
365          *
366          * The calendardata is also optional. If it's not returned
367          * 'getCalendarObject' will be called later, which *is* expected to return
368          * calendardata.
369          *
370          * If neither etag or size are specified, the calendardata will be
371          * used/fetched to determine these numbers. If both are specified the
372          * amount of times this is needed is reduced by a great degree.
373          *
374          * @param mixed $calendarId
375          * @return array
376          */
377         function getCalendarObjects($calendarId)
378         {
379                 $objs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
380                 $ret  = array();
381                 foreach ($objs as $obj) {
382                         $ret[] = array(
383                                 "id"           => IntVal($obj["id"]),
384                                 "calendardata" => $obj["calendardata"],
385                                 "uri"          => $obj["uri"],
386                                 "lastmodified" => $obj["lastmodified"],
387                                 "calendarid"   => $calendarId,
388                                 "etag"         => $obj["etag"],
389                                 "size"         => IntVal($obj["size"]),
390                         );
391                 }
392                 return $ret;
393         }
394
395         /**
396          * Returns information from a single calendar object, based on it's object
397          * uri.
398          *
399          * The returned array must have the same keys as getCalendarObjects. The
400          * 'calendardata' object is required here though, while it's not required
401          * for getCalendarObjects.
402          *
403          * @param string $calendarId
404          * @param string $objectUri
405          * @throws Sabre_DAV_Exception_NotFound
406          * @return array
407          */
408         function getCalendarObject($calendarId, $objectUri)
409         {
410                 $o = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'",
411                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
412                 if (count($o) > 0) {
413                         $o[0]["calendarid"]   = $calendarId;
414                         $o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]);
415                         return $o[0];
416                 } else throw new Sabre_DAV_Exception_NotFound($calendarId . " / " . $objectUri);
417         }
418
419         /**
420          * Creates a new calendar object.
421          *
422          * It is possible return an etag from this function, which will be used in
423          * the response to this PUT request. Note that the ETag must be surrounded
424          * by double-quotes.
425          *
426          * However, you should only really return this ETag if you don't mangle the
427          * calendar-data. If the result of a subsequent GET to this object is not
428          * the exact same as this request body, you should omit the ETag.
429          *
430          * @param mixed $calendarId
431          * @param string $objectUri
432          * @param string $calendarData
433          * @return string|null
434          */
435         function createCalendarObject($calendarId, $objectUri, $calendarData)
436         {
437                 $calendarData = icalendar_sanitize_string($calendarData);
438
439                 $extraData = $this->getDenormalizedData($calendarData);
440
441                 q("INSERT INTO %s%scalendarobjects (`calendar_id`, `uri`, `calendardata`, `lastmodified`, `componentType`, `firstOccurence`, `lastOccurence`, `etag`, `size`)
442                         VALUES (%d, '%s', '%s', NOW(), '%s', '%s', '%s', '%s', %d)",
443                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri), addslashes($calendarData), dbesc($extraData['componentType']),
444                         dbesc(wdcal_php2MySqlTime($extraData['firstOccurence'])), dbesc(wdcal_php2MySqlTime($extraData['lastOccurence'])), dbesc($extraData["etag"]), IntVal($extraData["size"])
445                 );
446
447                 $this->increaseCalendarCtag($calendarId);
448                 renderCalDavEntry_uri($objectUri);
449
450                 return '"' . $extraData['etag'] . '"';
451         }
452
453         /**
454          * Updates an existing calendarobject, based on it's uri.
455          *
456          * It is possible return an etag from this function, which will be used in
457          * the response to this PUT request. Note that the ETag must be surrounded
458          * by double-quotes.
459          *
460          * However, you should only really return this ETag if you don't mangle the
461          * calendar-data. If the result of a subsequent GET to this object is not
462          * the exact same as this request body, you should omit the ETag.
463          *
464          * @param mixed $calendarId
465          * @param string $objectUri
466          * @param string $calendarData
467          * @return string|null
468          */
469         function updateCalendarObject($calendarId, $objectUri, $calendarData)
470         {
471                 $calendarData = icalendar_sanitize_string($calendarData);
472
473                 $extraData = $this->getDenormalizedData($calendarData);
474
475                 q("UPDATE %s%scalendarobjects SET `calendardata` = '%s', `lastmodified` = NOW(), `etag` = '%s', `size` = %d, `componentType` = '%s', `firstOccurence` = '%s', `lastOccurence` = '%s'
476                         WHERE `calendar_id` = %d AND `uri` = '%s'",
477                         CALDAV_SQL_DB, CALDAV_SQL_PREFIX, dbesc($calendarData), dbesc($extraData["etag"]), IntVal($extraData["size"]), dbesc($extraData["componentType"]),
478                         dbesc(wdcal_php2MySqlTime($extraData["firstOccurence"])), dbesc(wdcal_php2MySqlTime($extraData["lastOccurence"])), IntVal($calendarId), dbesc($objectUri));
479
480                 $this->increaseCalendarCtag($calendarId);
481                 renderCalDavEntry_uri($objectUri);
482
483                 return '"' . $extraData['etag'] . '"';
484         }
485
486         /**
487          * Deletes an existing calendar object.
488          *
489          * @param string $calendarId
490          * @param string $objectUri
491          * @throws Sabre_DAV_Exception_NotFound
492          * @return void
493          */
494         function deleteCalendarObject($calendarId, $objectUri)
495         {
496                 $r = q("SELECT `id` FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
497                 if (count($r) == 0) throw new Sabre_DAV_Exception_NotFound();
498
499                 q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
500
501                 $this->increaseCalendarCtag($calendarId);
502                 renderCalDavEntry_calobj_id($r[0]["id"]);
503         }
504 }