Skip to content

Commit

Permalink
feat: carddav client (#3851)
Browse files Browse the repository at this point in the history
  • Loading branch information
asbiin authored Sep 20, 2021
1 parent 1dc2c49 commit e6c92cf
Show file tree
Hide file tree
Showing 66 changed files with 4,728 additions and 105 deletions.
55 changes: 55 additions & 0 deletions app/Console/Commands/DavClientsUpdate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace App\Console\Commands;

use Carbon\Carbon;
use Illuminate\Console\Command;
use App\Jobs\SynchronizeAddressBooks;
use App\Models\Account\AddressBookSubscription;

class DavClientsUpdate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'monica:davclients';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Update all dav subscriptions';

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$subscriptions = AddressBookSubscription::active()->get();

$now = now();
$subscriptions->filter(function ($subscription) use ($now) {
return $this->isTimeToRunSync($subscription, $now);
})->each(function ($subscription) {
SynchronizeAddressBooks::dispatch($subscription);
});
}

/**
* Test if the last synchronized timestamp is older than the subscription's frequency time.
*
* @param AddressBookSubscription $subscription
* @param Carbon $now
* @return bool
*/
private function isTimeToRunSync(AddressBookSubscription $subscription, Carbon $now): bool
{
return is_null($subscription->last_synchronized_at)
|| $subscription->last_synchronized_at->addMinutes($subscription->frequency)->lessThan($now);
}
}
60 changes: 60 additions & 0 deletions app/Console/Commands/NewAddressBookSubscription.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace App\Console\Commands;

use App\Models\User\User;
use Illuminate\Console\Command;
use App\Services\DavClient\CreateAddressBookSubscription;

class NewAddressBookSubscription extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'monica:newaddressbooksubscription
{--email= : Monica account to add subscription to}
{--url= : CardDAV url of the address book}
{--login= : Login}
{--password= : Password of the account}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Add a new all dav subscriptions';

/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
$user = User::where('email', $this->option('email'))->firstOrFail();

$url = $this->option('url') ?? $this->ask('url', 'CardDAV url of the address book');
$login = $this->option('login') ?? $this->ask('login', 'Login name');
$password = $this->option('password') ?? $this->ask('password', 'User password');

try {
$addressBook = app(CreateAddressBookSubscription::class)->execute([
'account_id' => $user->account_id,
'user_id' => $user->id,
'base_uri' => $url,
'username' => $login,
'password' => $password,
]);
} catch (\Exception $e) {
$this->error($e->getMessage());
}

