创建版本
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
<?php namespace Gdoo\Calendar\Sabre\Connector;
|
||||
|
||||
/**
|
||||
* This is an authentication backend that uses a file to manage passwords.
|
||||
*
|
||||
* The backend file must conform to Apache's htdigest format
|
||||
*
|
||||
* @package Sabre
|
||||
* @subpackage DAV
|
||||
* @copyright Copyright (C) 2007-2013 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
class Auth extends \Sabre\DAV\Auth\Backend\AbstractBasic
|
||||
{
|
||||
/**
|
||||
* Validates a username and password
|
||||
*
|
||||
* This method should return true or false depending on if login
|
||||
* succeeded.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateUserPass($username, $password)
|
||||
{
|
||||
if (\Auth::check()) {
|
||||
return true;
|
||||
} else {
|
||||
$credentials = [
|
||||
'username' => $username,
|
||||
'password' => $password
|
||||
];
|
||||
if (\Auth::attempt($credentials)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the currently logged in username.
|
||||
*
|
||||
* If nobody is currently logged in, this method should return null.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCurrentUser()
|
||||
{
|
||||
if (\Auth::id() > 0) {
|
||||
return \Auth::user()->username;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,649 @@
|
||||
<?php namespace Gdoo\Calendar\Sabre\Connector;
|
||||
|
||||
/**
|
||||
* PDO CalDAV backend
|
||||
*
|
||||
* This backend is used to store calendar-data in a PDO database, such as
|
||||
* sqlite or MySQL
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
use Gdoo\Index\Models\Share;
|
||||
use Gdoo\Calendar\Models\Calendar;
|
||||
use Gdoo\Calendar\Models\CalendarObject;
|
||||
|
||||
use Gdoo\Index\Services\ShareService;
|
||||
use Gdoo\Calendar\Services\CalendarService;
|
||||
use Gdoo\Calendar\Services\CalendarObjectService;
|
||||
use Illuminate\Support\Arr;
|
||||
|
||||
class CalDAV extends \Sabre\CalDAV\Backend\AbstractBackend
|
||||
{
|
||||
/**
|
||||
* We need to specify a max date, because we need to stop *somewhere*
|
||||
*
|
||||
* On 32 bit system the maximum for a signed integer is 2147483647, so
|
||||
* MAX_DATE cannot be higher than date('Y-m-d', 2147483647) which results
|
||||
* in 2038-01-19 to avoid problems when the date is converted
|
||||
* to a unix timestamp.
|
||||
*/
|
||||
const MAX_DATE = '2038-01-01';
|
||||
|
||||
/**
|
||||
* List of CalDAV properties, and how they map to database fieldnames
|
||||
* Add your own properties by simply adding on to this array.
|
||||
*
|
||||
* Note that only string-based properties are supported here.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $propertyMap = array(
|
||||
'{DAV:}displayname' => 'displayname',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-description' => 'description',
|
||||
'{urn:ietf:params:xml:ns:caldav}calendar-timezone' => 'timezone',
|
||||
'{http://apple.com/ns/ical/}calendar-order' => 'calendarorder',
|
||||
'{http://apple.com/ns/ical/}calendar-color' => 'calendarcolor',
|
||||
);
|
||||
|
||||
/**
|
||||
* 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, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * 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'.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri)
|
||||
{
|
||||
$fields = array_values($this->propertyMap);
|
||||
$fields[] = 'id';
|
||||
$fields[] = 'uri';
|
||||
$fields[] = 'ctag';
|
||||
$fields[] = 'components';
|
||||
$fields[] = 'principaluri';
|
||||
$fields[] = 'transparent';
|
||||
|
||||
$rows = Calendar::where('principaluri', $principalUri)->get($fields);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$components = array();
|
||||
if ($row->components) {
|
||||
$components = explode(',', $row->components);
|
||||
}
|
||||
|
||||
$calendar = array(
|
||||
'id' => $row->id,
|
||||
'uri' => $row->uri,
|
||||
'principaluri' => $row->principaluri,
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $row->ctag?$row->ctag:'0',
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Property\SupportedCalendarComponentSet($components),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new \Sabre\CalDAV\Property\ScheduleCalendarTransp($row->transparent?'transparent':'opaque'),
|
||||
);
|
||||
|
||||
foreach ($this->propertyMap as $xmlName => $dbName) {
|
||||
$calendar[$xmlName] = $row[$dbName];
|
||||
}
|
||||
$calendars[] = $calendar;
|
||||
}
|
||||
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
|
||||
*/
|
||||
public function createCalendar($principalUri, $calendarUri, array $properties)
|
||||
{
|
||||
$fieldNames = array(
|
||||
'principaluri',
|
||||
'uri',
|
||||
'ctag',
|
||||
'transparent',
|
||||
);
|
||||
$values = array(
|
||||
':principaluri' => $principalUri,
|
||||
':uri' => $calendarUri,
|
||||
':ctag' => 1,
|
||||
':transparent' => 0,
|
||||
);
|
||||
|
||||
$data = array(
|
||||
'principaluri' => $principalUri,
|
||||
'uri' => $calendarUri,
|
||||
'ctag' => 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';
|
||||
} else {
|
||||
if (!($properties[$sccs] instanceof \Sabre\CalDAV\Property\SupportedCalendarComponentSet)) {
|
||||
throw new \Sabre\DAV\Exception('The ' . $sccs . ' property must be of type: \Sabre\CalDAV\Property\SupportedCalendarComponentSet');
|
||||
}
|
||||
$values[':components'] = implode(',', $properties[$sccs]->getValue());
|
||||
}
|
||||
$transp = '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp';
|
||||
if (isset($properties[$transp])) {
|
||||
$values[':transparent'] = $properties[$transp]->getValue()==='transparent';
|
||||
}
|
||||
|
||||
foreach ($this->propertyMap as $xmlName => $dbName) {
|
||||
if (isset($properties[$xmlName])) {
|
||||
$values[':' . $dbName] = $properties[$xmlName];
|
||||
$fieldNames[] = $dbName;
|
||||
|
||||
$data[$dbName] = $properties[$xmlName];
|
||||
}
|
||||
}
|
||||
return Calendar::insertGetId($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates properties for a calendar.
|
||||
*
|
||||
* The mutations array uses the propertyName in clark-notation as key,
|
||||
* and the array value for the property value. In the case a property
|
||||
* should be deleted, the property value will be null.
|
||||
*
|
||||
* This method must be atomic. If one property cannot be changed, the
|
||||
* entire operation must fail.
|
||||
*
|
||||
* If the operation was successful, true can be returned.
|
||||
* If the operation failed, false can be returned.
|
||||
*
|
||||
* Deletion of a non-existent property is always successful.
|
||||
*
|
||||
* Lastly, it is optional to return detailed information about any
|
||||
* failures. In this case an array should be returned with the following
|
||||
* structure:
|
||||
*
|
||||
* array(
|
||||
* 403 => array(
|
||||
* '{DAV:}displayname' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}owner' => null,
|
||||
* )
|
||||
* )
|
||||
*
|
||||
* In this example it was forbidden to update {DAV:}displayname.
|
||||
* (403 Forbidden), which in turn also caused {DAV:}owner to fail
|
||||
* (424 Failed Dependency) because the request needs to be atomic.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param array $mutations
|
||||
* @return bool|array
|
||||
*/
|
||||
public function updateCalendar($calendarId, array $mutations)
|
||||
{
|
||||
$newValues = array();
|
||||
$result = array(
|
||||
200 => array(), // Ok
|
||||
403 => array(), // Forbidden
|
||||
424 => array(), // Failed Dependency
|
||||
);
|
||||
|
||||
$hasError = false;
|
||||
|
||||
foreach ($mutations as $propertyName => $propertyValue) {
|
||||
switch ($propertyName) {
|
||||
case '{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp':
|
||||
$fieldName = 'transparent';
|
||||
$newValues[$fieldName] = $propertyValue->getValue()==='transparent';
|
||||
break;
|
||||
default:
|
||||
// Checking the property map
|
||||
if (!isset($this->propertyMap[$propertyName])) {
|
||||
// We don't know about this property.
|
||||
$hasError = true;
|
||||
$result[403][$propertyName] = null;
|
||||
unset($mutations[$propertyName]);
|
||||
continue;
|
||||
}
|
||||
|
||||
$fieldName = $this->propertyMap[$propertyName];
|
||||
$newValues[$fieldName] = $propertyValue;
|
||||
}
|
||||
}
|
||||
|
||||
// If there were any errors we need to fail the request
|
||||
if ($hasError) {
|
||||
// Properties has the remaining properties
|
||||
foreach ($mutations as $propertyName => $propertyValue) {
|
||||
$result[424][$propertyName] = null;
|
||||
}
|
||||
|
||||
// Removing unused statuscodes for cleanliness
|
||||
foreach ($result as $status => $properties) {
|
||||
if (is_array($properties) && count($properties)===0) {
|
||||
unset($result[$status]);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// Success
|
||||
|
||||
// Now we're generating the sql query.
|
||||
Calendar::where('id', $calendarId)->update($newValues);
|
||||
CalendarService::touchCalendar($calendarId);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all it's objects
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId)
|
||||
{
|
||||
CalendarService::deleteCalendar($calendarId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all calendar objects within a calendar.
|
||||
*
|
||||
* Every item contains an array with the following keys:
|
||||
* * id - unique identifier which will be used for subsequent updates
|
||||
* * calendardata - The iCalendar-compatible calendar data
|
||||
* * uri - a unique key which will be used to construct the uri. This can be any arbitrary string.
|
||||
* * lastmodified - a timestamp of the last modification time
|
||||
* * etag - An arbitrary string, surrounded by double-quotes. (e.g.:
|
||||
* ' "abcdef"')
|
||||
* * calendarid - The calendarid as it was passed to this function.
|
||||
* * size - The size of the calendar objects, in bytes.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public function getCalendarObjects($calendarId)
|
||||
{
|
||||
$model = CalendarObject::select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size']);
|
||||
if ($calendarId == 'share-events') {
|
||||
$shared = ShareService::getItemsSourceBy(['event'], \Auth::id());
|
||||
if ($shared->isEmpty()) {
|
||||
return array();
|
||||
}
|
||||
$share_id = Arr::pluck($shared, 'source_id');
|
||||
$rows = $model->whereIn('id', $share_id)->get();
|
||||
foreach ($rows as $key => $row) {
|
||||
$rows[$key]['calendarid'] = 'share-events';
|
||||
}
|
||||
} elseif ($calendarId > 0) {
|
||||
$rows = $model->where('calendarid', $calendarId)->get();
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($rows as $row) {
|
||||
$result[] = array(
|
||||
'id' => $row->id,
|
||||
'uri' => $row->uri,
|
||||
'lastmodified' => $row->lastmodified,
|
||||
'etag' => '"' . $row->etag . '"',
|
||||
'calendarid' => $row->calendarid,
|
||||
'size' => (int)$row->size,
|
||||
);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information from a single calendar object, based on it's object
|
||||
* uri.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public function getCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
$model = CalendarObject::where('uri', $objectUri);
|
||||
if ($calendarId == 'share-events') {
|
||||
$shared = ShareService::getItemsSourceBy(['event'], \Auth::id());
|
||||
if ($shared->isEmpty()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$shareId = Arr::pluck($shared, 'source_id');
|
||||
$shareUsersList = Arr::pluck($shared, 'name', 'source_id');
|
||||
|
||||
$row = $model->whereIn('id', $shareId)->first();
|
||||
$row->calendarid = 'share-events';
|
||||
|
||||
$vcalendar = \Sabre\VObject\Reader::read($row->calendardata);
|
||||
$vcalendar->VEvent->SUMMARY->value = '['.$shareUsersList[$row->id].']'.$vcalendar->VEvent->SUMMARY->value;
|
||||
$row->calendardata = $vcalendar->serialize();
|
||||
} elseif ($calendarId > 0) {
|
||||
$row = $model->where('calendarid', $calendarId)->first();
|
||||
}
|
||||
|
||||
if (empty($row)) {
|
||||
return array();
|
||||
}
|
||||
return array(
|
||||
'id' => $row->id,
|
||||
'uri' => $row->uri,
|
||||
'lastmodified' => $row->lastmodified,
|
||||
'etag' => '"' . $row->etag . '"',
|
||||
'calendarid' => $row->calendarid,
|
||||
'size' => (int)$row->size,
|
||||
'calendardata' => $row->calendardata,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public function createCalendarObject($calendarId, $objectUri, $calendarData)
|
||||
{
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
$data = array(
|
||||
'calendarid' => $calendarId,
|
||||
'uri' => $objectUri,
|
||||
'calendardata' => $calendarData,
|
||||
'lastmodified' => time(),
|
||||
'etag' => $extraData['etag'],
|
||||
'size' => $extraData['size'],
|
||||
'componenttype' => $extraData['componentType'],
|
||||
'firstoccurence' => $extraData['firstOccurence'],
|
||||
'lastoccurence' => $extraData['lastOccurence'],
|
||||
);
|
||||
CalendarObject::insert($data);
|
||||
CalendarService::touchCalendar($calendarId);
|
||||
return '"' . $extraData['etag'] . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates an existing calendarobject, based on it's uri.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
public function updateCalendarObject($calendarId, $objectUri, $calendarData)
|
||||
{
|
||||
$extraData = $this->getDenormalizedData($calendarData);
|
||||
$update = array(
|
||||
'calendardata' => $calendarData,
|
||||
'lastmodified' => time(),
|
||||
'etag' => $extraData['etag'],
|
||||
'size' => $extraData['size'],
|
||||
'componenttype' => $extraData['componentType'],
|
||||
'firstoccurence' => $extraData['firstOccurence'],
|
||||
'lastoccurence' => $extraData['lastOccurence'],
|
||||
);
|
||||
CalendarObject::where('calendarid', $calendarId)->where('uri', $objectUri)->update($update);
|
||||
CalendarService::touchCalendar($calendarId);
|
||||
return '"' . $extraData['etag'] . '"';
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses some information from calendar objects, used for optimized
|
||||
* calendar-queries.
|
||||
*
|
||||
* Returns an array with the following keys:
|
||||
* * etag
|
||||
* * size
|
||||
* * componentType
|
||||
* * firstOccurence
|
||||
* * lastOccurence
|
||||
*
|
||||
* @param string $calendarData
|
||||
* @return array
|
||||
*/
|
||||
protected function getDenormalizedData($calendarData)
|
||||
{
|
||||
$vObject = \Sabre\VObject\Reader::read($calendarData);
|
||||
$componentType = null;
|
||||
$component = null;
|
||||
$firstOccurence = null;
|
||||
$lastOccurence = null;
|
||||
foreach ($vObject->getComponents() as $component) {
|
||||
if ($component->name!=='VTIMEZONE') {
|
||||
$componentType = $component->name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$componentType) {
|
||||
throw new \Sabre\DAV\Exception\BadRequest('Calendar objects must have a VJOURNAL, VEVENT or VTODO component');
|
||||
}
|
||||
if ($componentType === 'VEVENT') {
|
||||
$firstOccurence = $component->DTSTART->getDateTime()->getTimeStamp();
|
||||
// Finding the last occurence is a bit harder
|
||||
if (!isset($component->RRULE)) {
|
||||
if (isset($component->DTEND)) {
|
||||
$lastOccurence = $component->DTEND->getDateTime()->getTimeStamp();
|
||||
} elseif (isset($component->DURATION)) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->add(\Sabre\VObject\DateTimeParser::parse($component->DURATION->getValue()));
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} elseif (!$component->DTSTART->hasTime()) {
|
||||
$endDate = clone $component->DTSTART->getDateTime();
|
||||
$endDate->modify('+1 day');
|
||||
$lastOccurence = $endDate->getTimeStamp();
|
||||
} else {
|
||||
$lastOccurence = $firstOccurence;
|
||||
}
|
||||
} else {
|
||||
$it = new \Sabre\VObject\RecurrenceIterator($vObject, (string)$component->UID);
|
||||
$maxDate = new \DateTime(self::MAX_DATE);
|
||||
if ($it->isInfinite()) {
|
||||
$lastOccurence = $maxDate->getTimeStamp();
|
||||
} else {
|
||||
$end = $it->getDtEnd();
|
||||
while ($it->valid() && $end < $maxDate) {
|
||||
$end = $it->getDtEnd();
|
||||
$it->next();
|
||||
}
|
||||
$lastOccurence = $end->getTimeStamp();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
'etag' => md5($calendarData),
|
||||
'size' => strlen($calendarData),
|
||||
'componentType' => $componentType,
|
||||
'firstOccurence' => $firstOccurence,
|
||||
'lastOccurence' => $lastOccurence,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
CalendarObject::where('calendarid', $calendarId)->where('uri', $objectUri)->delete();
|
||||
CalendarService::touchCalendar($calendarId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
$model = CalendarObject::select(array('uri', 'calendardata'));
|
||||
} else {
|
||||
$model = CalendarObject::select(array('uri'));
|
||||
}
|
||||
|
||||
if ($calendarId == 'share-events') {
|
||||
$shared = ShareService::getItemsSourceBy(['event'], \Auth::id());
|
||||
$shareId = Arr::pluck($shared, 'source_id');
|
||||
$model->whereIn('id', $shareId);
|
||||
} else {
|
||||
$model->where('calendarid', $calendarId);
|
||||
}
|
||||
|
||||
if ($componentType) {
|
||||
$model->where('componenttype', $componentType);
|
||||
}
|
||||
|
||||
if ($timeRange && $timeRange['start']) {
|
||||
$model->where('lastoccurence', '>', $timeRange['start']->getTimeStamp());
|
||||
}
|
||||
if ($timeRange && $timeRange['end']) {
|
||||
$model->where('firstoccurence', '<', $timeRange['end']->getTimeStamp());
|
||||
}
|
||||
$rows = $model->get()->toArray();
|
||||
foreach ($rows as $row) {
|
||||
if ($requirePostFilter) {
|
||||
if (!$this->validateFilterForObject($row, $filters)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$result[] = $row['uri'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,406 @@
|
||||
<?php namespace Gdoo\Calendar\Sabre\Connector;
|
||||
|
||||
use Sabre\DAV;
|
||||
use Sabre\DAVACL;
|
||||
|
||||
/**
|
||||
* PDO principal backend
|
||||
*
|
||||
*
|
||||
* This backend assumes all principals are in a single collection. The default collection
|
||||
* is 'principals/', but this can be overriden.
|
||||
*
|
||||
* @copyright Copyright (C) 2007-2014 fruux GmbH (https://fruux.com/).
|
||||
* @author Evert Pot (http://evertpot.com/)
|
||||
* @license http://code.google.com/p/sabredav/wiki/License Modified BSD License
|
||||
*/
|
||||
|
||||
use Gdoo\User\Models\User;
|
||||
|
||||
class Principal extends DAVACL\PrincipalBackend\AbstractBackend
|
||||
{
|
||||
|
||||
/**
|
||||
* pdo
|
||||
*
|
||||
* @var PDO
|
||||
*/
|
||||
protected $pdo;
|
||||
|
||||
/**
|
||||
* PDO table name for 'principals'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $tableName;
|
||||
|
||||
/**
|
||||
* PDO table name for 'group members'
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $groupMembersTableName;
|
||||
|
||||
/**
|
||||
* A list of additional fields to support
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fieldMap = array(
|
||||
|
||||
/**
|
||||
* This property can be used to display the users' real name.
|
||||
*/
|
||||
'{DAV:}displayname' => array(
|
||||
'dbField' => 'displayname',
|
||||
),
|
||||
|
||||
/**
|
||||
* This property is actually used by the CardDAV plugin, where it gets
|
||||
* mapped to {http://calendarserver.orgi/ns/}me-card.
|
||||
*
|
||||
* The reason we don't straight-up use that property, is because
|
||||
* me-card is defined as a property on the users' addressbook
|
||||
* collection.
|
||||
*/
|
||||
'{http://sabredav.org/ns}vcard-url' => array(
|
||||
'dbField' => 'vcardurl',
|
||||
),
|
||||
/**
|
||||
* This is the users' primary email-address.
|
||||
*/
|
||||
'{http://sabredav.org/ns}email-address' => array(
|
||||
'dbField' => 'email',
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns a list of principals based on a prefix.
|
||||
*
|
||||
* This prefix will often contain something like 'principals'. You are only
|
||||
* expected to return principals that are in this base path.
|
||||
*
|
||||
* You are expected to return at least a 'uri' for every user, you can
|
||||
* return any additional properties if you wish so. Common properties are:
|
||||
* {DAV:}displayname
|
||||
* {http://sabredav.org/ns}email-address - This is a custom SabreDAV
|
||||
* field that's actualy injected in a number of other properties. If
|
||||
* you have an email address, use this property.
|
||||
*
|
||||
* @param string $prefixPath
|
||||
* @return array
|
||||
*/
|
||||
public function getPrincipalsByPrefix($prefixPath)
|
||||
{
|
||||
if ($prefixPath == 'principals') {
|
||||
$users = User::where('status', 1)
|
||||
->where('id', \Auth::id())
|
||||
->get(['id', 'username', 'name', 'email']);
|
||||
|
||||
foreach ($users as $user) {
|
||||
$principals[] = array(
|
||||
'id' => $user['id'],
|
||||
'uri' => 'principals/'.$user['username'],
|
||||
'{DAV:}displayname' => $user['name'],
|
||||
'{http://sabredav.org/ns}email-address' => $user['email'],
|
||||
);
|
||||
}
|
||||
}
|
||||
return $principals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a specific principal, specified by it's path.
|
||||
* The returned structure should be the exact same as from
|
||||
* getPrincipalsByPrefix.
|
||||
*
|
||||
* @param string $path
|
||||
* @return array
|
||||
*/
|
||||
public function getPrincipalByPath($path)
|
||||
{
|
||||
list($prefix, $username) = explode('/', $path);
|
||||
|
||||
$user = User::where('username', $username)->first(['id', 'username', 'name', 'email']);
|
||||
|
||||
if ($prefix == 'principals') {
|
||||
return array(
|
||||
'id' => $user['id'],
|
||||
'uri' => 'principals/'.$user['username'],
|
||||
'{DAV:}displayname' => $user['name'],
|
||||
'{http://sabredav.org/ns}email-address' => $user['email'],
|
||||
);
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
'id',
|
||||
'uri',
|
||||
);
|
||||
|
||||
foreach ($this->fieldMap as $key => $value) {
|
||||
$fields[] = $value['dbField'];
|
||||
}
|
||||
$stmt = $this->pdo->prepare('SELECT '.implode(',', $fields).' FROM '. $this->tableName . ' WHERE uri = ?');
|
||||
$stmt->execute(array($path));
|
||||
|
||||
$row = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
if (!$row) {
|
||||
return;
|
||||
}
|
||||
|
||||
$principal = array(
|
||||
'id' => $row['id'],
|
||||
'uri' => $row['uri'],
|
||||
);
|
||||
foreach ($this->fieldMap as $key => $value) {
|
||||
if ($row[$value['dbField']]) {
|
||||
$principal[$key] = $row[$value['dbField']];
|
||||
}
|
||||
}
|
||||
return $principal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates one ore more webdav properties on a principal.
|
||||
*
|
||||
* The list of mutations is supplied as an array. Each key in the array is
|
||||
* a propertyname, such as {DAV:}displayname.
|
||||
*
|
||||
* Each value is the actual value to be updated. If a value is null, it
|
||||
* must be deleted.
|
||||
*
|
||||
* This method should be atomic. It must either completely succeed, or
|
||||
* completely fail. Success and failure can simply be returned as 'true' or
|
||||
* 'false'.
|
||||
*
|
||||
* It is also possible to return detailed failure information. In that case
|
||||
* an array such as this should be returned:
|
||||
*
|
||||
* array(
|
||||
* 200 => array(
|
||||
* '{DAV:}prop1' => null,
|
||||
* ),
|
||||
* 201 => array(
|
||||
* '{DAV:}prop2' => null,
|
||||
* ),
|
||||
* 403 => array(
|
||||
* '{DAV:}prop3' => null,
|
||||
* ),
|
||||
* 424 => array(
|
||||
* '{DAV:}prop4' => null,
|
||||
* ),
|
||||
* );
|
||||
*
|
||||
* In this previous example prop1 was successfully updated or deleted, and
|
||||
* prop2 was succesfully created.
|
||||
*
|
||||
* prop3 failed to update due to '403 Forbidden' and because of this prop4
|
||||
* also could not be updated with '424 Failed dependency'.
|
||||
*
|
||||
* This last example was actually incorrect. While 200 and 201 could appear
|
||||
* in 1 response, if there's any error (403) the other properties should
|
||||
* always fail with 423 (failed dependency).
|
||||
*
|
||||
* But anyway, if you don't want to scratch your head over this, just
|
||||
* return true or false.
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $mutations
|
||||
* @return array|bool
|
||||
*/
|
||||
public function updatePrincipal($path, $mutations)
|
||||
{
|
||||
$updateAble = array();
|
||||
foreach ($mutations as $key => $value) {
|
||||
|
||||
// We are not aware of this field, we must fail.
|
||||
if (!isset($this->fieldMap[$key])) {
|
||||
$response = array(
|
||||
403 => array(
|
||||
$key => null,
|
||||
),
|
||||
424 => array(),
|
||||
);
|
||||
|
||||
// Adding the rest to the response as a 424
|
||||
foreach ($mutations as $subKey => $subValue) {
|
||||
if ($subKey !== $key) {
|
||||
$response[424][$subKey] = null;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
$updateAble[$this->fieldMap[$key]['dbField']] = $value;
|
||||
}
|
||||
|
||||
// No fields to update
|
||||
$query = "UPDATE " . $this->tableName . " SET ";
|
||||
|
||||
$first = true;
|
||||
foreach ($updateAble as $key => $value) {
|
||||
if (!$first) {
|
||||
$query.= ', ';
|
||||
}
|
||||
$first = false;
|
||||
$query.= "$key = :$key ";
|
||||
}
|
||||
$query.='WHERE uri = :uri';
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$updateAble['uri'] = $path;
|
||||
$stmt->execute($updateAble);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to search for principals matching a set of
|
||||
* properties.
|
||||
*
|
||||
* This search is specifically used by RFC3744's principal-property-search
|
||||
* REPORT. You should at least allow searching on
|
||||
* http://sabredav.org/ns}email-address.
|
||||
*
|
||||
* The actual search should be a unicode-non-case-sensitive search. The
|
||||
* keys in searchProperties are the WebDAV property names, while the values
|
||||
* are the property values to search on.
|
||||
*
|
||||
* If multiple properties are being searched on, the search should be
|
||||
* AND'ed.
|
||||
*
|
||||
* This method should simply return an array with full principal uri's.
|
||||
*
|
||||
* If somebody attempted to search on a property the backend does not
|
||||
* support, you should simply return 0 results.
|
||||
*
|
||||
* You can also just return 0 results if you choose to not support
|
||||
* searching at all, but keep in mind that this may stop certain features
|
||||
* from working.
|
||||
*
|
||||
* @param string $prefixPath
|
||||
* @param array $searchProperties
|
||||
* @return array
|
||||
*/
|
||||
public function searchPrincipals($prefixPath, array $searchProperties)
|
||||
{
|
||||
$query = 'SELECT uri FROM ' . $this->tableName . ' WHERE 1=1 ';
|
||||
$values = array();
|
||||
foreach ($searchProperties as $property => $value) {
|
||||
switch ($property) {
|
||||
case '{DAV:}displayname':
|
||||
$query.=' AND displayname LIKE ?';
|
||||
$values[] = '%' . $value . '%';
|
||||
break;
|
||||
case '{http://sabredav.org/ns}email-address':
|
||||
$query.=' AND email LIKE ?';
|
||||
$values[] = '%' . $value . '%';
|
||||
break;
|
||||
default:
|
||||
// Unsupported property
|
||||
return array();
|
||||
}
|
||||
}
|
||||
$stmt = $this->pdo->prepare($query);
|
||||
$stmt->execute($values);
|
||||
|
||||
$principals = array();
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
// Checking if the principal is in the prefix
|
||||
list($rowPrefix) = DAV\URLUtil::splitPath($row['uri']);
|
||||
if ($rowPrefix !== $prefixPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$principals[] = $row['uri'];
|
||||
}
|
||||
|
||||
return $principals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of members for a group-principal
|
||||
*
|
||||
* @param string $principal
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMemberSet($principal)
|
||||
{
|
||||
$principal = $this->getPrincipalByPath($principal);
|
||||
if (!$principal) {
|
||||
throw new DAV\Exception('Principal not found');
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
|
||||
$stmt->execute(array($principal['id']));
|
||||
|
||||
$result = array();
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row['uri'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of groups a principal is a member of
|
||||
*
|
||||
* @param string $principal
|
||||
* @return array
|
||||
*/
|
||||
public function getGroupMembership($principal)
|
||||
{
|
||||
return array();
|
||||
|
||||
$principal = $this->getPrincipalByPath($principal);
|
||||
if (!$principal) {
|
||||
throw new DAV\Exception('Principal not found');
|
||||
}
|
||||
|
||||
$stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM '.$this->groupMembersTableName.' AS groupmembers LEFT JOIN '.$this->tableName.' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
|
||||
$stmt->execute(array($principal['id']));
|
||||
|
||||
$result = array();
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$result[] = $row['uri'];
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the list of group members for a group principal.
|
||||
*
|
||||
* The principals should be passed as a list of uri's.
|
||||
*
|
||||
* @param string $principal
|
||||
* @param array $members
|
||||
* @return void
|
||||
*/
|
||||
public function setGroupMemberSet($principal, array $members)
|
||||
{
|
||||
// Grabbing the list of principal id's.
|
||||
$stmt = $this->pdo->prepare('SELECT id, uri FROM '.$this->tableName.' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
|
||||
$stmt->execute(array_merge(array($principal), $members));
|
||||
|
||||
$memberIds = array();
|
||||
$principalId = null;
|
||||
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
if ($row['uri'] == $principal) {
|
||||
$principalId = $row['id'];
|
||||
} else {
|
||||
$memberIds[] = $row['id'];
|
||||
}
|
||||
}
|
||||
if (!$principalId) {
|
||||
throw new DAV\Exception('Principal not found');
|
||||
}
|
||||
|
||||
// Wiping out old members
|
||||
$stmt = $this->pdo->prepare('DELETE FROM '.$this->groupMembersTableName.' WHERE principal_id = ?;');
|
||||
$stmt->execute(array($principalId));
|
||||
|
||||
foreach ($memberIds as $memberId) {
|
||||
$stmt = $this->pdo->prepare('INSERT INTO '.$this->groupMembersTableName.' (principal_id, member_id) VALUES (?, ?);');
|
||||
$stmt->execute(array($principalId, $memberId));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
<?php namespace Gdoo\Calendar\Sabre\Connector\Share;
|
||||
|
||||
use Gdoo\Calendar\Sabre\Connector;
|
||||
|
||||
use Auth;
|
||||
|
||||
use Gdoo\Calendar\Models\Calendar;
|
||||
use Gdoo\Index\Services\ShareService;
|
||||
use Gdoo\Calendar\Services\CalendarService;
|
||||
|
||||
class CalDAV extends Connector\CalDAV implements \Sabre\CalDAV\Backend\SharingSupport
|
||||
{
|
||||
/**
|
||||
* List of properties for the calendar shares table
|
||||
* This list maps exactly to the field names in the db table
|
||||
*/
|
||||
public $sharesProperties = array(
|
||||
'calendarId',
|
||||
'member',
|
||||
'status',
|
||||
'readOnly',
|
||||
'summary',
|
||||
//'displayName',
|
||||
//'colour'
|
||||
);
|
||||
|
||||
public $sharedEvents = array(
|
||||
'displayname' => '共享事件',
|
||||
'color' => '#999999',
|
||||
);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return void
|
||||
*/
|
||||
public function updateShares($calendarId, array $add, array $remove)
|
||||
{
|
||||
$fields = array();
|
||||
$fields[':calendarId'] = $calendarId;
|
||||
// get the principal based on the supplied email address
|
||||
$principal = $this->getPrincipalByEmail($add[0]['href']);
|
||||
$fields[':member'] = $principal['id'];
|
||||
$fields[':status'] = \Sabre\CalDAV\SharingPlugin::STATUS_NORESPONSE;
|
||||
// check we have all the required fields
|
||||
foreach ($this->sharesProperties as $field) {
|
||||
if (isset($add[0][$field])) {
|
||||
$fields[':'.$field] = $add[0][$field];
|
||||
}
|
||||
}
|
||||
$stmt = $this->pdo->prepare("INSERT INTO ".$this->calendarSharesTableName." (".implode(', ', $this->sharesProperties).") VALUES (".implode(', ', array_keys($fields)).")");
|
||||
$stmt->execute($fields);
|
||||
|
||||
// are we removing any shares?
|
||||
if (count($remove)>0) {
|
||||
$r_ids = array();
|
||||
foreach ($remove as $r_mailto) {
|
||||
// get the principalid
|
||||
$r_principal = $this->getPrincipalByEmail($r_mailto);
|
||||
$r_ids[] = $r_principal['id'];
|
||||
}
|
||||
$stmt = $this->pdo->prepare("DELETE FROM ".$this->calendarSharesTableName." WHERE MEMBER = ?");
|
||||
$stmt->execute($r_ids);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getShares($calendarId)
|
||||
{
|
||||
|
||||
//$fields = implode(',', $this->sharesProperties);
|
||||
//$shareRows = Share::getItemsSourceId('calendar',$calendarId);
|
||||
|
||||
//print_r($shareRows);
|
||||
//$stmt = $this->pdo->prepare("SELECT * FROM ".$this->calendarSharesTableName." AS calendarShares LEFT JOIN ".$this->principalsTableName." AS principals ON calendarShares.member = principals.id WHERE calendarShares.calendarId = ? ORDER BY calendarShares.calendarId ASC");
|
||||
//$stmt->execute(array($calendarId));
|
||||
|
||||
$shareRows = array();
|
||||
|
||||
$shares = array();
|
||||
foreach ($shareRows as $row) {
|
||||
//while($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
$share = array(
|
||||
'calendarId' => $row['calendarId'],
|
||||
'principalPath' => $row['uri'],
|
||||
'readOnly' => $row['readonly'],
|
||||
'summary' => $row['summary'],
|
||||
'href' => $row['email'],
|
||||
'commonName' => $row['displayname'],
|
||||
'displayName' => $row['displayname'],
|
||||
'status' => $row['status']
|
||||
/*
|
||||
'colour'=>$row['colour'],
|
||||
'displayName'=>$row['displayName'],
|
||||
*/
|
||||
);
|
||||
// map the status integer to a predefined constant
|
||||
/*
|
||||
switch($row['status']) {
|
||||
case 1:
|
||||
$share['status'] = 'STATUS_ACCEPTED';
|
||||
break;
|
||||
case 2:
|
||||
$share['status'] = 'STATUS_DECLINED';
|
||||
break;
|
||||
case 3:
|
||||
$share['status'] = 'STATUS_DELETED';
|
||||
break;
|
||||
case 4:
|
||||
$share['status'] = 'STATUS_NORESPONSE';
|
||||
break;
|
||||
case 5:
|
||||
$share['status'] = 'STATUS_INVALID';
|
||||
break;
|
||||
}*/
|
||||
// add it to main array
|
||||
$shares[] = $share;
|
||||
}
|
||||
return $shares;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, which the basename of the uri with which the calendar is
|
||||
* accessed.
|
||||
* * 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'.
|
||||
*
|
||||
* MODIFIED: THIS METHOD NOW NEEDS TO BE ABLE TO RETRIEVE SHARED CALENDARS
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri)
|
||||
{
|
||||
// get the principal id
|
||||
$principalBackend = $this->getPrincipalBackend();
|
||||
$principal = $principalBackend->getPrincipalByPath($principalUri);
|
||||
|
||||
$fields = array_values($this->propertyMap);
|
||||
$fields[] = 'id';
|
||||
$fields[] = 'uri';
|
||||
$fields[] = 'ctag';
|
||||
$fields[] = 'components';
|
||||
$fields[] = 'principaluri';
|
||||
$fields[] = 'transparent';
|
||||
|
||||
$uri = 'principals/'.$principal['id'];
|
||||
|
||||
// Making fields a comma-delimited list
|
||||
$rows = Calendar::where('principaluri', $uri)->get($fields);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$components = array();
|
||||
if ($row->components) {
|
||||
$components = explode(',', $row->components);
|
||||
}
|
||||
$calendar = array(
|
||||
'id' => $row->id,
|
||||
'uri' => $row->uri,
|
||||
'principaluri' => $principalUri,
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $row->ctag ? $row->ctag : '0',
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Property\SupportedCalendarComponentSet($components),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new \Sabre\CalDAV\Property\ScheduleCalendarTransp($row->transparent ? 'transparent' : 'opaque'),
|
||||
);
|
||||
foreach ($this->propertyMap as $xmlName => $dbName) {
|
||||
$calendar[$xmlName] = $row[$dbName];
|
||||
}
|
||||
$calendars[] = $calendar;
|
||||
}
|
||||
|
||||
// now let's get any shared calendars
|
||||
$shareFields = implode(', ', $this->sharesProperties);
|
||||
$shareRows = ShareService::getItemsSourceBy(['calendar'], $principal['id']);
|
||||
|
||||
foreach ($shareRows as $shareRow) {
|
||||
// get the original calendar
|
||||
$calendarShareRow = CalendarService::getCalendar($shareRow['source_id'], false);
|
||||
|
||||
$shareComponents = array();
|
||||
if ($calendarShareRow['components']) {
|
||||
$shareComponents = explode(',', $calendarShareRow['components']);
|
||||
}
|
||||
|
||||
$sharedCalendar = array(
|
||||
'id' => $calendarShareRow['id'],
|
||||
'uri' => $calendarShareRow['uri'],
|
||||
'principaluri' => $principalUri,
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALENDARSERVER . '}getctag' => $calendarShareRow['ctag']?$calendarShareRow['ctag']:'0',
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}supported-calendar-component-set' => new \Sabre\CalDAV\Property\SupportedCalendarComponentSet($shareComponents),
|
||||
'{' . \Sabre\CalDAV\Plugin::NS_CALDAV . '}schedule-calendar-transp' => new \Sabre\CalDAV\Property\ScheduleCalendarTransp($calendarShareRow['transparent']?'transparent':'opaque'),
|
||||
);
|
||||
|
||||
// some specific properies for shared calendars
|
||||
$shareRow['displayname'] = '['.$shareRow['name'].']'.$calendarShareRow['displayname'];
|
||||
$sharedCalendar['{http://calendarserver.org/ns/}shared-url'] = $calendarShareRow['uri'];
|
||||
$sharedCalendar['{http://sabredav.org/ns}owner-principal'] = $calendarShareRow['principaluri'];
|
||||
$sharedCalendar['{http://sabredav.org/ns}read-only'] = true;
|
||||
$sharedCalendar['{http://calendarserver.org/ns/}summary'] = $shareRow['summary'];
|
||||
|
||||
foreach ($this->propertyMap as $xmlName => $dbName) {
|
||||
if ($xmlName == '{DAV:}displayname') {
|
||||
$sharedCalendar[$xmlName] = $shareRow['displayname'] == null ? $calendarShareRow['displayname'] : $shareRow['displayname'];
|
||||
} elseif ($xmlName == '{http://apple.com/ns/ical/}calendar-color') {
|
||||
$sharedCalendar[$xmlName] = $shareRow['colour'] == null ? $calendarShareRow['calendarcolor'] : $shareRow['color'];
|
||||
} else {
|
||||
$sharedCalendar[$xmlName] = $calendarShareRow[$dbName];
|
||||
}
|
||||
}
|
||||
$calendars[] = $sharedCalendar;
|
||||
}
|
||||
|
||||
// 获取分享记录
|
||||
$shareSource = ShareService::getItemsSourceBy(['event'], Auth::id());
|
||||
$sharedEvent = array(
|
||||
'id' => 'share-events',
|
||||
'uri' => 'share-events',
|
||||
'principaluri' => $principalUri,
|
||||
'{'.\Sabre\CalDAV\Plugin::NS_CALENDARSERVER.'}getctag' => count($shareSource),
|
||||
'{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new \Sabre\CalDAV\Property\SupportedCalendarComponentSet(array('VEVENT')),
|
||||
'{'.\Sabre\CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp' => new \Sabre\CalDAV\Property\ScheduleCalendarTransp('opaque'),
|
||||
);
|
||||
$sharedEvent['{http://calendarserver.org/ns/}shared-url'] = 'share-events';
|
||||
$sharedEvent['{http://sabredav.org/ns}owner-principal'] = 'principals/owner';
|
||||
$sharedEvent['{http://sabredav.org/ns}read-only'] = true;
|
||||
$sharedEvent['{DAV:}displayname'] = $this->sharedEvents['displayname'];
|
||||
$calendars[] = $sharedEvent;
|
||||
|
||||
return $calendars;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public function shareReply($href, $status, $calendarUri, $inReplyTo, $summary = null)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this calendar as published.
|
||||
*
|
||||
* Publishing a calendar should automatically create a read-only, public,
|
||||
* subscribable calendar.
|
||||
*
|
||||
* @param bool $value
|
||||
* @return void
|
||||
*/
|
||||
public function setPublishStatus($calendarId, $value)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of notifications for a given principal url.
|
||||
*
|
||||
* The returned array should only consist of implementations of
|
||||
* \Sabre\CalDAV\Notifications\INotificationType.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @return array
|
||||
*/
|
||||
public function getNotificationsForPrincipal($principalUri)
|
||||
{
|
||||
// get ALL notifications for the user NB. Any read or out of date notifications should be already deleted.
|
||||
$stmt = $this->pdo->prepare("SELECT * FROM ".$this->notificationsTableName." WHERE principaluri = ? ORDER BY dtstamp ASC");
|
||||
$stmt->execute(array($principalUri));
|
||||
|
||||
$notifications = array();
|
||||
while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
|
||||
// we need to return the correct type of notification
|
||||
switch ($row['notification']) {
|
||||
case 'Invite':
|
||||
$values = array();
|
||||
// sort out the required data
|
||||
if ($row['id']) {
|
||||
$values['id'] = $row['id'];
|
||||
}
|
||||
if ($row['etag']) {
|
||||
$values['etag'] = $row['etag'];
|
||||
}
|
||||
if ($row['principalUri']) {
|
||||
$values['href'] = $row['principalUri'];
|
||||
}
|
||||
if ($row['dtstamp']) {
|
||||
$values['dtstamp'] = $row['dtstamp'];
|
||||
}
|
||||
if ($row['type']) {
|
||||
$values['type'] = $row['type'];
|
||||
}
|
||||
if ($row['readOnly']) {
|
||||
$values['readOnly'] = $row['readOnly'];
|
||||
}
|
||||
if ($row['hostUrl']) {
|
||||
$values['hostUrl'] = $row['hostUrl'];
|
||||
}
|
||||
if ($row['organizer']) {
|
||||
$values['organizer'] = $row['organizer'];
|
||||
}
|
||||
if ($row['commonName']) {
|
||||
$values['commonName'] = $row['commonName'];
|
||||
}
|
||||
if ($row['firstName']) {
|
||||
$values['firstName'] = $row['firstName'];
|
||||
}
|
||||
if ($row['lastName']) {
|
||||
$values['lastName'] = $row['lastName'];
|
||||
}
|
||||
if ($row['summary']) {
|
||||
$values['summary'] = $row['summary'];
|
||||
}
|
||||
$notifications[] = new \Sabre\CalDAV\Notifications\Notification\Invite($values);
|
||||
break;
|
||||
|
||||
case 'InviteReply':
|
||||
break;
|
||||
case 'SystemStatus':
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* This deletes a specific notifcation.
|
||||
*
|
||||
* This may be called by a client once it deems a notification handled.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param \Sabre\CalDAV\Notifications\INotificationType $notification
|
||||
* @return void
|
||||
*/
|
||||
public function deleteNotification($principalUri, \Sabre\CalDAV\Notifications\INotificationType $notification)
|
||||
{
|
||||
}
|
||||
|
||||
private function getPrincipalByEmail($email)
|
||||
{
|
||||
$principalBackend = $this->getPrincipalBackend();
|
||||
$principalPath = $principalBackend->searchPrincipals('principals/users', array('{http://sabredav.org/ns}email-address'=>$email));
|
||||
if ($principalPath == 0) {
|
||||
throw new \Exception("Unknown email address");
|
||||
}
|
||||
// use the path to get the principal
|
||||
return $principalBackend->getPrincipalByPath($principalPath);
|
||||
}
|
||||
|
||||
private function getPrincipalBackend()
|
||||
{
|
||||
return new \Gdoo\Calendar\Sabre\Connector\Principal();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user