Skip to content
Open
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
6 changes: 6 additions & 0 deletions config/services.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ parameters:
default_birthday_reminder_offset: "PT9H"
caldav_enabled: "%env(bool:CALDAV_ENABLED)%"
carddav_enabled: "%env(bool:CARDDAV_ENABLED)%"
icscalendars_enabled: "%env(default:default_icscalendars_enabled:bool:ICSCALENDARS_ENABLED)%"
default_icscalendars_enabled: "0"

services:
# default configuration for services in *this* file
Expand Down Expand Up @@ -78,6 +80,10 @@ services:
arguments:
$birthdayReminderOffset: "%birthday_reminder_offset%"

App\Services\ICSCalendarsService:
arguments:
$enabled: "%icscalendars_enabled%"

App\Security\ApiKeyAuthenticator:
arguments:
$apiKey: "%env(API_KEY)%"
Expand Down
60 changes: 60 additions & 0 deletions src/Command/SyncICSCalendars.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

declare(strict_types=1);

namespace App\Command;

use App\Entity\CalendarSubscription;
use App\Services\ICSCalendarsService;
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\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class SyncICSCalendars extends Command
{
public function __construct(
private ManagerRegistry $doctrine,
private ICSCalendarsService $icsService,
) {
parent::__construct();

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

protected function configure(): void
{
$this
->setName('dav:sync-ics-calendars')
->setDescription('Synchronizes the ICS calendars');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
if (!$this->icsService->isEnabled()) {
$output->writeln('ICS calendars are disabled.');
$output->writeln('Enable ICS calendars by setting ICSCALENDARS_ENABLED=true.');
return self::SUCCESS;
}

$output->writeln('Start ICS calendars sync ...');
$p = new ProgressBar($output);
$p->start();

$subscriptions = $this->doctrine->getRepository(CalendarSubscription::class)->findAll();

foreach ($subscriptions as $subscription) {
$p->advance();
$this->icsService->sync($subscription);
}

$p->finish();
$output->writeln('');

return self::SUCCESS;
}
}
12 changes: 10 additions & 2 deletions src/Controller/DAVController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
use App\Plugins\BirthdayCalendarPlugin;
use App\Plugins\DavisIMipPlugin;
use App\Plugins\PublicAwareDAVACLPlugin;
use App\Plugins\ICSCalendarsPlugin;
use App\Services\BasicAuth;
use App\Services\BirthdayService;
use App\Services\ICSCalendarsService;
use App\Services\IMAPAuth;
use App\Services\LDAPAuth;
use Doctrine\ORM\EntityManagerInterface;
use PDO;
use Psr\Log\LoggerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
Expand Down Expand Up @@ -107,6 +108,11 @@ class DAVController extends AbstractController
*/
protected $birthdayService;

/**
* @var ICSCalendarsService
*/
protected $icsCalendarsService;

/**
* Base URI of the server.
*
Expand Down Expand Up @@ -149,7 +155,7 @@ class DAVController extends AbstractController
*/
protected $server;

public function __construct(MailerInterface $mailer, BasicAuth $basicAuthBackend, IMAPAuth $IMAPAuthBackend, LDAPAuth $LDAPAuthBackend, UrlGeneratorInterface $router, EntityManagerInterface $entityManager, LoggerInterface $logger, BirthdayService $birthdayService, string $publicDir, bool $calDAVEnabled = true, bool $cardDAVEnabled = true, bool $webDAVEnabled = false, bool $publicCalendarsEnabled = true, ?string $inviteAddress = null, ?string $authMethod = null, ?string $authRealm = null, ?string $webdavPublicDir = null, ?string $webdavHomesDir = null, ?string $webdavTmpDir = null)
public function __construct(MailerInterface $mailer, BasicAuth $basicAuthBackend, IMAPAuth $IMAPAuthBackend, LDAPAuth $LDAPAuthBackend, UrlGeneratorInterface $router, EntityManagerInterface $entityManager, LoggerInterface $logger, BirthdayService $birthdayService, ICSCalendarsService $icsCalendarsService, string $publicDir, bool $calDAVEnabled = true, bool $cardDAVEnabled = true, bool $webDAVEnabled = false, bool $publicCalendarsEnabled = true, ?string $inviteAddress = null, ?string $authMethod = null, ?string $authRealm = null, ?string $webdavPublicDir = null, ?string $webdavHomesDir = null, ?string $webdavTmpDir = null)
{
$this->publicDir = $publicDir;

Expand All @@ -167,6 +173,7 @@ public function __construct(MailerInterface $mailer, BasicAuth $basicAuthBackend
$this->logger = $logger;
$this->mailer = $mailer;
$this->birthdayService = $birthdayService;
$this->icsCalendarsService = $icsCalendarsService;
$this->baseUri = $router->generate('dav', ['path' => '']);

$this->basicAuthBackend = $basicAuthBackend;
Expand Down Expand Up @@ -273,6 +280,7 @@ private function initServer(string $authMethod, string $authRealm = User::DEFAUL
if ($this->inviteAddress) {
$this->server->addPlugin(new DavisIMipPlugin($this->mailer, $this->inviteAddress, $this->publicDir));
}
$this->server->addPlugin(new ICSCalendarsPlugin($this->icsCalendarsService, $calendarBackend));
}

// CardDAV plugins
Expand Down
2 changes: 1 addition & 1 deletion src/Entity/CalendarInstance.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public function isPublic(): bool

public function isAutomaticallyGenerated(): bool
{
return in_array($this->uri, [Constants::BIRTHDAY_CALENDAR_URI]);
return $this->uri === Constants::BIRTHDAY_CALENDAR_URI || str_ends_with($this->uri, '.ics');
}

public function getDisplayName(): ?string
Expand Down
64 changes: 64 additions & 0 deletions src/Plugins/ICSCalendarsPlugin.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

declare(strict_types=1);

namespace App\Plugins;

use App\Services\ICSCalendarsService;
use Sabre\CalDAV\Backend\PDO as CalendarBackend;
use Sabre\CalDAV\Subscriptions\ISubscription;
use Sabre\DAV\Server;
use Sabre\DAV\ServerPlugin;

class ICSCalendarsPlugin extends ServerPlugin
{
private ICSCalendarsService $icsService;
private Server $server;

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

public function initialize(Server $server)
{
$this->server = $server;

// Hook into creation
$server->on('afterBind', [$this, 'afterSubscriptionCreate']);

// Hook into deletion
// Note: The node no longer exists after unbind so we hook in before
$server->on('beforeUnbind', [$this, 'beforeSubscriptionDelete']);
}

public function afterSubscriptionCreate(string $path): void
{
$node = $this->server->tree->getNodeForPath($path);
if (!$node instanceof ISubscription) {
return;
}

$this->icsService->onSubscriptionCreate($node->getProperties(['id'])['id']);
}

public function beforeSubscriptionDelete(string $path): void
{
$node = $this->server->tree->getNodeForPath($path);
if (!$node instanceof ISubscription) {
return;
}

$this->icsService->onSubscriptionDelete($node->getProperties(['id'])['id']);
}

public function getPluginInfo()
{
return [
'name' => $this->getPluginName(),
'description' => 'Creates calendars for Subscriptions.',
'link' => 'https://github.com/tchapi/davis',
];
}
}
Loading
Loading