if (! isset($addressBook)) {
$this->error('Could not add subscription');
} else {
$this->info('Subscription added');
}
}
}
1 change: 1 addition & 0 deletions app/Console/Kernel.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ protected function schedule(Schedule $schedule)
{
$this->scheduleCommand($schedule, 'send:reminders', 'hourly');
$this->scheduleCommand($schedule, 'send:stay_in_touch', 'hourly');
$this->scheduleCommand($schedule, 'monica:davclients', 'hourly');
$this->scheduleCommand($schedule, 'monica:calculatestatistics', 'daily');
$this->scheduleCommand($schedule, 'monica:ping', 'daily');
$this->scheduleCommand($schedule, 'monica:clean', 'daily');
Expand Down
6 changes: 3 additions & 3 deletions app/Helpers/SearchHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ public static function searchContacts(string $needle, string $orderByColumn, str
$field_id = is_null($field) ? 0 : $field->id;

/** @var Builder */
$b = Contact::whereHas('contactFields', function ($query) use ($accountId, $field_id, $search_term) {
$builder = Contact::whereHas('contactFields', function ($query) use ($accountId, $field_id, $search_term) {
$query->where([
['account_id', $accountId],
['data', 'like', "$search_term%"],
['contact_field_type_id', $field_id],
]);
});

return $b->addressBook($accountId, $addressBookName)
->orderBy($orderByColumn, $orderByDirection);
return $builder->addressBook($accountId, $addressBookName)
->orderBy($orderByColumn, $orderByDirection);
}

return Contact::search($needle, $accountId, $orderByColumn, $orderByDirection)
Expand Down
2 changes: 1 addition & 1 deletion app/Http/Controllers/DAV/Auth/AuthBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function check(RequestInterface $request, ResponseInterface $response)
return [false, 'User is not authenticated'];
}

return [true, PrincipalBackend::getPrincipalUser()];
return [true, PrincipalBackend::getPrincipalUser(Auth::user())];
}

/**
Expand Down
31 changes: 27 additions & 4 deletions app/Http/Controllers/DAV/Backend/CalDAV/AbstractCalDAVBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Http\Controllers\DAV\Backend\CalDAV;

use App\Models\User\User;
use Sabre\DAV\Server as SabreServer;
use Sabre\CalDAV\Plugin as CalDAVPlugin;
use Sabre\DAV\Sync\Plugin as DAVSyncPlugin;
Expand All @@ -13,18 +14,40 @@ abstract class AbstractCalDAVBackend implements ICalDAVBackend, IDAVBackend
{
use SyncDAVBackend;

/**
* Create a new instance of AbstractCalDAVBackend.
*
* @param User $user
*/
public function __construct($user)
{
$this->user = $user;
}

/**
* Get description array.
*
* @return array
*/
public function getDescription()
{
$token = DAVSyncPlugin::SYNCTOKEN_PREFIX.$this->refreshSyncToken(null)->id;
$des = [

return [
'id' => $this->backendUri(),
'uri' => $this->backendUri(),
'principaluri' => PrincipalBackend::getPrincipalUser(),
'principaluri' => PrincipalBackend::getPrincipalUser($this->user),
'{DAV:}sync-token' => $token,
'{'.SabreServer::NS_SABREDAV.'}sync-token' => $token,
'{'.CalDAVPlugin::NS_CALENDARSERVER.'}getctag' => $token,
];

return $des;
}

/**
* Get the new exported version of the object.
*
* @param mixed $obj
* @return string
*/
abstract protected function refreshObject($obj): string;
}
20 changes: 18 additions & 2 deletions app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,27 @@
namespace App\Http\Controllers\DAV\Backend\CalDAV;

use Sabre\DAV;
use App\Models\User\User;
use Sabre\CalDAV\Backend\SyncSupport;
use Sabre\CalDAV\Backend\AbstractBackend;

class CalDAVBackend extends AbstractBackend implements SyncSupport
{
/**
* @var User
*/
protected $user;

/**
* Create a new instance of CalDAVBackend.
*
* @param User $user
*/
public function __construct(User $user)
{
$this->user = $user;
}

/**
* Set the Calendar backends.
*
Expand All @@ -16,8 +32,8 @@ class CalDAVBackend extends AbstractBackend implements SyncSupport
private function getBackends(): array
{
return [
new CalDAVBirthdays(),
new CalDAVTasks(),
new CalDAVBirthdays($this->user),
new CalDAVTasks($this->user),
];
}

Expand Down
34 changes: 22 additions & 12 deletions app/Http/Controllers/DAV/Backend/CalDAV/CalDAVBirthdays.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Illuminate\Support\Facades\Log;
use App\Models\Instance\SpecialDate;
use Illuminate\Support\Facades\Auth;
use Sabre\DAV\Server as SabreServer;
use Sabre\CalDAV\Plugin as CalDAVPlugin;
use App\Services\VCalendar\ExportVCalendar;
Expand All @@ -29,8 +28,8 @@ public function getDescription()
+ [
'{DAV:}displayname' => trans('app.dav_birthdays'),
'{'.SabreServer::NS_SABREDAV.'}read-only' => true,
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-description' => trans('app.dav_birthdays_description', ['name' => Auth::user()->name]),
'{'.CalDAVPlugin::NS_CALDAV.'}calendar-timezone' => Auth::user()->timezone,
'{'.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),
];
Expand All @@ -56,13 +55,7 @@ public function prepareData($obj)
{
if ($obj instanceof SpecialDate) {
try {
$vcal = app(ExportVCalendar::class)
->execute([
'account_id' => Auth::user()->account_id,
'special_date_id' => $obj->id,
]);

$calendardata = $vcal->serialize();
$calendardata = $this->refreshObject($obj);

return [
'id' => $obj->id,
Expand All @@ -79,6 +72,23 @@ public function prepareData($obj)
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) {
Expand All @@ -102,7 +112,7 @@ private function hasBirthday($contact)
public function getObjectUuid($collectionId, $uuid)
{
return SpecialDate::where([
'account_id' => Auth::user()->account_id,
'account_id' => $this->user->account_id,
'uuid' => $uuid,
])->first();
}
Expand All @@ -115,7 +125,7 @@ public function getObjectUuid($collectionId, $uuid)
public function getObjects($collectionId)
{
// We only return the birthday of default addressBook
$contacts = Auth::user()->account->contacts()
$contacts = $this->user->account->contacts()
->real()
->active()
->get();
Expand Down
Loading

0 comments on commit e6c92cf

Please sign in to comment.