Skip to content

Commit d65db84

Browse files
authored
Birthday service revamp (#246)
1 parent 0cae15d commit d65db84

12 files changed

Lines changed: 603 additions & 64 deletions

src/Command/SyncBirthdayCalendars.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Entity\User;
66
use App\Services\BirthdayService;
77
use Doctrine\Persistence\ManagerRegistry;
8+
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
89
use Symfony\Component\Console\Command\Command;
910
use Symfony\Component\Console\Helper\ProgressBar;
1011
use Symfony\Component\Console\Input\InputArgument;
@@ -18,6 +19,10 @@ public function __construct(
1819
private BirthdayService $birthdayService,
1920
) {
2021
parent::__construct();
22+
23+
$em = $doctrine->getManager();
24+
$pdo = $em->getConnection()->getNativeConnection();
25+
$this->birthdayService->setBackend(new CalendarBackend($pdo));
2126
}
2227

2328
protected function configure(): void

src/Controller/DAVController.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ private function initServer(string $authMethod, string $authRealm = User::DEFAUL
282282
}
283283

284284
if ($this->cardDAVEnabled && $this->calDAVEnabled) {
285-
$this->server->addPlugin(new BirthdayCalendarPlugin($this->birthdayService));
285+
$this->server->addPlugin(new BirthdayCalendarPlugin($this->birthdayService, $calendarBackend));
286286
}
287287

288288
// WebDAV plugins

src/Entity/CalendarChange.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class CalendarChange
2424
private $calendar;
2525

2626
#[ORM\Column(type: 'smallint')]
27-
private $operation;
27+
private $operation; // 1 = create, 2 = update, 3 = delete
2828

2929
public function getId(): ?int
3030
{

src/Plugins/BirthdayCalendarPlugin.php

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Plugins;
44

55
use App\Services\BirthdayService;
6+
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
67
use Sabre\CardDAV;
78
use Sabre\DAV;
89

@@ -18,9 +19,10 @@ class BirthdayCalendarPlugin extends DAV\ServerPlugin
1819
*/
1920
protected $server;
2021

21-
public function __construct(BirthdayService $birthdayService)
22+
public function __construct(BirthdayService $birthdayService, CalendarBackend $calendarBackend)
2223
{
2324
$this->birthdayService = $birthdayService;
25+
$this->birthdayService->setBackend($calendarBackend);
2426
}
2527

2628
public function initialize(DAV\Server $server)
@@ -39,48 +41,56 @@ public function initialize(DAV\Server $server)
3941
$server->on('beforeUnbind', [$this, 'beforeCardDelete']);
4042
}
4143

42-
private function resyncCurrentPrincipal()
44+
public function afterCardCreate(string $path, DAV\ICollection $parentNode): void
4345
{
44-
$authPlugin = $this->server->getPlugin('auth');
45-
46-
if (!$authPlugin) {
47-
return null;
48-
}
49-
50-
$principal = $authPlugin->getCurrentPrincipal();
51-
52-
if ($principal) {
53-
$this->birthdayService->syncPrincipal($principal);
46+
if (!$parentNode instanceof CardDAV\AddressBook) {
47+
return;
5448
}
49+
$this->handleCardChange($path, $parentNode);
5550
}
5651

57-
public function afterCardCreate($path, DAV\ICollection $parentNode)
52+
public function afterCardUpdate(string $path, DAV\IFile $node): void
5853
{
59-
if (!$parentNode instanceof CardDAV\IAddressBook) {
54+
if (!$node instanceof CardDAV\ICard) {
6055
return;
6156
}
57+
$parentPath = dirname($path);
58+
$parentNode = $this->server->tree->getNodeForPath($parentPath);
6259

63-
$principal = $this->resyncCurrentPrincipal();
64-
}
65-
66-
public function afterCardUpdate($path, DAV\IFile $node)
67-
{
68-
if (!$node instanceof CardDAV\ICard) {
60+
if (!$parentNode instanceof CardDAV\AddressBook) {
6961
return;
7062
}
7163

72-
$principal = $this->resyncCurrentPrincipal();
64+
$this->handleCardChange($path, $parentNode);
7365
}
7466

75-
public function beforeCardDelete($path)
67+
public function beforeCardDelete(string $path): void
7668
{
7769
$node = $this->server->tree->getNodeForPath($path);
7870

7971
if (!$node instanceof CardDAV\ICard) {
8072
return;
8173
}
8274

83-
$principal = $this->resyncCurrentPrincipal();
75+
$parentPath = dirname($path);
76+
$parentNode = $this->server->tree->getNodeForPath($parentPath);
77+
78+
if (!$parentNode instanceof CardDAV\AddressBook) {
79+
return;
80+
}
81+
82+
$addressBookId = $parentNode->getProperties(['id'])['id'];
83+
84+
$this->birthdayService->onCardDeleted($addressBookId, basename($path));
85+
}
86+
87+
private function handleCardChange(string $path, CardDAV\AddressBook $parentNode): void
88+
{
89+
$cardUri = basename($path);
90+
$addressBookId = $parentNode->getProperties(['id'])['id'];
91+
$cardNode = $this->server->tree->getNodeForPath($path);
92+
93+
$this->birthdayService->onCardChanged($addressBookId, $cardUri, $cardNode->get());
8494
}
8595

8696
public function getPluginName(): string

src/Services/BirthdayService.php

Lines changed: 43 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use App\Entity\Card;
2020
use App\Entity\Principal;
2121
use Doctrine\Persistence\ManagerRegistry;
22+
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
2223
use Sabre\DAV\Sharing\Plugin as SharingPlugin;
2324
use Sabre\VObject\Component\VCalendar;
2425
use Sabre\VObject\Component\VCard;
@@ -30,12 +31,22 @@
3031

3132
class BirthdayService
3233
{
34+
/**
35+
* @var CalendarBackend
36+
*/
37+
private $calendarBackend;
38+
3339
public function __construct(
3440
private ManagerRegistry $doctrine,
3541
private string $birthdayReminderOffset,
3642
) {
3743
}
3844

45+
public function setBackend(CalendarBackend $calendarBackend)
46+
{
47+
$this->calendarBackend = $calendarBackend;
48+
}
49+
3950
public function onCardChanged(int $addressBookId, string $cardUri, string $cardData): void
4051
{
4152
$book = $this->doctrine->getRepository(AddressBook::class)->findOneById($addressBookId);
@@ -45,9 +56,9 @@ public function onCardChanged(int $addressBookId, string $cardUri, string $cardD
4556
}
4657

4758
$principalUri = $book->getPrincipalUri();
48-
$calendar = $this->ensureBirthdayCalendarExists($principalUri);
59+
$calendarInstance = $this->ensureBirthdayCalendarExists($principalUri);
4960

50-
$this->updateCalendar($cardUri, $cardData, $book, $calendar->getCalendar());
61+
$this->updateCalendar($cardUri, $cardData, $book, $calendarInstance);
5162
}
5263

5364
public function onCardDeleted(int $addressBookId, string $cardUri): void
@@ -59,14 +70,18 @@ public function onCardDeleted(int $addressBookId, string $cardUri): void
5970
}
6071

6172
$principalUri = $book->getPrincipalUri();
62-
$calendar = $this->ensureBirthdayCalendarExists($principalUri);
73+
$calendarInstance = $this->ensureBirthdayCalendarExists($principalUri);
6374

6475
$objectUri = $book->getUri().'-'.$cardUri.'.ics';
65-
$calendarObject = $this->doctrine->getRepository(CalendarObject::class)->findOneBy(['calendar' => $calendar, 'uri' => $objectUri]);
6676

67-
$em = $this->doctrine->getManager();
68-
$em->remove($calendarObject);
69-
$em->flush();
77+
$calendar = $calendarInstance->getCalendar();
78+
// This is the structure that needs to be passed to the backend methods
79+
$calendarId = [$calendar->getId(), $calendarInstance->getId()];
80+
81+
$this->calendarBackend->deleteCalendarObject(
82+
$calendarId,
83+
$objectUri
84+
);
7085
}
7186

7287
public function shouldBirthdayCalendarExist(string $principalUri): bool
@@ -290,7 +305,7 @@ public function syncPrincipal(string $principal): void
290305
}
291306
}
292307

293-
public function birthdayEvenChanged(string $existingCalendarData, VCalendar $newCalendarData): bool
308+
public function birthdayEventChanged(string $existingCalendarData, VCalendar $newCalendarData): bool
294309
{
295310
try {
296311
$existingBirthday = Reader::read($existingCalendarData);
@@ -307,52 +322,41 @@ public function birthdayEvenChanged(string $existingCalendarData, VCalendar $new
307322
/**
308323
* @throws InvalidDataException
309324
*/
310-
private function updateCalendar(string $cardUri, string $cardData, AddressBook $book, Calendar $calendar): void
325+
private function updateCalendar(string $cardUri, string $cardData, AddressBook $book, CalendarInstance $calendarInstance): void
311326
{
312327
$objectUid = $book->getUri().'-'.$cardUri;
313328
$objectUri = $objectUid.'.ics';
314329
$calendarData = $this->buildDataFromContact($cardData);
315330

316-
$existing = $this->doctrine->getRepository(CalendarObject::class)->findOneBy(['calendar' => $calendar, 'uri' => $objectUri]);
331+
$calendar = $calendarInstance->getCalendar();
332+
// This is the structure that needs to be passed to the backend methods
333+
$calendarId = [$calendar->getId(), $calendarInstance->getId()];
317334

318-
$em = $this->doctrine->getManager();
335+
$existing = $this->doctrine->getRepository(CalendarObject::class)->findOneBy(['calendar' => $calendar, 'uri' => $objectUri]);
319336

320337
if (null === $calendarData) {
321338
if (null !== $existing) {
322-
$em->remove($existing);
339+
$this->calendarBackend->deleteCalendarObject(
340+
[$calendar->getId(), $calendarInstance->getId()],
341+
$objectUri
342+
);
323343
}
324344
} else {
325-
$serializedCalendarData = $calendarData->serialize();
326-
$vEvent = $calendarData->getComponents()[0];
327-
$maxDate = new \DateTime(Constants::MAX_DATE);
328-
329345
if (null === $existing) {
330-
$calendarObject = (new CalendarObject())
331-
->setCalendar($calendar)
332-
->setUri($objectUri)
333-
->setComponentType('VEVENT')
334-
->setUid($objectUid)
335-
->setLastModified((new \DateTime())->getTimestamp())
336-
->setFirstOccurence($vEvent->DTSTART->getDateTime()->getTimeStamp())
337-
->setLastOccurence($maxDate->getTimestamp())
338-
->setEtag(md5($serializedCalendarData))
339-
->setSize(strlen($serializedCalendarData))
340-
->setCalendarData($serializedCalendarData);
341-
342-
$em->persist($calendarObject);
346+
$this->calendarBackend->createCalendarObject(
347+
[$calendar->getId(), $calendarInstance->getId()],
348+
$objectUri,
349+
$calendarData->serialize()
350+
);
343351
} else {
344-
if ($this->birthdayEvenChanged($existing->getCalendarData(), $calendarData)) {
345-
$existing
346-
->setLastModified((new \DateTime())->getTimestamp())
347-
->setFirstOccurence($vEvent->DTSTART->getDateTime()->getTimeStamp())
348-
->setLastOccurence($maxDate->getTimestamp())
349-
->setEtag(md5($serializedCalendarData))
350-
->setSize(strlen($serializedCalendarData))
351-
->setCalendarData($serializedCalendarData);
352+
if ($this->birthdayEventChanged($existing->getCalendarData(), $calendarData)) {
353+
$this->calendarBackend->updateCalendarObject(
354+
[$calendar->getId(), $calendarInstance->getId()],
355+
$objectUri,
356+
$calendarData->serialize()
357+
);
352358
}
353359
}
354360
}
355-
356-
$em->flush();
357361
}
358362
}

0 commit comments

Comments
 (0)