Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Command/SyncBirthdayCalendars.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Entity\User;
use App\Services\BirthdayService;
use Doctrine\Persistence\ManagerRegistry;
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
Expand All @@ -18,6 +19,10 @@ public function __construct(
private BirthdayService $birthdayService,
) {
parent::__construct();

$em = $doctrine->getManager();
$pdo = $em->getConnection()->getNativeConnection();
$this->birthdayService->setBackend(new CalendarBackend($pdo));
}

protected function configure(): void
Expand Down
2 changes: 1 addition & 1 deletion src/Controller/DAVController.php
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ private function initServer(string $authMethod, string $authRealm = User::DEFAUL
}

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

// WebDAV plugins
Expand Down
2 changes: 1 addition & 1 deletion src/Entity/CalendarChange.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class CalendarChange
private $calendar;

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

public function getId(): ?int
{
Expand Down
56 changes: 33 additions & 23 deletions src/Plugins/BirthdayCalendarPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Plugins;

use App\Services\BirthdayService;
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
use Sabre\CardDAV;
use Sabre\DAV;

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

public function __construct(BirthdayService $birthdayService)
public function __construct(BirthdayService $birthdayService, CalendarBackend $calendarBackend)
{
$this->birthdayService = $birthdayService;
$this->birthdayService->setBackend($calendarBackend);
}

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

private function resyncCurrentPrincipal()
public function afterCardCreate(string $path, DAV\ICollection $parentNode): void
{
$authPlugin = $this->server->getPlugin('auth');

if (!$authPlugin) {
return null;
}

$principal = $authPlugin->getCurrentPrincipal();

if ($principal) {
$this->birthdayService->syncPrincipal($principal);
if (!$parentNode instanceof CardDAV\AddressBook) {
return;
}
$this->handleCardChange($path, $parentNode);
}

public function afterCardCreate($path, DAV\ICollection $parentNode)
public function afterCardUpdate(string $path, DAV\IFile $node): void
{
if (!$parentNode instanceof CardDAV\IAddressBook) {
if (!$node instanceof CardDAV\ICard) {
return;
}
$parentPath = dirname($path);
$parentNode = $this->server->tree->getNodeForPath($parentPath);

$principal = $this->resyncCurrentPrincipal();
}

public function afterCardUpdate($path, DAV\IFile $node)
{
if (!$node instanceof CardDAV\ICard) {
if (!$parentNode instanceof CardDAV\AddressBook) {
return;
}

$principal = $this->resyncCurrentPrincipal();
$this->handleCardChange($path, $parentNode);
}

public function beforeCardDelete($path)
public function beforeCardDelete(string $path): void
{
$node = $this->server->tree->getNodeForPath($path);

if (!$node instanceof CardDAV\ICard) {
return;
}

$principal = $this->resyncCurrentPrincipal();
$parentPath = dirname($path);
$parentNode = $this->server->tree->getNodeForPath($parentPath);

if (!$parentNode instanceof CardDAV\AddressBook) {
return;
}

$addressBookId = $parentNode->getProperties(['id'])['id'];

$this->birthdayService->onCardDeleted($addressBookId, basename($path));
}

private function handleCardChange(string $path, CardDAV\AddressBook $parentNode): void
{
$cardUri = basename($path);
$addressBookId = $parentNode->getProperties(['id'])['id'];
$cardNode = $this->server->tree->getNodeForPath($path);

$this->birthdayService->onCardChanged($addressBookId, $cardUri, $cardNode->get());
}

public function getPluginName(): string
Expand Down
82 changes: 43 additions & 39 deletions src/Services/BirthdayService.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use App\Entity\Card;
use App\Entity\Principal;
use Doctrine\Persistence\ManagerRegistry;
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
use Sabre\DAV\Sharing\Plugin as SharingPlugin;
use Sabre\VObject\Component\VCalendar;
use Sabre\VObject\Component\VCard;
Expand All @@ -30,12 +31,22 @@

class BirthdayService
{
/**
* @var CalendarBackend
*/
private $calendarBackend;

public function __construct(
private ManagerRegistry $doctrine,
private string $birthdayReminderOffset,
) {
}

public function setBackend(CalendarBackend $calendarBackend)
{
$this->calendarBackend = $calendarBackend;
}

public function onCardChanged(int $addressBookId, string $cardUri, string $cardData): void
{
$book = $this->doctrine->getRepository(AddressBook::class)->findOneById($addressBookId);
Expand All @@ -45,9 +56,9 @@ public function onCardChanged(int $addressBookId, string $cardUri, string $cardD
}

$principalUri = $book->getPrincipalUri();
$calendar = $this->ensureBirthdayCalendarExists($principalUri);
$calendarInstance = $this->ensureBirthdayCalendarExists($principalUri);

$this->updateCalendar($cardUri, $cardData, $book, $calendar->getCalendar());
$this->updateCalendar($cardUri, $cardData, $book, $calendarInstance);
}

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

$principalUri = $book->getPrincipalUri();
$calendar = $this->ensureBirthdayCalendarExists($principalUri);
$calendarInstance = $this->ensureBirthdayCalendarExists($principalUri);

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

$em = $this->doctrine->getManager();
$em->remove($calendarObject);
$em->flush();
$calendar = $calendarInstance->getCalendar();
// This is the structure that needs to be passed to the backend methods
$calendarId = [$calendar->getId(), $calendarInstance->getId()];

$this->calendarBackend->deleteCalendarObject(
$calendarId,
$objectUri
);
}

public function shouldBirthdayCalendarExist(string $principalUri): bool
Expand Down Expand Up @@ -290,7 +305,7 @@ public function syncPrincipal(string $principal): void
}
}

public function birthdayEvenChanged(string $existingCalendarData, VCalendar $newCalendarData): bool
public function birthdayEventChanged(string $existingCalendarData, VCalendar $newCalendarData): bool
{
try {
$existingBirthday = Reader::read($existingCalendarData);
Expand All @@ -307,52 +322,41 @@ public function birthdayEvenChanged(string $existingCalendarData, VCalendar $new
/**
* @throws InvalidDataException
*/
private function updateCalendar(string $cardUri, string $cardData, AddressBook $book, Calendar $calendar): void
private function updateCalendar(string $cardUri, string $cardData, AddressBook $book, CalendarInstance $calendarInstance): void
{
$objectUid = $book->getUri().'-'.$cardUri;
$objectUri = $objectUid.'.ics';
$calendarData = $this->buildDataFromContact($cardData);

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

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

if (null === $calendarData) {
if (null !== $existing) {
$em->remove($existing);
$this->calendarBackend->deleteCalendarObject(
[$calendar->getId(), $calendarInstance->getId()],
$objectUri
);
}
} else {
$serializedCalendarData = $calendarData->serialize();
$vEvent = $calendarData->getComponents()[0];
$maxDate = new \DateTime(Constants::MAX_DATE);

if (null === $existing) {
$calendarObject = (new CalendarObject())
->setCalendar($calendar)
->setUri($objectUri)
->setComponentType('VEVENT')
->setUid($objectUid)
->setLastModified((new \DateTime())->getTimestamp())
->setFirstOccurence($vEvent->DTSTART->getDateTime()->getTimeStamp())
->setLastOccurence($maxDate->getTimestamp())
->setEtag(md5($serializedCalendarData))
->setSize(strlen($serializedCalendarData))
->setCalendarData($serializedCalendarData);

$em->persist($calendarObject);
$this->calendarBackend->createCalendarObject(
[$calendar->getId(), $calendarInstance->getId()],
$objectUri,
$calendarData->serialize()
);
} else {
if ($this->birthdayEvenChanged($existing->getCalendarData(), $calendarData)) {
$existing
->setLastModified((new \DateTime())->getTimestamp())
->setFirstOccurence($vEvent->DTSTART->getDateTime()->getTimeStamp())
->setLastOccurence($maxDate->getTimestamp())
->setEtag(md5($serializedCalendarData))
->setSize(strlen($serializedCalendarData))
->setCalendarData($serializedCalendarData);
if ($this->birthdayEventChanged($existing->getCalendarData(), $calendarData)) {
$this->calendarBackend->updateCalendarObject(
[$calendar->getId(), $calendarInstance->getId()],
$objectUri,
$calendarData->serialize()
);
}
}
}

$em->flush();
}
}
Loading
Loading