aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/dav/lib/CalDAV/Backend
diff options
context:
space:
mode:
authorAndrew Manning <tamanning@zoho.com>2016-06-30 21:51:33 -0400
committerAndrew Manning <tamanning@zoho.com>2016-06-30 21:51:33 -0400
commit0fd8eeec23a0613db8ea6c5bb54b4658ddaa0e61 (patch)
treeb6684b05aee92729406a90f397ccd446f58863c7 /vendor/sabre/dav/lib/CalDAV/Backend
parent7124c0aee5486aab74272c81ceb3e383b2e3a7f7 (diff)
parent852b2659e9a71f0542e822aa20efc009e22ff66a (diff)
downloadvolse-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.php6
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/NotificationSupport.php15
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/PDO.php381
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SharingSupport.php225
-rw-r--r--vendor/sabre/dav/lib/CalDAV/Backend/SimplePDO.php296
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]);
+
+ }
+
+}