refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\DAV\Backend\CalDAV;
|
||||
|
||||
use Sabre\DAV\Server as SabreServer;
|
||||
use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||
use Sabre\DAV\Sync\Plugin as DAVSyncPlugin;
|
||||
use App\Http\Controllers\DAV\Backend\IDAVBackend;
|
||||
use App\Http\Controllers\DAV\Backend\SyncDAVBackend;
|
||||
use App\Http\Controllers\DAV\DAVACL\PrincipalBackend;
|
||||
|
||||
abstract class AbstractCalDAVBackend implements ICalDAVBackend, IDAVBackend
|
||||
{
|
||||
use SyncDAVBackend;
|
||||
|
||||
/**
|
||||
* Get description array.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDescription()
|
||||
{
|
||||
$token = DAVSyncPlugin::SYNCTOKEN_PREFIX.$this->refreshSyncToken(null)->id;
|
||||
|
||||
return [
|
||||
'id' => $this->backendUri(),
|
||||
'uri' => $this->backendUri(),
|
||||
'principaluri' => PrincipalBackend::getPrincipalUser($this->user),
|
||||
'{DAV:}sync-token' => $token,
|
||||
'{'.SabreServer::NS_SABREDAV.'}sync-token' => $token,
|
||||
'{'.CalDAVPlugin::NS_CALENDARSERVER.'}getctag' => $token,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new exported version of the object.
|
||||
*
|
||||
* @param mixed $obj
|
||||
* @return string
|
||||
*/
|
||||
abstract protected function refreshObject($obj): string;
|
||||
}
|
||||
356
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBackend.php
Normal file
356
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBackend.php
Normal file
@@ -0,0 +1,356 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\DAV\Backend\CalDAV;
|
||||
|
||||
use Sabre\DAV;
|
||||
use App\Traits\WithUser;
|
||||
use Sabre\CalDAV\Backend\SyncSupport;
|
||||
use Sabre\CalDAV\Backend\AbstractBackend;
|
||||
|
||||
class CalDAVBackend extends AbstractBackend implements SyncSupport
|
||||
{
|
||||
use WithUser;
|
||||
|
||||
/**
|
||||
* Set the Calendar backends.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getBackends(): array
|
||||
{
|
||||
return [
|
||||
app(CalDAVBirthdays::class)->init($this->user),
|
||||
app(CalDAVTasks::class)->init($this->user),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backend for this id.
|
||||
*
|
||||
* @return AbstractCalDAVBackend|null
|
||||
*/
|
||||
private function getBackend($id)
|
||||
{
|
||||
return collect($this->getBackends())->first(function ($backend) use ($id) {
|
||||
return $backend->backendUri() === $id;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 is 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'.
|
||||
*
|
||||
* 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\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
|
||||
*/
|
||||
public function getCalendarsForUser($principalUri)
|
||||
{
|
||||
return array_map(function ($backend) {
|
||||
return $backend->getDescription();
|
||||
}, $this->getBackends());
|
||||
}
|
||||
|
||||
/**
|
||||
* The getChanges method returns all the changes that have happened, since
|
||||
* the specified syncToken in the specified calendar.
|
||||
*
|
||||
* This function should return an array, such as the following:
|
||||
*
|
||||
* [
|
||||
* 'syncToken' => 'The current synctoken',
|
||||
* 'added' => [
|
||||
* 'new.txt',
|
||||
* ],
|
||||
* 'modified' => [
|
||||
* 'modified.txt',
|
||||
* ],
|
||||
* 'deleted' => [
|
||||
* 'foo.php.bak',
|
||||
* 'old.txt'
|
||||
* ]
|
||||
* );
|
||||
*
|
||||
* The returned syncToken property should reflect the *current* syncToken
|
||||
* of the calendar, as reported in the {http://sabredav.org/ns}sync-token
|
||||
* property This is * needed here too, to ensure the operation is atomic.
|
||||
*
|
||||
* If the $syncToken argument is specified as null, this is an initial
|
||||
* sync, and all members should be reported.
|
||||
*
|
||||
* The modified property is an array of nodenames that have changed since
|
||||
* the last token.
|
||||
*
|
||||
* The deleted property is an array with nodenames, that have been deleted
|
||||
* from collection.
|
||||
*
|
||||
* The $syncLevel argument is basically the 'depth' of the report. If it's
|
||||
* 1, you only have to report changes that happened only directly in
|
||||
* immediate descendants. If it's 2, it should also include changes from
|
||||
* the nodes below the child collections. (grandchildren)
|
||||
*
|
||||
* The $limit argument allows a client to specify how many results should
|
||||
* be returned at most. If the limit is not specified, it should be treated
|
||||
* as infinite.
|
||||
*
|
||||
* If the limit (infinite or not) is higher than you're willing to return,
|
||||
* you should throw a Sabre\DAV\Exception\TooMuchMatches() exception.
|
||||
*
|
||||
* If the syncToken is expired (due to data cleanup) or unknown, you must
|
||||
* return null.
|
||||
*
|
||||
* The limit is 'suggestive'. You are free to ignore it.
|
||||
*
|
||||
* @param string $calendarId
|
||||
* @param string $syncToken
|
||||
* @param int $syncLevel
|
||||
* @param int $limit
|
||||
* @return array
|
||||
*/
|
||||
public function getChangesForCalendar($calendarId, $syncToken, $syncLevel, $limit = null)
|
||||
{
|
||||
$backend = $this->getBackend($calendarId);
|
||||
if ($backend) {
|
||||
return $backend->getChanges($calendarId, $syncToken);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 mixed $calendarId
|
||||
* @return array
|
||||
*/
|
||||
public function getCalendarObjects($calendarId)
|
||||
{
|
||||
$backend = $this->getBackend($calendarId);
|
||||
if ($backend) {
|
||||
$objs = $backend->getObjects($calendarId);
|
||||
|
||||
return $objs
|
||||
->map(function ($date) use ($backend) {
|
||||
return $backend->prepareData($date);
|
||||
})
|
||||
->filter(function ($event) {
|
||||
return $event !== null;
|
||||
})
|
||||
->toArray();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @return array|null
|
||||
*/
|
||||
public function getCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
$backend = $this->getBackend($calendarId);
|
||||
if ($backend) {
|
||||
$obj = $backend->getObject($calendarId, $objectUri);
|
||||
|
||||
if ($obj) {
|
||||
return $backend->prepareData($obj);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* It is possible to 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)
|
||||
{
|
||||
return $this->updateCalendarObject($calendarId, $objectUri, $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
|
||||
*/
|
||||
public function updateCalendarObject($calendarId, $objectUri, $calendarData): ?string
|
||||
{
|
||||
$backend = $this->getBackend($calendarId);
|
||||
|
||||
return $backend ?
|
||||
$backend->updateOrCreateCalendarObject($calendarId, $objectUri, $calendarData)
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($calendarId, $objectUri)
|
||||
{
|
||||
$backend = $this->getBackend($calendarId);
|
||||
if ($backend) {
|
||||
$backend->deleteCalendarObject($objectUri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* The id can be any type, including ints, strings, objects or array.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $calendarUri
|
||||
* @param array $properties
|
||||
* @return void
|
||||
*/
|
||||
public function createCalendar($principalUri, $calendarUri, array $properties): void
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a calendar and all its objects.
|
||||
*
|
||||
* @param mixed $calendarId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendar($calendarId)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new subscription for a principal.
|
||||
*
|
||||
* If the creation was a success, an id must be returned that can be used to reference
|
||||
* this subscription in other methods, such as updateSubscription.
|
||||
*
|
||||
* @param string $principalUri
|
||||
* @param string $uri
|
||||
* @param array $properties
|
||||
* @return mixed
|
||||
*/
|
||||
public function createSubscription($principalUri, $uri, array $properties)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a subscription.
|
||||
*
|
||||
* The list of mutations is stored in a Sabre\DAV\PropPatch object.
|
||||
* To do the actual updates, you must tell this object which properties
|
||||
* you're going to process with the handle() method.
|
||||
*
|
||||
* Calling the handle method is like telling the PropPatch object "I
|
||||
* promise I can handle updating this property".
|
||||
*
|
||||
* Read the PropPatch documentation for more info and examples.
|
||||
*
|
||||
* @param mixed $subscriptionId
|
||||
* @param \Sabre\DAV\PropPatch $propPatch
|
||||
* @return void
|
||||
*/
|
||||
public function updateSubscription($subscriptionId, DAV\PropPatch $propPatch)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a subscription.
|
||||
*
|
||||
* @param mixed $subscriptionId
|
||||
* @return void
|
||||
*/
|
||||
public function deleteSubscription($subscriptionId)
|
||||
{
|
||||
}
|
||||
}
|
||||
168
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php
Normal file
168
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php
Normal file
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\DAV\Backend\CalDAV;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Models\Instance\SpecialDate;
|
||||
use Sabre\DAV\Server as SabreServer;
|
||||
use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||
use App\Services\VCalendar\ExportVCalendar;
|
||||
use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
|
||||
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
|
||||
|
||||
class CalDAVBirthdays extends AbstractCalDAVBackend
|
||||
{
|
||||
/**
|
||||
* Returns the uri for this backend.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function backendUri()
|
||||
{
|
||||
return 'birthdays';
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return parent::getDescription()
|
||||
+ [
|
||||
'{DAV:}displayname' => trans('app.dav_birthdays'),
|
||||
'{'.SabreServer::NS_SABREDAV.'}read-only' => true,
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-description' => trans('app.dav_birthdays_description', ['name' => $this->user->name]),
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-timezone' => $this->user->timezone,
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VEVENT']),
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}schedule-calendar-transp' => new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for Calendar objects.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return '.ics';
|
||||
}
|
||||
|
||||
/**
|
||||
* Datas for this date.
|
||||
*
|
||||
* @param mixed $obj
|
||||
* @return array
|
||||
*/
|
||||
public function prepareData($obj)
|
||||
{
|
||||
$calendardata = null;
|
||||
if ($obj instanceof SpecialDate) {
|
||||
try {
|
||||
$calendardata = $this->refreshObject($obj);
|
||||
|
||||
return [
|
||||
'id' => $obj->id,
|
||||
'uri' => $this->encodeUri($obj),
|
||||
'calendardata' => $calendardata,
|
||||
'etag' => '"'.sha1($calendardata).'"',
|
||||
'lastmodified' => $obj->updated_at->timestamp,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [
|
||||
'calendardata' => $calendardata,
|
||||
$e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new exported version of the object.
|
||||
*
|
||||
* @param mixed $obj date
|
||||
* @return string
|
||||
*/
|
||||
protected function refreshObject($obj): string
|
||||
{
|
||||
$vcal = app(ExportVCalendar::class)
|
||||
->execute([
|
||||
'account_id' => $this->user->account_id,
|
||||
'special_date_id' => $obj->id,
|
||||
]);
|
||||
|
||||
return $vcal->serialize();
|
||||
}
|
||||
|
||||
private function hasBirthday($contact)
|
||||
{
|
||||
if (! $contact || ! $contact->birthdate) {
|
||||
return false;
|
||||
}
|
||||
$birthdayState = $contact->getBirthdayState();
|
||||
if ($birthdayState != 'almost' && $birthdayState != 'exact') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the date for the specific uuid.
|
||||
*
|
||||
* @param string|null $collectionId
|
||||
* @param string $uuid
|
||||
* @return mixed
|
||||
*/
|
||||
public function getObjectUuid($collectionId, $uuid)
|
||||
{
|
||||
return SpecialDate::where([
|
||||
'account_id' => $this->user->account_id,
|
||||
'uuid' => $uuid,
|
||||
])->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of contact's birthdays.
|
||||
*
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getObjects($collectionId)
|
||||
{
|
||||
// We only return the birthday of default addressBook
|
||||
$contacts = $this->user->account->contacts()
|
||||
->real()
|
||||
->active()
|
||||
->get();
|
||||
|
||||
return $contacts->filter(function ($contact) {
|
||||
return $this->hasBirthday($contact);
|
||||
})
|
||||
->map(function ($contact) {
|
||||
return $contact->birthdate;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of deleted birthdays.
|
||||
*
|
||||
* @param string|null $collectionId
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getDeletedObjects($collectionId)
|
||||
{
|
||||
return collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateOrCreateCalendarObject($calendarId, $objectUri, $calendarData): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function deleteCalendarObject($objectUri)
|
||||
{
|
||||
// Not implemented
|
||||
}
|
||||
}
|
||||
219
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php
Normal file
219
app/Http/Controllers/DAV/Backend/CalDAV/CalDAVTasks.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\DAV\Backend\CalDAV;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use App\Models\Contact\Task;
|
||||
use App\Services\Task\DestroyTask;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Services\VCalendar\ExportTask;
|
||||
use App\Services\VCalendar\ImportTask;
|
||||
use Sabre\CalDAV\Plugin as CalDAVPlugin;
|
||||
use Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp;
|
||||
use Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet;
|
||||
|
||||
class CalDAVTasks extends AbstractCalDAVBackend
|
||||
{
|
||||
/**
|
||||
* Returns the uri for this backend.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function backendUri()
|
||||
{
|
||||
return 'tasks';
|
||||
}
|
||||
|
||||
public function getDescription()
|
||||
{
|
||||
return parent::getDescription()
|
||||
+ [
|
||||
'{DAV:}displayname' => trans('app.dav_tasks'),
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-description' => trans('app.dav_tasks_description', ['name' => $this->user->name]),
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-timezone' => $this->user->timezone,
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VTODO']),
|
||||
'{'.CalDAVPlugin::NS_CALDAV.'}schedule-calendar-transp' => new ScheduleCalendarTransp(ScheduleCalendarTransp::TRANSPARENT),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of all tasks.
|
||||
*
|
||||
* @param mixed|null $collectionId
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getObjects($collectionId)
|
||||
{
|
||||
return $this->user->account
|
||||
->tasks()
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collection of deleted tasks.
|
||||
*
|
||||
* @param string|null $collectionId
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
public function getDeletedObjects($collectionId)
|
||||
{
|
||||
return collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contact for the specific uuid.
|
||||
*
|
||||
* @param mixed|null $collectionId
|
||||
* @param string $uuid
|
||||
* @return mixed
|
||||
*/
|
||||
public function getObjectUuid($collectionId, $uuid)
|
||||
{
|
||||
return Task::where([
|
||||
'account_id' => $this->user->account_id,
|
||||
'uuid' => $uuid,
|
||||
])->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension for Calendar objects.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getExtension()
|
||||
{
|
||||
return '.ics';
|
||||
}
|
||||
|
||||
/**
|
||||
* Datas for this task.
|
||||
*
|
||||
* @param mixed $obj
|
||||
* @return array
|
||||
*/
|
||||
public function prepareData($obj)
|
||||
{
|
||||
$calendardata = null;
|
||||
if ($obj instanceof Task) {
|
||||
try {
|
||||
$calendardata = $this->refreshObject($obj);
|
||||
|
||||
return [
|
||||
'id' => $obj->id,
|
||||
'uri' => $this->encodeUri($obj),
|
||||
'calendardata' => $calendardata,
|
||||
'etag' => '"'.sha1($calendardata).'"',
|
||||
'lastmodified' => $obj->updated_at->timestamp,
|
||||
];
|
||||
} catch (\Exception $e) {
|
||||
Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [
|
||||
'calendardata' => $calendardata,
|
||||
$e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the new exported version of the object.
|
||||
*
|
||||
* @param mixed $obj task
|
||||
* @return string
|
||||
*/
|
||||
protected function refreshObject($obj): string
|
||||
{
|
||||
$vcal = app(ExportTask::class)
|
||||
->execute([
|
||||
'account_id' => $this->user->account_id,
|
||||
'task_id' => $obj->id,
|
||||
]);
|
||||
|
||||
return $vcal->serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateOrCreateCalendarObject($calendarId, $objectUri, $calendarData): ?string
|
||||
{
|
||||
$task_id = null;
|
||||
if ($objectUri) {
|
||||
$task = $this->getObject($this->backendUri(), $objectUri);
|
||||
|
||||
if ($task) {
|
||||
$task_id = $task->id;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$result = app(ImportTask::class)
|
||||
->execute([
|
||||
'account_id' => $this->user->account_id,
|
||||
'task_id' => $task_id,
|
||||
'entry' => $calendarData,
|
||||
]);
|
||||
|
||||
if (! Arr::has($result, 'error')) {
|
||||
$task = Task::where('account_id', $this->user->account_id)
|
||||
->find($result['task_id']);
|
||||
|
||||
$calendar = $this->prepareData($task);
|
||||
|
||||
return $calendar['etag'];
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [
|
||||
'calendarId' => $calendarId,
|
||||
'objectUri' => $objectUri,
|
||||
'calendarData' => $calendarData,
|
||||
$e,
|
||||
]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($objectUri)
|
||||
{
|
||||
$task = $this->getObject($this->backendUri(), $objectUri);
|
||||
|
||||
if ($task) {
|
||||
try {
|
||||
app(DestroyTask::class)
|
||||
->execute([
|
||||
'account_id' => $this->user->account_id,
|
||||
'task_id' => $task->id,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error(__CLASS__.' '.__FUNCTION__.': '.$e->getMessage(), [
|
||||
'objectUri' => $objectUri,
|
||||
$e,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
118
app/Http/Controllers/DAV/Backend/CalDAV/ICalDAVBackend.php
Normal file
118
app/Http/Controllers/DAV/Backend/CalDAV/ICalDAVBackend.php
Normal file
@@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\DAV\Backend\CalDAV;
|
||||
|
||||
interface ICalDAVBackend
|
||||
{
|
||||
/**
|
||||
* Returns a list of properties 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 is 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'.
|
||||
*
|
||||
* 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\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.
|
||||
*
|
||||
************************
|
||||
* == From Subscription :
|
||||
* Furthermore, all the subscription info must be returned too:
|
||||
*
|
||||
* 1. {DAV:}displayname
|
||||
* 2. {http://apple.com/ns/ical/}refreshrate
|
||||
* 3. {http://calendarserver.org/ns/}subscribed-strip-todos (omit if todos
|
||||
* should not be stripped).
|
||||
* 4. {http://calendarserver.org/ns/}subscribed-strip-alarms (omit if alarms
|
||||
* should not be stripped).
|
||||
* 5. {http://calendarserver.org/ns/}subscribed-strip-attachments (omit if
|
||||
* attachments should not be stripped).
|
||||
* 6. {http://calendarserver.org/ns/}source (Must be a
|
||||
* Sabre\DAV\Property\Href).
|
||||
* 7. {http://apple.com/ns/ical/}calendar-color
|
||||
* 8. {http://apple.com/ns/ical/}calendar-order
|
||||
* 9. {urn:ietf:params:xml:ns:caldav}supported-calendar-component-set
|
||||
* (should just be an instance of
|
||||
* Sabre\CalDAV\Property\SupportedCalendarComponentSet, with a bunch of
|
||||
* default components).
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getDescription();
|
||||
|
||||
/**
|
||||
* The getChanges method returns all the changes that have happened, since
|
||||
* the specified syncToken in the specified calendar.
|
||||
*
|
||||
* @param string|null $calendarId
|
||||
* @param string $syncToken
|
||||
* @return array
|
||||
*/
|
||||
public function getChanges($calendarId, $syncToken);
|
||||
|
||||
/**
|
||||
* Returns calendar object.
|
||||
*
|
||||
* It returns 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.
|
||||
*
|
||||
* @param mixed $obj
|
||||
* @return array
|
||||
*/
|
||||
public function prepareData($obj);
|
||||
|
||||
/**
|
||||
* 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 string|null $calendarId
|
||||
* @param string $objectUri
|
||||
* @param string $calendarData
|
||||
* @return string|null
|
||||
*/
|
||||
public function updateOrCreateCalendarObject($calendarId, $objectUri, $calendarData): ?string;
|
||||
|
||||
/**
|
||||
* Deletes an existing calendar object.
|
||||
*
|
||||
* The object uri is only the basename, or filename and not a full path.
|
||||
*
|
||||
* @param string $objectUri
|
||||
* @return void
|
||||
*/
|
||||
public function deleteCalendarObject($objectUri);
|
||||
}
|
||||
Reference in New Issue
Block a user