3 class Sabre_CalDAV_Backend_Private extends Sabre_CalDAV_Backend_Common
8 * @var null|Sabre_CalDAV_Backend_Private
10 private static $instance = null;
14 * @return Sabre_CalDAV_Backend_Private
16 public static function getInstance()
18 if (self::$instance == null) {
19 self::$instance = new Sabre_CalDAV_Backend_Private();
21 return self::$instance;
28 public function getNamespace()
30 return CALDAV_NAMESPACE_PRIVATE;
37 public static function getBackendTypeName() {
38 return t("Private Events");
43 * @param array $calendar
47 public function getPermissionsCalendar($calendar, $user)
49 if ($calendar["namespace"] == CALDAV_NAMESPACE_PRIVATE && $user == $calendar["namespace_id"]) return array("read"=> true, "write"=> true);
50 return array("read"=> false, "write"=> false);
55 * @param array $calendar
57 * @param string $calendarobject_id
58 * @param null|array $item_arr
61 public function getPermissionsItem($calendar, $user, $calendarobject_id, $item_arr = null)
63 return $this->getPermissionsCalendar($calendar, $user);
69 * @param array $calendar
70 * @param string $base_path
73 private function jqcal2wdcal($row, $calendar, $base_path)
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"])
78 $editable = $this->getPermissionsItem($calendar["namespace_id"], $row["calendarobject_id"], $row);
80 $end = wdcal_mySql2PhpTime($row["EndTime"]);
81 if ($row["IsAllDayEvent"]) $end -= 1;
84 "jq_id" => $row["id"],
85 "ev_id" => $row["calendarobject_id"],
86 "summary" => escape_tags($row["Summary"]),
87 "start" => wdcal_mySql2PhpTime($row["StartTime"]),
89 "is_allday" => $row["IsAllDayEvent"],
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),
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" => "",
105 * @param int $calendarId
108 * @param string $base_path
111 public function listItemsByRange($calendarId, $sd, $ed, $base_path)
113 $calendar = Sabre_CalDAV_Backend_Common::loadCalendarById($calendarId);
114 $von = wdcal_php2MySqlTime($sd);
115 $bis = wdcal_php2MySqlTime($ed);
116 $timezoneOffset = date("P");
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));
124 foreach ($evs as $row) $events[] = $this->jqcal2wdcal($row, $calendar, $base_path . $row["calendar_id"] . "/");
131 * @param int $calendar_id
132 * @param int $calendarobject_id
135 public function getItemDetailRedirect($calendar_id, $calendarobject_id)
137 return "/dav/wdcal/$calendar_id/$calendarobject_id/edit/";
141 * Returns a list of calendars for a principal.
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
148 * * principaluri. The owner of the calendar. Almost always the same as
149 * principalUri passed to this method.
151 * Furthermore it can contain webdav properties in clark notation. A very
152 * common one is '{DAV:}displayname'.
154 * @param string $principalUri
155 * @throws DAVVersionMismatchException
158 public function getCalendarsForUser($principalUri)
160 $n = dav_compat_principal2namespace($principalUri);
161 if ($n["namespace"] != $this->getNamespace()) return array();
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"]));
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;
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",
176 foreach ($this->propertyMap as $key=> $field) $dat[$key] = $cal[$field];
186 * Creates a new calendar for a principal.
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.
191 * @param string $principalUri
192 * @param string $calendarUri
193 * @param array $properties
194 * @throws Sabre_DAV_Exception
195 * @return string|void
197 public function createCalendar($principalUri, $calendarUri, array $properties)
200 $uid = dav_compat_principal2uid($principalUri);
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");
205 $keys = array("`namespace`", "`namespace_id`", "`ctag`", "`uri`");
206 $vals = array(CALDAV_NAMESPACE_PRIVATE, IntVal($uid), 1, "'" . dbesc($calendarUri) . "'");
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');
215 $v = $properties[$sccs]->getValue();
216 $has_vevent = $has_vtodo = 0;
218 if (mb_strtolower($w) == "vevent") $has_vevent = 1;
219 if (mb_strtolower($w) == "vtodo") $has_vtodo = 1;
222 $keys[] = "`has_vevent`";
223 $keys[] = "`has_vtodo`";
224 $vals[] = $has_vevent;
225 $vals[] = $has_vtodo;
227 foreach ($this->propertyMap as $xmlName=> $dbName) {
228 if (isset($properties[$xmlName])) {
229 $keys[] = "`$dbName`";
230 $vals[] = "'" . dbesc($properties[$xmlName]) . "'";
234 $sql = sprintf("INSERT INTO %s%scalendars (" . implode(', ', $keys) . ") VALUES (" . implode(', ', $vals) . ")", CALDAV_SQL_DB, CALDAV_SQL_PREFIX);
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
246 * Updates properties for a calendar.
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.
252 * This method must be atomic. If one property cannot be changed, the
253 * entire operation must fail.
255 * If the operation was successful, true can be returned.
256 * If the operation failed, false can be returned.
258 * Deletion of a non-existent property is always successful.
260 * Lastly, it is optional to return detailed information about any
261 * failures. In this case an array should be returned with the following
266 * '{DAV:}displayname' => null,
269 * '{DAV:}owner' => null,
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.
277 * @param string $calendarId
278 * @param array $mutations
281 public function updateCalendar($calendarId, array $mutations)
284 $newValues = array();
286 200 => array(), // Ok
287 403 => array(), // Forbidden
288 424 => array(), // Failed Dependency
293 foreach ($mutations as $propertyName=> $propertyValue) {
295 // We don't know about this property.
296 if (!isset($this->propertyMap[$propertyName])) {
298 $result[403][$propertyName] = null;
299 unset($mutations[$propertyName]);
303 $fieldName = $this->propertyMap[$propertyName];
304 $newValues[$fieldName] = $propertyValue;
308 // If there were any errors we need to fail the request
310 // Properties has the remaining properties
311 foreach ($mutations as $propertyName=> $propertyValue) {
312 $result[424][$propertyName] = null;
315 // Removing unused statuscodes for cleanliness
316 foreach ($result as $status=> $properties) {
317 if (is_array($properties) && count($properties) === 0) unset($result[$status]);
324 $sql = "`ctag` = `ctag` + 1";
325 foreach ($newValues as $key=> $val) $sql .= ", `" . $key . "` = '" . dbesc($val) . "'";
327 $sql = sprintf("UPDATE %s%scalendars SET $sql WHERE `id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
337 * Delete a calendar and all it's objects
339 * @param string $calendarId
342 public function deleteCalendar($calendarId)
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));
351 * Returns all calendar objects within a calendar.
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.:
360 * * calendarid - The calendarid as it was passed to this function.
361 * * size - The size of the calendar objects, in bytes.
363 * Note that the etag is optional, but it's highly encouraged to return for
366 * The calendardata is also optional. If it's not returned
367 * 'getCalendarObject' will be called later, which *is* expected to return
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.
374 * @param mixed $calendarId
377 function getCalendarObjects($calendarId)
379 $objs = q("SELECT * FROM %s%scalendarobjects WHERE `calendar_id` = %d", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId));
381 foreach ($objs as $obj) {
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"]),
396 * Returns information from a single calendar object, based on it's object
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.
403 * @param string $calendarId
404 * @param string $objectUri
405 * @throws Sabre_DAV_Exception_NotFound
408 function getCalendarObject($calendarId, $objectUri)
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));
413 $o[0]["calendarid"] = $calendarId;
414 $o[0]["calendardata"] = str_ireplace("Europe/Belgrade", "Europe/Berlin", $o[0]["calendardata"]);
416 } else throw new Sabre_DAV_Exception_NotFound($calendarId . " / " . $objectUri);
420 * Creates a new calendar object.
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
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.
430 * @param mixed $calendarId
431 * @param string $objectUri
432 * @param string $calendarData
433 * @return string|null
435 function createCalendarObject($calendarId, $objectUri, $calendarData)
437 $calendarData = icalendar_sanitize_string($calendarData);
439 $extraData = $this->getDenormalizedData($calendarData);
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"])
447 $this->increaseCalendarCtag($calendarId);
448 renderCalDavEntry_uri($objectUri);
450 return '"' . $extraData['etag'] . '"';
454 * Updates an existing calendarobject, based on it's uri.
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
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.
464 * @param mixed $calendarId
465 * @param string $objectUri
466 * @param string $calendarData
467 * @return string|null
469 function updateCalendarObject($calendarId, $objectUri, $calendarData)
471 $calendarData = icalendar_sanitize_string($calendarData);
473 $extraData = $this->getDenormalizedData($calendarData);
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));
480 $this->increaseCalendarCtag($calendarId);
481 renderCalDavEntry_uri($objectUri);
483 return '"' . $extraData['etag'] . '"';
487 * Deletes an existing calendar object.
489 * @param string $calendarId
490 * @param string $objectUri
491 * @throws Sabre_DAV_Exception_NotFound
494 function deleteCalendarObject($calendarId, $objectUri)
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();
499 q("DELETE FROM %s%scalendarobjects WHERE `calendar_id` = %d AND `uri` = '%s'", CALDAV_SQL_DB, CALDAV_SQL_PREFIX, IntVal($calendarId), dbesc($objectUri));
501 $this->increaseCalendarCtag($calendarId);
502 renderCalDavEntry_calobj_id($r[0]["id"]);