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