function setUp() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('SQLite driver is not available'); } if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite')) { unlink(SABRE_TEMPDIR . '/testdb.sqlite'); } $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite'); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $pdo->exec(<<<SQL CREATE TABLE simple_calendars ( id INTEGER PRIMARY KEY ASC NOT NULL, uri TEXT NOT NULL, principaluri TEXT NOT NULL ) SQL ); $pdo->exec(<<<SQL CREATE TABLE simple_calendarobjects ( id INTEGER PRIMARY KEY ASC NOT NULL, calendarid INT UNSIGNED NOT NULL, uri TEXT NOT NULL, calendardata TEXT ); SQL ); $this->pdo = $pdo; }
function testMkCalendarEmptyBodySucceed() { $request = new HTTP\Request('MKCALENDAR', '/calendars/user1/NEWCALENDAR'); $request->setBody(''); $this->server->httpRequest = $request; $this->server->exec(); $this->assertEquals(201, $this->response->status, 'Invalid response code received. Full response body: ' . $this->response->body); $calendars = $this->caldavBackend->getCalendarsForUser('principals/user1'); $this->assertEquals(3, count($calendars)); $newCalendar = null; foreach ($calendars as $calendar) { if ($calendar['uri'] === 'NEWCALENDAR') { $newCalendar = $calendar; break; } } $this->assertInternalType('array', $newCalendar); $keys = ['uri' => 'NEWCALENDAR', 'id' => null, '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set' => null]; foreach ($keys as $key => $value) { $this->assertArrayHasKey($key, $newCalendar); if (is_null($value)) { continue; } $this->assertEquals($value, $newCalendar[$key]); } $sccs = '{urn:ietf:params:xml:ns:caldav}supported-calendar-component-set'; $this->assertTrue($newCalendar[$sccs] instanceof Xml\Property\SupportedCalendarComponentSet); $this->assertEquals(['VEVENT', 'VTODO'], $newCalendar[$sccs]->getValue()); }
/** * Creates the backend * * @param \PDO $pdo * @param string $calendarTableName * @param string $calendarObjectTableName */ public function __construct(\PDO $pdo, $calendarTableName = 'calendars', $principalsTableName = 'principals', $calendarObjectTableName = 'calendarobjects', $calendarSharesTableName = 'calendarShares', $notificationsTableName = 'notifications') { parent::__construct($pdo, $calendarTableName, $calendarObjectTableName); $this->calendarSharesTableName = $calendarSharesTableName; $this->principalsTableName = $principalsTableName; $this->notificationsTableName = $notificationsTableName; }
/** * 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'], 'inviteStatus' => (int) $row['share_invitestatus'], 'properties' => !empty($row['share_displayname']) ? ['{DAV:}displayname' => $row['share_displayname']] : [], 'principal' => $row['principaluri']]); } return $result; }
/** * Performs a calendar-query on the contents of this calendar. * * The calendar-query is defined in RFC4791 : CalDAV. Using the * calendar-query it is possible for a client to request a specific set of * object, based on contents of iCalendar properties, date-ranges and * iCalendar component types (VTODO, VEVENT). * * This method should just return a list of (relative) urls that match this * query. * * The list of filters are specified as an array. The exact array is * documented by \Sabre\CalDAV\CalendarQueryParser. * * Note that it is extremely likely that getCalendarObject for every path * returned from this method will be called almost immediately after. You * may want to anticipate this to speed up these requests. * * This method provides a default implementation, which parses *all* the * iCalendar objects in the specified calendar. * * This default may well be good enough for personal use, and calendars * that aren't very large. But if you anticipate high usage, big calendars * or high loads, you are strongly adviced to optimize certain paths. * * The best way to do so is override this method and to optimize * specifically for 'common filters'. * * Requests that are extremely common are: * * requests for just VEVENTS * * requests for just VTODO * * requests with a time-range-filter on a VEVENT. * * ..and combinations of these requests. It may not be worth it to try to * handle every possible situation and just rely on the (relatively * easy to use) CalendarQueryValidator to handle the rest. * * Note that especially time-range-filters may be difficult to parse. A * time-range filter specified on a VEVENT must for instance also handle * recurrence rules correctly. * A good example of how to interprete all these filters can also simply * be found in \Sabre\CalDAV\CalendarQueryFilter. This class is as correct * as possible, so it gives you a good idea on what type of stuff you need * to think of. * * This specific implementation (for the PDO) backend optimizes filters on * specific components, and VEVENT time-ranges. * * @param string $calendarId * @param array $filters * @return array */ public function calendarQuery($calendarId, array $filters) { $result = array(); $validator = new \Sabre\CalDAV\CalendarQueryValidator(); $componentType = null; $requirePostFilter = true; $timeRange = null; // if no filters were specified, we don't need to filter after a query if (!$filters['prop-filters'] && !$filters['comp-filters']) { $requirePostFilter = false; } // Figuring out if there's a component filter if (count($filters['comp-filters']) > 0 && !$filters['comp-filters'][0]['is-not-defined']) { $componentType = $filters['comp-filters'][0]['name']; // Checking if we need post-filters if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['time-range'] && !$filters['comp-filters'][0]['prop-filters']) { $requirePostFilter = false; } // There was a time-range filter if ($componentType == 'VEVENT' && isset($filters['comp-filters'][0]['time-range'])) { $timeRange = $filters['comp-filters'][0]['time-range']; // If start time OR the end time is not specified, we can do a // 100% accurate mysql query. if (!$filters['prop-filters'] && !$filters['comp-filters'][0]['comp-filters'] && !$filters['comp-filters'][0]['prop-filters'] && (!$timeRange['start'] || !$timeRange['end'])) { $requirePostFilter = false; } } } if ($requirePostFilter) { $query = "SELECT uri, calendardata FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; } else { $query = "SELECT uri FROM " . $this->calendarObjectTableName . " WHERE calendarid = :calendarid"; } $values = array('calendarid' => $calendarId); if ($componentType) { $query .= " AND componenttype = :componenttype"; $values['componenttype'] = $componentType; } if ($timeRange && $timeRange['start']) { $query .= " AND lastoccurence > :startdate"; $values['startdate'] = $timeRange['start']->getTimeStamp(); } if ($timeRange && $timeRange['end']) { $query .= " AND firstoccurence < :enddate"; $values['enddate'] = $timeRange['end']->getTimeStamp(); } $stmt = $this->pdo->prepare($query); $stmt->execute($values); $result = array(); while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { if ($requirePostFilter) { if (!$this->validateFilterForObject($row, $filters)) { continue; } } $result[] = $row['uri']; } return $result; }
function setup() { if (!SABRE_HASSQLITE) { $this->markTestSkipped('SQLite driver is not available'); } if (file_exists(SABRE_TEMPDIR . '/testdb.sqlite')) { unlink(SABRE_TEMPDIR . '/testdb.sqlite'); } $pdo = new \PDO('sqlite:' . SABRE_TEMPDIR . '/testdb.sqlite'); $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); // Yup this is definitely not 'fool proof', but good enough for now. $queries = explode(';', file_get_contents(__DIR__ . '/../../../../examples/sql/sqlite.calendars.sql')); foreach ($queries as $query) { $pdo->exec($query); } $this->pdo = $pdo; }
/** * Delete a calendar and all it's objects * * @param string $calendarId * @return void */ public function deleteCalendar($calendarId) { parent::deleteCalendar($calendarId); $this->deleteCalendarShares($calendarId); }
/** * Delete a calendar and all it's objects * * @param string $calendarId * @return void */ public function deleteCalendar($calendarId) { \CApi::Log('deleteCalendar', \ELogLevel::Full, 'del-'); parent::deleteCalendar($calendarId); $this->deleteCalendarShares($calendarId); }
/** * Creates a new scheduling object. This should land in a users' inbox. * * @param string $principalUri * @param string $objectUri * @param string $objectData * @return void */ function createSchedulingObject($principalUri, $objectUri, $objectData) { $stmt = $this->pdo->prepare('INSERT INTO ' . $this->schedulingObjectTableName . ' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)'); $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]); }
function testSchedulingMethods() { $backend = new PDO($this->pdo); $calData = "BEGIN:VCALENDAR\r\nEND:VCALENDAR\r\n"; $backend->createSchedulingObject('principals/user1', 'schedule1.ics', $calData); $expected = ['calendardata' => $calData, 'uri' => 'schedule1.ics', 'etag' => '"' . md5($calData) . '"', 'size' => strlen($calData)]; $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); foreach ($expected as $k => $v) { $this->assertArrayHasKey($k, $result); $this->assertEquals($v, $result[$k]); } $results = $backend->getSchedulingObjects('principals/user1'); $this->assertEquals(1, count($results)); $result = $results[0]; foreach ($expected as $k => $v) { $this->assertEquals($v, $result[$k]); } $backend->deleteSchedulingObject('principals/user1', 'schedule1.ics'); $result = $backend->getSchedulingObject('principals/user1', 'schedule1.ics'); $this->assertNull($result); }
/** * @expectedException \Sabre\DAV\Exception\NotImplemented */ function testSetPublishStatus() { $backend = new PDO($this->pdo); $backend->setPublishStatus([1, 1], true); }
function testCalendarQueryTimeRangeNoEnd() { $backend = new PDO($this->pdo); $backend->createCalendarObject(1, "todo", "BEGIN:VCALENDAR\r\nBEGIN:VTODO\r\nEND:VTODO\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120101\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $backend->createCalendarObject(1, "event2", "BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDTSTART:20120103\r\nEND:VEVENT\r\nEND:VCALENDAR\r\n"); $filters = array('name' => 'VCALENDAR', 'comp-filters' => array(array('name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array('start' => new \DateTime('20120102'), 'end' => null))), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null); $this->assertEquals(array("event2"), $backend->calendarQuery(1, $filters)); }
/** * 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]); }