Skip to content

Commit bffff2a

Browse files
author
tchapi
committed
chore
1 parent de2f2b7 commit bffff2a

File tree

11 files changed

+564
-5
lines changed

11 files changed

+564
-5
lines changed
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace DoctrineMigrations;
6+
7+
use Doctrine\DBAL\Schema\Schema;
8+
use Doctrine\Migrations\AbstractMigration;
9+
10+
/**
11+
* Add birthday calendar.
12+
*/
13+
final class Version20250421163214 extends AbstractMigration
14+
{
15+
public function getDescription(): string
16+
{
17+
return 'Add birthday calendars';
18+
}
19+
20+
public function up(Schema $schema): void
21+
{
22+
$engine = $this->connection->getDatabasePlatform()->getName();
23+
24+
if ('mysql' === $engine) {
25+
$this->addSql('ALTER TABLE addressbooks ADD included_in_birthday_calendar TINYINT(1) DEFAULT 0');
26+
} elseif ('postgresql' === $engine) {
27+
$this->addSql('ALTER TABLE addressbooks ADD COLUMN included_in_birthday_calendar BOOLEAN DEFAULT FALSE;');
28+
} elseif ('sqlite' === $engine) {
29+
$this->addSql('ALTER TABLE addressbooks ADD COLUMN included_in_birthday_calendar INTEGER DEFAULT 0;');
30+
}
31+
}
32+
33+
public function down(Schema $schema): void
34+
{
35+
if ('mysql' === $this->connection->getDatabasePlatform()->getName()) {
36+
$this->addSql('ALTER TABLE addressbooks DROP included_in_birthday_calendar');
37+
} else {
38+
$this->addSql('ALTER TABLE addressbooks DROP COLUMN included_in_birthday_calendar');
39+
}
40+
}
41+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace App\Command;
4+
5+
use App\Entity\User;
6+
use App\Services\BirthdayService;
7+
use Doctrine\Persistence\ManagerRegistry;
8+
use Symfony\Component\Console\Command\Command;
9+
use Symfony\Component\Console\Helper\ProgressBar;
10+
use Symfony\Component\Console\Input\InputArgument;
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
class SyncBirthdayCalendars extends Command
15+
{
16+
public function __construct(
17+
private ManagerRegistry $doctrine,
18+
private BirthdayService $birthdayService,
19+
) {
20+
parent::__construct();
21+
}
22+
23+
protected function configure(): void
24+
{
25+
$this
26+
->setName('dav:sync-birthday-calendar')
27+
->setDescription('Synchronizes the birthday calendar')
28+
->addArgument('username',
29+
InputArgument::OPTIONAL,
30+
'Username for whom the birthday calendar will be synchronized');
31+
}
32+
33+
protected function execute(InputInterface $input, OutputInterface $output): int
34+
{
35+
$username = $input->getArgument('username');
36+
37+
if (!is_null($username)) {
38+
if (!$this->doctrine->getRepository(User::class)->findOneByUsername($username)) {
39+
throw new \InvalidArgumentException("User <$username> is unknown.");
40+
}
41+
42+
$output->writeln("Start birthday calendar sync for $username");
43+
$this->birthdayService->syncUser($username);
44+
45+
return self::SUCCESS;
46+
}
47+
48+
$output->writeln('Start birthday calendar sync for all users ...');
49+
$p = new ProgressBar($output);
50+
$p->start();
51+
52+
$users = $this->doctrine->getRepository(User::class)->findAll();
53+
54+
foreach ($users as $user) {
55+
$p->advance();
56+
$this->birthdayService->syncUser($user->getUsername());
57+
}
58+
59+
$p->finish();
60+
$output->writeln('');
61+
62+
return self::SUCCESS;
63+
}
64+
}

src/Constants.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App;
6+
7+
class Constants
8+
{
9+
public const BIRTHDAY_CALENDAR_URI = 'birthday-calendar';
10+
public const BIRTHDAY_REMINDER_OFFSET = 'PT9H'; // 9am on the day of the event
11+
12+
public const MAX_DATE = '2038-01-01'; // Year 2038 bug — will be fixed by https://github.com/tchapi/davis/pull/186
13+
}

src/Controller/Admin/AddressBookController.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use App\Entity\AddressBook;
66
use App\Entity\Principal;
77
use App\Form\AddressBookType;
8+
use App\Services\BirthdayService;
89
use Doctrine\Persistence\ManagerRegistry;
910
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1011
use Symfony\Component\HttpFoundation\Request;
@@ -30,7 +31,7 @@ public function addressBooks(ManagerRegistry $doctrine, string $username): Respo
3031

3132
#[Route('/{username}/new', name: 'create')]
3233
#[Route('/{username}/edit/{id}', name: 'edit', requirements: ['id' => "\d+"])]
33-
public function addressbookCreate(ManagerRegistry $doctrine, Request $request, string $username, ?int $id, TranslatorInterface $trans): Response
34+
public function addressbookCreate(ManagerRegistry $doctrine, Request $request, string $username, ?int $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response
3435
{
3536
$principal = $doctrine->getRepository(Principal::class)->findOneByUri(Principal::PREFIX.$username);
3637

@@ -61,6 +62,9 @@ public function addressbookCreate(ManagerRegistry $doctrine, Request $request, s
6162

6263
$this->addFlash('success', $trans->trans('addressbooks.saved'));
6364

65+
// Let's sync the user birthday calendar if needed
66+
$birthdayService->syncUser($username);
67+
6468
return $this->redirectToRoute('addressbook_index', ['username' => $username]);
6569
}
6670

@@ -73,7 +77,7 @@ public function addressbookCreate(ManagerRegistry $doctrine, Request $request, s
7377
}
7478

7579
#[Route('/{username}/delete/{id}', name: 'delete', requirements: ['id' => "\d+"])]
76-
public function addressbookDelete(ManagerRegistry $doctrine, string $username, string $id, TranslatorInterface $trans): Response
80+
public function addressbookDelete(ManagerRegistry $doctrine, string $username, string $id, TranslatorInterface $trans, BirthdayService $birthdayService): Response
7781
{
7882
$addressbook = $doctrine->getRepository(AddressBook::class)->findOneById($id);
7983
if (!$addressbook) {
@@ -93,6 +97,9 @@ public function addressbookDelete(ManagerRegistry $doctrine, string $username, s
9397
$entityManager->flush();
9498
$this->addFlash('success', $trans->trans('addressbooks.deleted'));
9599

100+
// Let's sync the user birthday calendar if needed
101+
$birthdayService->syncUser($username);
102+
96103
return $this->redirectToRoute('addressbook_index', ['username' => $username]);
97104
}
98105
}

src/Controller/Admin/CalendarController.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $rout
3232
// Separate shared calendars
3333
$calendars = [];
3434
$shared = [];
35+
$auto = [];
3536
foreach ($allCalendars as $calendar) {
36-
if (!$calendar->isShared()) {
37+
if ($calendar->isAutomaticallyGenerated()) {
38+
$auto[] = [
39+
'entity' => $calendar,
40+
'uri' => $router->generate('dav', ['path' => 'calendars/'.$username.'/'.$calendar->getUri()], UrlGeneratorInterface::ABSOLUTE_URL),
41+
];
42+
} elseif (!$calendar->isShared()) {
3743
$calendars[] = [
3844
'entity' => $calendar,
3945
'uri' => $router->generate('dav', ['path' => 'calendars/'.$username.'/'.$calendar->getUri()], UrlGeneratorInterface::ABSOLUTE_URL),
@@ -53,6 +59,7 @@ public function calendars(ManagerRegistry $doctrine, UrlGeneratorInterface $rout
5359
'calendars' => $calendars,
5460
'subscriptions' => $subscriptions,
5561
'shared' => $shared,
62+
'auto' => $auto,
5663
'principal' => $principal,
5764
'username' => $username,
5865
'allPrincipals' => $allPrincipalsExcept,

src/Entity/AddressBook.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ class AddressBook
3434
#[ORM\Column(type: 'string', length: 255)]
3535
private $synctoken;
3636

37+
#[ORM\Column(type: 'boolean', nullable: true, options: ['default' => false])]
38+
private $includedInBirthdayCalendar;
39+
3740
#[ORM\OneToMany(targetEntity: "App\Entity\Card", mappedBy: 'addressBook')]
3841
private $cards;
3942

@@ -43,6 +46,7 @@ class AddressBook
4346
public function __construct()
4447
{
4548
$this->synctoken = 1;
49+
$this->includedInBirthdayCalendar = false;
4650
$this->cards = new ArrayCollection();
4751
$this->changes = new ArrayCollection();
4852
}
@@ -76,6 +80,18 @@ public function setDisplayName(string $displayName): self
7680
return $this;
7781
}
7882

83+
public function isIncludedInBirthdayCalendar(): ?bool
84+
{
85+
return $this->includedInBirthdayCalendar;
86+
}
87+
88+
public function setIncludedInBirthdayCalendar(bool $includedInBirthdayCalendar): self
89+
{
90+
$this->includedInBirthdayCalendar = $includedInBirthdayCalendar;
91+
92+
return $this;
93+
}
94+
7995
public function getUri(): ?string
8096
{
8197
return $this->uri;

src/Entity/CalendarInstance.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Entity;
44

5+
use App\Constants;
56
use Doctrine\ORM\Mapping as ORM;
67
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
78
use Symfony\Component\Validator\Constraints as Assert;
@@ -141,6 +142,11 @@ public function isPublic(): bool
141142
return self::ACCESS_PUBLIC === $this->access;
142143
}
143144

145+
public function isAutomaticallyGenerated(): bool
146+
{
147+
return in_array($this->uri, [Constants::BIRTHDAY_CALENDAR_URI]);
148+
}
149+
144150
public function getDisplayName(): ?string
145151
{
146152
return $this->displayName;

src/Form/AddressBookType.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use App\Entity\AddressBook;
66
use Symfony\Component\Form\AbstractType;
7+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
78
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
89
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
910
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
@@ -28,6 +29,12 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
2829
'label' => 'form.displayName',
2930
'help' => 'form.name.help.carddav',
3031
])
32+
->add('includedInBirthdayCalendar', ChoiceType::class, [
33+
'label' => 'form.includedInBirthdayCalendar',
34+
'help' => 'form.includedInBirthdayCalendar.help',
35+
'required' => true,
36+
'choices' => ['yes' => true, 'no' => false],
37+
])
3138
->add('description', TextareaType::class, [
3239
'label' => 'form.description',
3340
'required' => false,

0 commit comments

Comments
 (0)