diff options
author | Andrew Manning <tamanning@zoho.com> | 2016-06-30 21:51:33 -0400 |
---|---|---|
committer | Andrew Manning <tamanning@zoho.com> | 2016-06-30 21:51:33 -0400 |
commit | 0fd8eeec23a0613db8ea6c5bb54b4658ddaa0e61 (patch) | |
tree | b6684b05aee92729406a90f397ccd446f58863c7 /vendor/sabre/dav/lib/CalDAV/Backend | |
parent | 7124c0aee5486aab74272c81ceb3e383b2e3a7f7 (diff) | |
parent | 852b2659e9a71f0542e822aa20efc009e22ff66a (diff) | |
download | volse-hubzilla-0fd8eeec23a0613db8ea6c5bb54b4658ddaa0e61.tar.gz volse-hubzilla-0fd8eeec23a0613db8ea6c5bb54b4658ddaa0e61.tar.bz2 volse-hubzilla-0fd8eeec23a0613db8ea6c5bb54b4658ddaa0e61.zip |
Merge remote-tracking branch 'upstream/dev' into wiki
Diffstat (limited to 'vendor/sabre/dav/lib/CalDAV/Backend')
-rw-r--r-- | vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php | 6 | ||||
-rw-r--r-- | vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php | 15 | ||||
-rw-r--r-- | vendor/sabre/dav/lib/CalDAV/Backend/PDO.php | 381 | ||||
-rw-r--r-- | vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php | 225 | ||||
-rw-r--r-- | vendor/sabre/dav/lib/CalDAV/Backend/SimplePDO.php | 296 |
5 files changed, 676 insertions, 247 deletions
diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php b/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php index 7513fb60d..bd8ee7602 100644 --- a/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php +++ b/vendor/sabre/dav/lib/CalDAV/Backend/BackendInterface.php @@ -44,10 +44,12 @@ interface BackendInterface { * If the creation was a success, an id must be returned that can be used to * reference this calendar in other methods, such as updateCalendar. * + * The id can be any type, including ints, strings, objects or array. + * * @param string $principalUri * @param string $calendarUri * @param array $properties - * @return void + * @return mixed */ function createCalendar($principalUri, $calendarUri, array $properties); @@ -63,7 +65,7 @@ interface BackendInterface { * * Read the PropPatch documentation for more info and examples. * - * @param string $path + * @param mixed $calendarId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php index 19b9b22a7..9c00a89ef 100644 --- a/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php +++ b/vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php @@ -43,4 +43,19 @@ interface NotificationSupport extends BackendInterface { */ function deleteNotification($principalUri, NotificationInterface $notification); + /** + * This method is called when a user replied to a request to share. + * + * If the user chose to accept the share, this method should return the + * newly created calendar url. + * + * @param string href The sharee who is replying (often a mailto: address) + * @param int status One of the SharingPlugin::STATUS_* constants + * @param string $calendarUri The url to the calendar thats being shared + * @param string $inReplyTo The unique id this message is a response to + * @param string $summary A description of the reply + * @return null|string + */ + function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null); + } diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php b/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php index 76b69dc6e..95f1d49a6 100644 --- a/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php +++ b/vendor/sabre/dav/lib/CalDAV/Backend/PDO.php @@ -2,10 +2,11 @@ namespace Sabre\CalDAV\Backend; -use Sabre\VObject; use Sabre\CalDAV; use Sabre\DAV; use Sabre\DAV\Exception\Forbidden; +use Sabre\VObject; +use Sabre\DAV\Xml\Element\Sharee; /** * PDO CalDAV backend @@ -17,7 +18,12 @@ use Sabre\DAV\Exception\Forbidden; * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, SchedulingSupport { +class PDO extends AbstractBackend + implements + SyncSupport, + SubscriptionSupport, + SchedulingSupport, + SharingSupport { /** * We need to specify a max date, because we need to stop *somewhere* @@ -44,6 +50,16 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S public $calendarTableName = 'calendars'; /** + * The table name that will be used for calendars instances. + * + * A single calendar can have multiple instances, if the calendar is + * shared. + * + * @var string + */ + public $calendarInstancesTableName = 'calendarinstances'; + + /** * The table name that will be used for calendar objects * * @var string @@ -140,16 +156,23 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S function getCalendarsForUser($principalUri) { $fields = array_values($this->propertyMap); - $fields[] = 'id'; + $fields[] = 'calendarid'; $fields[] = 'uri'; $fields[] = 'synctoken'; $fields[] = 'components'; $fields[] = 'principaluri'; $fields[] = 'transparent'; + $fields[] = 'access'; // Making fields a comma-delimited list $fields = implode(', ', $fields); - $stmt = $this->pdo->prepare("SELECT " . $fields . " FROM " . $this->calendarTableName . " WHERE principaluri = ? ORDER BY calendarorder ASC"); + $stmt = $this->pdo->prepare(<<<SQL +SELECT {$this->calendarInstancesTableName}.id as id, $fields FROM {$this->calendarInstancesTableName} + LEFT JOIN {$this->calendarTableName} ON + {$this->calendarInstancesTableName}.calendarid = {$this->calendarTableName}.id +WHERE principaluri = ? ORDER BY calendarorder ASC +SQL + ); $stmt->execute([$principalUri]); $calendars = []; @@ -161,15 +184,27 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S } $calendar = [ - 'id' => $row['id'], + 'id' => [(int)$row['calendarid'], (int)$row['id']], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], '{' . CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => 'http://sabre.io/ns/sync/' . ($row['synctoken'] ? $row['synctoken'] : '0'), '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', '{' . CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components), '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), + 'share-resource-uri' => '/ns/share/' . $row['calendarid'], ]; + $calendar['share-access'] = (int)$row['access']; + // 1 = owner, 2 = readonly, 3 = readwrite + if ($row['access'] > 1) { + // We need to find more information about the original owner. + //$stmt2 = $this->pdo->prepare('SELECT principaluri FROM ' . $this->calendarInstancesTableName . ' WHERE access = 1 AND id = ?'); + //$stmt2->execute([$row['id']]); + + // read-only is for backwards compatbility. Might go away in + // the future. + $calendar['read-only'] = (int)$row['access'] === \Sabre\DAV\Sharing\Plugin::ACCESS_READ; + } foreach ($this->propertyMap as $xmlName => $dbName) { $calendar[$xmlName] = $row[$dbName]; @@ -199,31 +234,38 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S $fieldNames = [ 'principaluri', 'uri', - 'synctoken', 'transparent', + 'calendarid', ]; $values = [ ':principaluri' => $principalUri, ':uri' => $calendarUri, - ':synctoken' => 1, ':transparent' => 0, ]; - // Default value + $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; - $fieldNames[] = 'components'; if (!isset($properties[$sccs])) { - $values[':components'] = 'VEVENT,VTODO'; + // Default value + $components = 'VEVENT,VTODO'; } else { if (!($properties[$sccs] instanceof CalDAV\Xml\Property\SupportedCalendarComponentSet)) { throw new DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet'); } - $values[':components'] = implode(',', $properties[$sccs]->getValue()); + $components = implode(',', $properties[$sccs]->getValue()); } $transp = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; if (isset($properties[$transp])) { - $values[':transparent'] = $properties[$transp]->getValue() === 'transparent'; + $values[':transparent'] = $properties[$transp]->getValue() === 'transparent' ? 1 : 0; } + $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (synctoken, components) VALUES (1, ?)"); + $stmt->execute([$components]); + + $calendarId = $this->pdo->lastInsertId( + $this->calendarTableName . '_id_seq' + ); + + $values[':calendarid'] = $calendarId; foreach ($this->propertyMap as $xmlName => $dbName) { if (isset($properties[$xmlName])) { @@ -233,10 +275,14 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S } } - $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarInstancesTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); + $stmt->execute($values); - return $this->pdo->lastInsertId(); + return [ + $calendarId, + $this->pdo->lastInsertId($this->calendarInstancesTableName . '_id_seq') + ]; } @@ -252,16 +298,21 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S * * Read the PropPatch documenation for more info and examples. * - * @param string $calendarId + * @param mixed $calendarId * @param \Sabre\DAV\PropPatch $propPatch * @return void */ function updateCalendar($calendarId, \Sabre\DAV\PropPatch $propPatch) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $supportedProperties = array_keys($this->propertyMap); $supportedProperties[] = '{' . CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp'; - $propPatch->handle($supportedProperties, function($mutations) use ($calendarId) { + $propPatch->handle($supportedProperties, function($mutations) use ($calendarId, $instanceId) { $newValues = []; foreach ($mutations as $propertyName => $propertyValue) { @@ -282,8 +333,8 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S $valuesSql[] = $fieldName . ' = ?'; } - $stmt = $this->pdo->prepare("UPDATE " . $this->calendarTableName . " SET " . implode(', ', $valuesSql) . " WHERE id = ?"); - $newValues['id'] = $calendarId; + $stmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET " . implode(', ', $valuesSql) . " WHERE id = ?"); + $newValues['id'] = $instanceId; $stmt->execute(array_values($newValues)); $this->addChange($calendarId, "", 2); @@ -297,19 +348,49 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S /** * Delete a calendar and all it's objects * - * @param string $calendarId + * @param mixed $calendarId * @return void */ function deleteCalendar($calendarId) { - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); - $stmt->execute([$calendarId]); + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarTableName . ' WHERE id = ?'); - $stmt->execute([$calendarId]); + $stmt = $this->pdo->prepare('SELECT access FROM ' . $this->calendarInstancesTableName . ' where id = ?'); + $stmt->execute([$instanceId]); + $access = (int)$stmt->fetchColumn(); + + if ($access === \Sabre\DAV\Sharing\Plugin::ACCESS_SHAREDOWNER) { + + /** + * If the user is the owner of the calendar, we delete all data and all + * instances. + **/ + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarChangesTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarTableName . ' WHERE id = ?'); + $stmt->execute([$calendarId]); + + } else { + + /** + * If it was an instance of a shared calendar, we only delete that + * instance. + */ + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + $stmt->execute([$instanceId]); + + } - $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarChangesTableName . ' WHERE calendarid = ?'); - $stmt->execute([$calendarId]); } @@ -341,11 +422,16 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S * used/fetched to determine these numbers. If both are specified the * amount of times this is needed is reduced by a great degree. * - * @param string $calendarId + * @param mixed $calendarId * @return array */ function getCalendarObjects($calendarId) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ?'); $stmt->execute([$calendarId]); @@ -354,9 +440,8 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S $result[] = [ 'id' => $row['id'], 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], + 'lastmodified' => (int)$row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'component' => strtolower($row['componenttype']), ]; @@ -378,12 +463,17 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S * * This method must return null if the object did not exist. * - * @param string $calendarId + * @param mixed $calendarId * @param string $objectUri * @return array|null */ function getCalendarObject($calendarId, $objectUri) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $stmt = $this->pdo->prepare('SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); $row = $stmt->fetch(\PDO::FETCH_ASSOC); @@ -393,9 +483,8 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S return [ 'id' => $row['id'], 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], + 'lastmodified' => (int)$row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $row['calendardata'], 'component' => strtolower($row['componenttype']), @@ -417,6 +506,11 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S */ function getMultipleCalendarObjects($calendarId, array $uris) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $query = 'SELECT id, uri, lastmodified, etag, calendarid, size, calendardata, componenttype FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri IN ('; // Inserting a whole bunch of question marks $query .= implode(',', array_fill(0, count($uris), '?')); @@ -431,9 +525,8 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S $result[] = [ 'id' => $row['id'], 'uri' => $row['uri'], - 'lastmodified' => $row['lastmodified'], + 'lastmodified' => (int)$row['lastmodified'], 'etag' => '"' . $row['etag'] . '"', - 'calendarid' => $row['calendarid'], 'size' => (int)$row['size'], 'calendardata' => $row['calendardata'], 'component' => strtolower($row['componenttype']), @@ -465,6 +558,11 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S */ function createCalendarObject($calendarId, $objectUri, $calendarData) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $extraData = $this->getDenormalizedData($calendarData); $stmt = $this->pdo->prepare('INSERT INTO ' . $this->calendarObjectTableName . ' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); @@ -506,6 +604,11 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S */ function updateCalendarObject($calendarId, $objectUri, $calendarData) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $extraData = $this->getDenormalizedData($calendarData); $stmt = $this->pdo->prepare('UPDATE ' . $this->calendarObjectTableName . ' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?'); @@ -583,6 +686,10 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S } } + + // Ensure Occurence values are positive + if ($firstOccurence < 0) $firstOccurence = 0; + if ($lastOccurence < 0) $lastOccurence = 0; } // Destroy circular references to PHP will GC the object. @@ -604,12 +711,17 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S * * The object uri is only the basename, or filename and not a full path. * - * @param string $calendarId + * @param mixed $calendarId * @param string $objectUri * @return void */ function deleteCalendarObject($calendarId, $objectUri) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $stmt = $this->pdo->prepare('DELETE FROM ' . $this->calendarObjectTableName . ' WHERE calendarid = ? AND uri = ?'); $stmt->execute([$calendarId, $objectUri]); @@ -665,12 +777,17 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S * This specific implementation (for the PDO) backend optimizes filters on * specific components, and VEVENT time-ranges. * - * @param string $calendarId + * @param mixed $calendarId * @param array $filters * @return array */ function calendarQuery($calendarId, array $filters) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + $componentType = null; $requirePostFilter = true; $timeRange = null; @@ -766,14 +883,14 @@ class PDO extends AbstractBackend implements SyncSupport, SubscriptionSupport, S $query = <<<SQL SELECT - calendars.uri AS calendaruri, calendarobjects.uri as objecturi + calendar_instances.uri AS calendaruri, calendarobjects.uri as objecturi FROM $this->calendarObjectTableName AS calendarobjects LEFT JOIN - $this->calendarTableName AS calendars - ON calendarobjects.calendarid = calendars.id + $this->calendarInstancesTableName AS calendar_instances + ON calendarobjects.calendarid = calendar_instances.calendarid WHERE - calendars.principaluri = ? + calendar_instances.principaluri = ? AND calendarobjects.uid = ? SQL; @@ -837,7 +954,7 @@ SQL; * * The limit is 'suggestive'. You are free to ignore it. * - * @param string $calendarId + * @param mixed $calendarId * @param string $syncToken * @param int $syncLevel * @param int $limit @@ -845,6 +962,11 @@ SQL; */ function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null) { + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + // Current synctoken $stmt = $this->pdo->prepare('SELECT synctoken FROM ' . $this->calendarTableName . ' WHERE id = ?'); $stmt->execute([ $calendarId ]); @@ -1043,7 +1165,9 @@ SQL; $stmt = $this->pdo->prepare("INSERT INTO " . $this->calendarSubscriptionsTableName . " (" . implode(', ', $fieldNames) . ") VALUES (" . implode(', ', array_keys($values)) . ")"); $stmt->execute($values); - return $this->pdo->lastInsertId(); + return $this->pdo->lastInsertId( + $this->calendarSubscriptionsTableName . '_id_seq' + ); } @@ -1207,4 +1331,179 @@ SQL; } + /** + * Updates the list of shares. + * + * @param mixed $calendarId + * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees + * @return void + */ + function updateInvites($calendarId, array $sharees) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to $calendarId is expected to be an array with a calendarId and an instanceId'); + } + $currentInvites = $this->getInvites($calendarId); + list($calendarId, $instanceId) = $calendarId; + + $removeStmt = $this->pdo->prepare("DELETE FROM " . $this->calendarInstancesTableName . " WHERE calendarid = ? AND share_href = ? AND access IN (2,3)"); + $updateStmt = $this->pdo->prepare("UPDATE " . $this->calendarInstancesTableName . " SET access = ?, share_displayname = ?, share_invitestatus = ? WHERE calendarid = ? AND share_href = ?"); + + $insertStmt = $this->pdo->prepare(' +INSERT INTO ' . $this->calendarInstancesTableName . ' + ( + calendarid, + principaluri, + access, + displayname, + uri, + description, + calendarorder, + calendarcolor, + timezone, + transparent, + share_href, + share_displayname, + share_invitestatus + ) + SELECT + ?, + ?, + ?, + displayname, + ?, + description, + calendarorder, + calendarcolor, + timezone, + 1, + ?, + ?, + ? + FROM ' . $this->calendarInstancesTableName . ' WHERE id = ?'); + + foreach ($sharees as $sharee) { + + if ($sharee->access === \Sabre\DAV\Sharing\Plugin::ACCESS_NOACCESS) { + // if access was set no NOACCESS, it means access for an + // existing sharee was removed. + $removeStmt->execute([$calendarId, $sharee->href]); + continue; + } + + if (is_null($sharee->principal)) { + // If the server could not determine the principal automatically, + // we will mark the invite status as invalid. + $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_INVALID; + } else { + // Because sabre/dav does not yet have an invitation system, + // every invite is automatically accepted for now. + $sharee->inviteStatus = \Sabre\DAV\Sharing\Plugin::INVITE_ACCEPTED; + } + + foreach ($currentInvites as $oldSharee) { + + if ($oldSharee->href === $sharee->href) { + // This is an update + $sharee->properties = array_merge( + $oldSharee->properties, + $sharee->properties + ); + $updateStmt->execute([ + $sharee->access, + isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, + $sharee->inviteStatus ?: $oldSharee->inviteStatus, + $calendarId, + $sharee->href + ]); + continue 2; + } + + } + // If we got here, it means it was a new sharee + $insertStmt->execute([ + $calendarId, + $sharee->principal, + $sharee->access, + \Sabre\DAV\UUIDUtil::getUUID(), + $sharee->href, + isset($sharee->properties['{DAV:}displayname']) ? $sharee->properties['{DAV:}displayname'] : null, + $sharee->inviteStatus ?: \Sabre\DAV\Sharing\Plugin::INVITE_NORESPONSE, + $instanceId + ]); + + } + + } + + /** + * Returns the list of people whom a calendar is shared with. + * + * Every item in the returned list must be a Sharee object with at + * least the following properties set: + * $href + * $shareAccess + * $inviteStatus + * + * and optionally: + * $properties + * + * @param mixed $calendarId + * @return \Sabre\DAV\Xml\Element\Sharee[] + */ + function getInvites($calendarId) { + + if (!is_array($calendarId)) { + throw new \InvalidArgumentException('The value passed to getInvites() is expected to be an array with a calendarId and an instanceId'); + } + list($calendarId, $instanceId) = $calendarId; + + $query = <<<SQL +SELECT + principaluri, + access, + share_href, + share_displayname, + share_invitestatus +FROM {$this->calendarInstancesTableName} +WHERE + calendarid = ? +SQL; + + $stmt = $this->pdo->prepare($query); + $stmt->execute([$calendarId]); + + $result = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $result[] = new Sharee([ + 'href' => isset($row['share_href']) ? $row['share_href'] : \Sabre\HTTP\encodePath($row['principaluri']), + 'access' => (int)$row['access'], + /// Everyone is always immediately accepted, for now. + 'inviteStatus' => (int)$row['share_invitestatus'], + 'properties' => + !empty($row['share_displayname']) + ? [ '{DAV:}displayname' => $row['share_displayname'] ] + : [], + 'principal' => $row['principaluri'], + ]); + + } + return $result; + + } + + /** + * Publishes a calendar + * + * @param mixed $calendarId + * @param bool $value + * @return void + */ + function setPublishStatus($calendarId, $value) { + + throw new \Sabre\DAV\Exception\NotImplemented('Not implemented'); + + } + } diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php b/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php index 6a11b0ab1..8b6e074e0 100644 --- a/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php +++ b/vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php @@ -5,231 +5,48 @@ namespace Sabre\CalDAV\Backend; /** * Adds support for sharing features to a CalDAV server. * - * Note: This feature is experimental, and may change in between different - * SabreDAV versions. + * CalDAV backends that implement this interface, must make the following + * modifications to getCalendarsForUser: * - * Early warning: Currently SabreDAV provides no implementation for this. This - * is, because in it's current state there is no elegant way to do this. - * The problem lies in the fact that a real CalDAV server with sharing support - * would first need email support (with invite notifications), and really also - * a browser-frontend that allows people to accept or reject these shares. - * - * In addition, the CalDAV backends are currently kept as independent as - * possible, and should not be aware of principals, email addresses or - * accounts. - * - * Adding an implementation for Sharing to standard-sabredav would contradict - * these goals, so for this reason this is currently not implemented, although - * it may very well in the future; but probably not before SabreDAV 2.0. - * - * The interface works however, so if you implement all this, and do it - * correctly sharing _will_ work. It's not particularly easy, and I _urge you_ - * to make yourself acquainted with the following document first: - * - * https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing.txt - * - * An overview - * =========== - * - * Implementing this interface will allow a user to share his or her calendars - * to other users. Effectively, when a calendar is shared the calendar will - * show up in both the Sharer's and Sharee's calendar-home root. - * This interface adds a few methods that ensure that this happens, and there - * are also a number of new requirements in the base-class you must now follow. - * - * - * How it works - * ============ - * - * When a user shares a calendar, the updateShares() method will be called with - * a list of sharees that are now added, and a list of sharees that have been - * removed. - * Removal is instant, but when a sharee is added the sharee first gets a - * chance to accept or reject the invitation for a share. - * - * After a share is accepted, the calendar will be returned from - * getUserCalendars for both the sharer, and the sharee. - * - * If the sharee deletes the calendar, only their share gets deleted. When the - * owner deletes a calendar, it will be removed for everybody. - * - * - * Notifications - * ============= - * - * During all these sharing operations, a lot of notifications are sent back - * and forward. - * - * Whenever the list of sharees for a calendar has been changed (they have been - * added, removed or modified) all sharees should get a notification for this - * change. - * This notification is always represented by: - * - * Sabre\CalDAV\Notifications\Notification\Invite - * - * In the case of an invite, the sharee may reply with an 'accept' or - * 'decline'. These are always represented by: - * - * Sabre\CalDAV\Notifications\Notification\InviteReply - * - * - * Calendar access by sharees - * ========================== - * - * As mentioned earlier, shared calendars must now also be returned for - * getCalendarsForUser for sharees. A few things change though. - * - * The following properties must be specified: - * - * 1. {http://calendarserver.org/ns/}shared-url - * - * This property MUST contain the url to the original calendar, that is.. the - * path to the calendar from the owner. - * - * 2. {http://sabredav.org/ns}owner-principal - * - * This is a url to to the principal who is sharing the calendar. - * - * 3. {http://sabredav.org/ns}read-only - * - * This should be either 0 or 1, depending on if the user has read-only or - * read-write access to the calendar. - * - * Only when this is done, the calendar will correctly be marked as a calendar - * that's shared to him, thus allowing clients to display the correct interface - * and ACL enforcement. - * - * If a sharee deletes their calendar, only their instance of the calendar - * should be deleted, the original should still exists. - * Pretty much any 'dead' WebDAV properties on these shared calendars should be - * specific to a user. This means that if the displayname is changed by a - * sharee, the original is not affected. This is also true for: - * * The description - * * The color - * * The order - * * And any other dead properties. - * - * Properties like a ctag should not be different for multiple instances of the - * calendar. - * - * Lastly, objects *within* calendars should also have user-specific data. The - * two things that are user-specific are: - * * VALARM objects - * * The TRANSP property - * - * This _also_ implies that if a VALARM is deleted by a sharee for some event, - * this has no effect on the original VALARM. - * - * Understandably, the this last requirement is one of the hardest. - * Realisticly, I can see people ignoring this part of the spec, but that could - * cause a different set of issues. - * - * - * Publishing - * ========== - * - * When a user publishes a url, the server should generate a 'publish url'. - * This is a read-only url, anybody can use to consume the calendar feed. - * - * Calendars are in one of two states: - * * published - * * unpublished - * - * If a calendar is published, the following property should be returned - * for each calendar in getCalendarsForUser. - * - * {http://calendarserver.org/ns/}publish-url - * - * This element should contain a {DAV:}href element, which points to the - * public url that does not require authentication. Unlike every other href, - * this url must be absolute. - * - * Ideally, the following property is always returned - * - * {http://calendarserver.org/ns/}pre-publish-url - * - * This property should contain the url that the calendar _would_ have, if it - * were to be published. iCal uses this to display the url, before the user - * will actually publish it. - * - * - * Selectively disabling publish or share feature - * ============================================== - * - * If Sabre\CalDAV\Property\AllowedSharingModes is returned from - * getCalendarsForUser, this allows the server to specify whether either sharing, - * or publishing is supported. - * - * This allows a client to determine in advance which features are available, - * and update the interface appropriately. If this property is not returned by - * the backend, the SharingPlugin automatically injects it and assumes both - * features are available. + * 1. Return shared calendars for users. + * 2. For every calendar, return calendar-resource-uri. This strings is a URI or + * relative URI reference that must be unique for every calendar, but + * identical for every instance of the same shared calenar. + * 3. For every calenar, you must return a share-access element. This element + * should contain one of the Sabre\DAV\Sharing\Plugin:ACCESS_* contants and + * indicates the access level the user has. * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://evertpot.com/) * @license http://sabre.io/license/ Modified BSD License */ -interface SharingSupport extends NotificationSupport { +interface SharingSupport extends BackendInterface { /** * Updates the list of shares. * - * The first array is a list of people that are to be added to the - * calendar. - * - * Every element in the add array has the following properties: - * * href - A url. Usually a mailto: address - * * commonName - Usually a first and last name, or false - * * summary - A description of the share, can also be false - * * readOnly - A boolean value - * - * Every element in the remove array is just the address string. - * - * Note that if the calendar is currently marked as 'not shared' by and - * this method is called, the calendar should be 'upgraded' to a shared - * calendar. - * * @param mixed $calendarId - * @param array $add - * @param array $remove + * @param \Sabre\DAV\Xml\Element\Sharee[] $sharees * @return void */ - function updateShares($calendarId, array $add, array $remove); + function updateInvites($calendarId, array $sharees); /** * Returns the list of people whom this calendar is shared with. * - * Every element in this array should have the following properties: - * * href - Often a mailto: address - * * commonName - Optional, for example a first + last name - * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants. - * * readOnly - boolean - * * summary - Optional, a description for the share + * Every item in the returned list must be a Sharee object with at + * least the following properties set: + * $href + * $shareAccess + * $inviteStatus * - * This method may be called by either the original instance of the - * calendar, as well as the shared instances. In the case of the shared - * instances, it is perfectly acceptable to return an empty array in case - * there are privacy concerns. + * and optionally: + * $properties * * @param mixed $calendarId - * @return array - */ - function getShares($calendarId); - - /** - * This method is called when a user replied to a request to share. - * - * If the user chose to accept the share, this method should return the - * newly created calendar url. - * - * @param string href The sharee who is replying (often a mailto: address) - * @param int status One of the SharingPlugin::STATUS_* constants - * @param string $calendarUri The url to the calendar thats being shared - * @param string $inReplyTo The unique id this message is a response to - * @param string $summary A description of the reply - * @return null|string + * @return \Sabre\DAV\Xml\Element\Sharee[] */ - function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null); + function getInvites($calendarId); /** * Publishes a calendar diff --git a/vendor/sabre/dav/lib/CalDAV/Backend/SimplePDO.php b/vendor/sabre/dav/lib/CalDAV/Backend/SimplePDO.php new file mode 100644 index 000000000..f8238ea9a --- /dev/null +++ b/vendor/sabre/dav/lib/CalDAV/Backend/SimplePDO.php @@ -0,0 +1,296 @@ +<?php + +namespace Sabre\CalDAV\Backend; + +use Sabre\CalDAV; +use Sabre\DAV; + +/** + * Simple PDO CalDAV backend. + * + * This class is basically the most minmum example to get a caldav backend up + * and running. This class uses the following schema (MySQL example): + * + * CREATE TABLE simple_calendars ( + * id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + * uri VARBINARY(200) NOT NULL, + * principaluri VARBINARY(200) NOT NULL + * ); + * + * CREATE TABLE simple_calendarobjects ( + * id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + * calendarid INT UNSIGNED NOT NULL, + * uri VARBINARY(200) NOT NULL, + * calendardata MEDIUMBLOB + * ) + * + * To make this class work, you absolutely need to have the PropertyStorage + * plugin enabled. + * + * @copyright Copyright (C) 2007-2015 fruux GmbH (https://fruux.com/). + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class SimplePDO extends AbstractBackend { + + /** + * pdo + * + * @var \PDO + */ + protected $pdo; + + /** + * Creates the backend + * + * @param \PDO $pdo + */ + function __construct(\PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Returns a list of calendars for a principal. + * + * Every project is an array with the following keys: + * * id, a unique id that will be used by other functions to modify the + * calendar. This can be the same as the uri or a database key. + * * uri. This is just the 'base uri' or 'filename' of the calendar. + * * principaluri. The owner of the calendar. Almost always the same as + * principalUri passed to this method. + * + * Furthermore it can contain webdav properties in clark notation. A very + * common one is '{DAV:}displayname'. + * + * Many clients also require: + * {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set + * For this property, you can just return an instance of + * Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet. + * + * If you return {http://sabredav.org/ns}read-only and set the value to 1, + * ACL will automatically be put in read-only mode. + * + * @param string $principalUri + * @return array + */ + function getCalendarsForUser($principalUri) { + + // Making fields a comma-delimited list + $stmt = $this->pdo->prepare("SELECT id, uri FROM simple_calendars WHERE principaluri = ? ORDER BY id ASC"); + $stmt->execute([$principalUri]); + + $calendars = []; + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + + $calendars[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'principaluri' => $principalUri, + ]; + + } + + return $calendars; + + } + + /** + * Creates a new calendar for a principal. + * + * If the creation was a success, an id must be returned that can be used + * to reference this calendar in other methods, such as updateCalendar. + * + * @param string $principalUri + * @param string $calendarUri + * @param array $properties + * @return string + */ + function createCalendar($principalUri, $calendarUri, array $properties) { + + $stmt = $this->pdo->prepare("INSERT INTO simple_calendars (principaluri, uri) VALUES (?, ?)"); + $stmt->execute([$principalUri, $calendarUri]); + + return $this->pdo->lastInsertId(); + + } + + /** + * Delete a calendar and all it's objects + * + * @param string $calendarId + * @return void + */ + function deleteCalendar($calendarId) { + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendars WHERE id = ?'); + $stmt->execute([$calendarId]); + + } + + /** + * Returns all calendar objects within a calendar. + * + * Every item contains an array with the following keys: + * * calendardata - The iCalendar-compatible calendar data + * * uri - a unique key which will be used to construct the uri. This can + * be any arbitrary string, but making sure it ends with '.ics' is a + * good idea. This is only the basename, or filename, not the full + * path. + * * lastmodified - a timestamp of the last modification time + * * etag - An arbitrary string, surrounded by double-quotes. (e.g.: + * ' "abcdef"') + * * size - The size of the calendar objects, in bytes. + * * component - optional, a string containing the type of object, such + * as 'vevent' or 'vtodo'. If specified, this will be used to populate + * the Content-Type header. + * + * Note that the etag is optional, but it's highly encouraged to return for + * speed reasons. + * + * The calendardata is also optional. If it's not returned + * 'getCalendarObject' will be called later, which *is* expected to return + * calendardata. + * + * If neither etag or size are specified, the calendardata will be + * used/fetched to determine these numbers. If both are specified the + * amount of times this is needed is reduced by a great degree. + * + * @param string $calendarId + * @return array + */ + function getCalendarObjects($calendarId) { + + $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ?'); + $stmt->execute([$calendarId]); + + $result = []; + foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $row) { + $result[] = [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"' . md5($row['calendardata']) . '"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), + 'calendardata' => $row['calendardata'], + ]; + } + + return $result; + + } + + /** + * Returns information from a single calendar object, based on it's object + * uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * The returned array must have the same keys as getCalendarObjects. The + * 'calendardata' object is required here though, while it's not required + * for getCalendarObjects. + * + * This method must return null if the object did not exist. + * + * @param string $calendarId + * @param string $objectUri + * @return array|null + */ + function getCalendarObject($calendarId, $objectUri) { + + $stmt = $this->pdo->prepare('SELECT id, uri, calendardata FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + $row = $stmt->fetch(\PDO::FETCH_ASSOC); + + if (!$row) return null; + + return [ + 'id' => $row['id'], + 'uri' => $row['uri'], + 'etag' => '"' . md5($row['calendardata']) . '"', + 'calendarid' => $calendarId, + 'size' => strlen($row['calendardata']), + 'calendardata' => $row['calendardata'], + ]; + + } + + /** + * Creates a new calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function createCalendarObject($calendarId, $objectUri, $calendarData) { + + $stmt = $this->pdo->prepare('INSERT INTO simple_calendarobjects (calendarid, uri, calendardata) VALUES (?,?,?)'); + $stmt->execute([ + $calendarId, + $objectUri, + $calendarData + ]); + + return '"' . md5($calendarData) . '"'; + + } + + /** + * Updates an existing calendarobject, based on it's uri. + * + * The object uri is only the basename, or filename and not a full path. + * + * It is possible return an etag from this function, which will be used in + * the response to this PUT request. Note that the ETag must be surrounded + * by double-quotes. + * + * However, you should only really return this ETag if you don't mangle the + * calendar-data. If the result of a subsequent GET to this object is not + * the exact same as this request body, you should omit the ETag. + * + * @param mixed $calendarId + * @param string $objectUri + * @param string $calendarData + * @return string|null + */ + function updateCalendarObject($calendarId, $objectUri, $calendarData) { + + $stmt = $this->pdo->prepare('UPDATE simple_calendarobjects SET calendardata = ? WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarData, $calendarId, $objectUri]); + + return '"' . md5($calendarData) . '"'; + + } + + /** + * Deletes an existing calendar object. + * + * The object uri is only the basename, or filename and not a full path. + * + * @param string $calendarId + * @param string $objectUri + * @return void + */ + function deleteCalendarObject($calendarId, $objectUri) { + + $stmt = $this->pdo->prepare('DELETE FROM simple_calendarobjects WHERE calendarid = ? AND uri = ?'); + $stmt->execute([$calendarId, $objectUri]); + + } + +} |