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
8 changes: 8 additions & 0 deletions app/config/packages/backoffice_menu.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@ parameters:
- admin_site_articles_list
- admin_site_articles_add
- admin_site_articles_edit
super_apero_infos:
nom: 'Super Apéro'
niveau: 'ROLE_ADMIN'
url: '/admin/super-apero'
extra_routes:
- admin_super_apero_list
- admin_super_apero_add
- admin_super_apero_edit
forum:
nom: 'Évènements'
niveau: 'ROLE_FORUM'
Expand Down
5 changes: 5 additions & 0 deletions app/config/packages/doctrine.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ doctrine:
is_bundle: false
dir: '%kernel.project_dir%/../sources/AppBundle/Site/Entity'
prefix: 'AppBundle\Site\Entity'
SuperApero:
type: attribute
is_bundle: false
dir: '%kernel.project_dir%/../sources/AppBundle/SuperApero/Entity'
prefix: 'AppBundle\SuperApero\Entity'

when@test:
doctrine:
Expand Down
4 changes: 4 additions & 0 deletions app/config/routing/admin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ admin_antennes:
resource: "admin_antennes.yml"
prefix: /antennes

admin_super_apero:
resource: "admin_super_apero.yml"
prefix: /super-apero

admin_members_reporting:
path: /members/reporting
defaults: {_controller: AppBundle\Controller\MembershipAdmin\ReportingAction}
Expand Down
20 changes: 20 additions & 0 deletions app/config/routing/admin_super_apero.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
admin_super_apero_list:
path: /
defaults: {_controller: AppBundle\Controller\Admin\SuperApero\ListAction}

admin_super_apero_add:
path: /add
defaults: {_controller: AppBundle\Controller\Admin\SuperApero\AddAction}

admin_super_apero_edit:
path: /edit/{id}
defaults: {_controller: AppBundle\Controller\Admin\SuperApero\EditAction}
requirements:
id: '\d+'

admin_super_apero_delete:
path: /delete/{id}
defaults: {_controller: AppBundle\Controller\Admin\SuperApero\DeleteAction}
methods: [POST]
requirements:
id: '\d+'
26 changes: 26 additions & 0 deletions db/migrations/20260217124904_create_super_apero_tables.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

use Phinx\Migration\AbstractMigration;

final class CreateSuperAperoTables extends AbstractMigration
{
public function change(): void
{
$this->table('super_apero')
->addColumn('date', 'date', ['null' => false])
->create();

$this->table('super_apero_meetup')
->addColumn('super_apero_id', 'integer', [
'null' => false,
'signed' => false,
])
->addColumn('antenne', 'string', ['limit' => 255, 'null' => false])
->addColumn('meetup_id', 'integer', ['null' => true])
->addColumn('description', 'text', ['null' => true])
->addForeignKey('super_apero_id', 'super_apero', 'id', ['delete' => 'CASCADE', 'update' => 'NO_ACTION'])
->create();
}
}
43 changes: 43 additions & 0 deletions sources/AppBundle/Controller/Admin/SuperApero/AddAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\SuperApero;

use AppBundle\AuditLog\Audit;
use AppBundle\SuperApero\Entity\SuperApero;
use AppBundle\SuperApero\Entity\Repository\SuperAperoRepository;
use AppBundle\SuperApero\Form\SuperAperoType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class AddAction extends AbstractController
{
public function __construct(
private readonly SuperAperoRepository $superAperoRepository,
private readonly Audit $audit,
) {}

public function __invoke(Request $request): Response
{
$superApero = new SuperApero();
$form = $this->createForm(SuperAperoType::class, $superApero);

$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->superAperoRepository->save($superApero);
$this->audit->log('Ajout du Super Apéro ' . $superApero->annee());
$this->addFlash('notice', 'Le Super Apéro ' . $superApero->annee() . ' a été ajouté');

return $this->redirectToRoute('admin_super_apero_list');
}

return $this->render('admin/super_apero/form.html.twig', [
'form' => $form->createView(),
'formTitle' => 'Ajouter un Super Apéro',
'submitLabel' => 'Ajouter',
]);
}
}
41 changes: 41 additions & 0 deletions sources/AppBundle/Controller/Admin/SuperApero/DeleteAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\SuperApero;

use AppBundle\AuditLog\Audit;
use AppBundle\SuperApero\Entity\Repository\SuperAperoRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;

final class DeleteAction extends AbstractController
{
public function __construct(
private readonly SuperAperoRepository $superAperoRepository,
private readonly Audit $audit,
) {}

public function __invoke(int $id, Request $request): RedirectResponse
{
if (!$this->isCsrfTokenValid('super_apero_delete', $request->request->getString('_token'))) {
$this->addFlash('error', 'Token invalide');

return $this->redirectToRoute('admin_super_apero_list');
}

$superApero = $this->superAperoRepository->find($id);

if ($superApero === null) {
throw $this->createNotFoundException('Super apéro non trouvé');
}

$annee = $superApero->annee();
$this->superAperoRepository->delete($superApero);
$this->audit->log('Suppression du Super Apéro ' . $annee);
$this->addFlash('notice', 'Le Super Apéro ' . $annee . ' a été supprimé');

return $this->redirectToRoute('admin_super_apero_list');
}
}
47 changes: 47 additions & 0 deletions sources/AppBundle/Controller/Admin/SuperApero/EditAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\SuperApero;

use AppBundle\AuditLog\Audit;
use AppBundle\SuperApero\Entity\Repository\SuperAperoRepository;
use AppBundle\SuperApero\Entity\SuperApero;
use AppBundle\SuperApero\Form\SuperAperoType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

final class EditAction extends AbstractController
{
public function __construct(
private readonly SuperAperoRepository $superAperoRepository,
private readonly Audit $audit,
) {}

public function __invoke(int $id, Request $request): Response
{
$superApero = $this->superAperoRepository->find($id);

if (!$superApero instanceof SuperApero) {
throw $this->createNotFoundException('Super apéro non trouvé');
}

$form = $this->createForm(SuperAperoType::class, $superApero);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$this->superAperoRepository->save($superApero);
$this->audit->log('Modification du Super Apéro ' . $superApero->annee());
$this->addFlash('notice', 'Le Super Apéro ' . $superApero->annee() . ' a été modifié');

return $this->redirectToRoute('admin_super_apero_list');
}

return $this->render('admin/super_apero/form.html.twig', [
'form' => $form->createView(),
'formTitle' => 'Modifier le Super Apéro ' . $superApero->annee(),
'submitLabel' => 'Modifier',
]);
}
}
30 changes: 30 additions & 0 deletions sources/AppBundle/Controller/Admin/SuperApero/ListAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace AppBundle\Controller\Admin\SuperApero;

use AppBundle\SuperApero\Entity\Repository\SuperAperoRepository;
use AppBundle\SuperApero\Entity\SuperApero;
use Psr\Clock\ClockInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;

final class ListAction extends AbstractController
{
public function __construct(
private readonly SuperAperoRepository $superAperoRepository,
private readonly ClockInterface $clock,
) {}

public function __invoke(): Response
{
$currentYear = (int) $this->clock->now()->format('Y');

return $this->render('admin/super_apero/index.html.twig', [
'aperos' => $this->superAperoRepository->getAllSortedByYear(),
'currentYear' => $currentYear,
'hasSuperAperoForCurrentYear' => $this->superAperoRepository->findOneByYear($currentYear) instanceof SuperApero,
]);
}
}
48 changes: 11 additions & 37 deletions sources/AppBundle/Controller/Website/Static/SuperAperoAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,30 @@

namespace AppBundle\Controller\Website\Static;

use AppBundle\SuperApero\Entity\Repository\SuperAperoRepository;
use AppBundle\Twig\ViewRenderer;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

final readonly class SuperAperoAction
{
public function __construct(
private ViewRenderer $view,
#[Autowire('%super_apero_csv_url%')]
private string $superAperoCsvUrl,
private SuperAperoRepository $superAperoRepository,
private UrlGeneratorInterface $urlGenerator,
) {}

public function __invoke(): Response
{
return $this->view->render('site/superapero.html.twig', [
'aperos' => $this->getAperos($this->superAperoCsvUrl),
]);
}
$superApero = $this->superAperoRepository->findActive();

/**
* @return array{code: (string | null), content: (string | null), meetup_id?: (string | null)}[]
*/
protected function getAperos(string $url): array
{
$fp = fopen($url, 'rb');
if (!$fp) {
throw new \RuntimeException("Error opening spreadsheet");
if ($superApero === null) {
return new RedirectResponse($this->urlGenerator->generate('home'));
}

$aperos = [];

while (false !== ($row = fgetcsv($fp))) {
if (trim((string) $row[0]) === '') {
continue;
}

[$code, $meeetupId, $content] = $row;

$apero = [
'code' => mb_strtolower((string) $code),
'content' => $content,
];

if (strlen(trim((string) $meeetupId)) !== 0) {
$apero['meetup_id'] = $meeetupId;
}

$aperos[] = $apero;
}

return $aperos;
return $this->view->render('site/superapero.html.twig', [
'superApero' => $superApero,
]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace AppBundle\SuperApero\Entity\Repository;

use AppBundle\Doctrine\EntityRepository;
use AppBundle\SuperApero\Entity\SuperApero;
use DateTimeImmutable;
use Doctrine\Persistence\ManagerRegistry;
use Psr\Clock\ClockInterface;

/**
* @extends EntityRepository<SuperApero>
*/
final class SuperAperoRepository extends EntityRepository
{
private ClockInterface $clock;

public function __construct(ManagerRegistry $registry, ClockInterface $clock)
{
parent::__construct($registry, SuperApero::class);

$this->clock = $clock;
}

/**
* @return array<SuperApero>
*/
public function getAllSortedByYear(): array
{
return $this->createQueryBuilder('s')
->orderBy('s.date', 'desc')
->getQuery()
->execute();
}

public function findOneByYear(int $year): ?SuperApero
{
return $this->createQueryBuilder('s')
->where('s.date >= :yearStart')
->andWhere('s.date <= :yearEnd')
->setParameter('yearStart', new DateTimeImmutable($year . '-01-01'))
->setParameter('yearEnd', new DateTimeImmutable($year . '-12-31'))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}

public function findActive(): ?SuperApero
{
$now = $this->clock->now();

return $this->createQueryBuilder('s')
// Le prochain Super Apéro doit être aujourd'hui ou dans le futur
->where('s.date >= :now')
// Et doit avoir lieu dans l'année courante
->andWhere('s.date >= :yearStart')
->andWhere('s.date <= :yearEnd')
// Et doit avoir au moins un meetup associé
->innerJoin('s.meetups', 'm')
->setParameter('now', new DateTimeImmutable($now->format('Y-m-d')))
->setParameter('yearStart', new DateTimeImmutable($now->format('Y') . '-01-01'))
->setParameter('yearEnd', new DateTimeImmutable($now->format('Y') . '-12-31'))
->setMaxResults(1)
->getQuery()
->getOneOrNullResult();
}
}
Loading
Loading