Skip to content

Commit 609ec84

Browse files
committed
Thématisation de la page programme
1 parent 67819fa commit 609ec84

18 files changed

Lines changed: 846 additions & 10 deletions

app/config/packages/backoffice_menu.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ parameters:
181181
- admin_talk_list
182182
- admin_talk_add
183183
- admin_talk_edit
184+
admin_event_themes_list:
185+
nom: 'Thèmes'
186+
niveau: 'ROLE_FORUM'
187+
url: '/admin/event/themes'
188+
extra_routes:
189+
- admin_event_themes_list
190+
- admin_event_themes_add
191+
- admin_event_themes_edit
184192
forum_vote_github:
185193
nom: 'Votes visiteurs'
186194
niveau: 'ROLE_FORUM'

app/config/routing/admin_event.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,5 +167,24 @@ admin_event_sessions_delete:
167167
requirements:
168168
id: \d+
169169

170+
admin_event_themes_add:
171+
path: /themes/add
172+
defaults:
173+
_controller: AppBundle\Controller\Admin\Event\EventThemeAddEditAction
174+
id: null
175+
176+
admin_event_themes_edit:
177+
path: /themes/edit/{id}
178+
defaults: {_controller: AppBundle\Controller\Admin\Event\EventThemeAddEditAction}
179+
requirements:
180+
id: \d+
181+
182+
admin_event_themes_list:
183+
path: /themes/
184+
requirements:
185+
id: \d+
186+
defaults:
187+
_controller: AppBundle\Controller\Admin\Event\EventThemeAction
188+
170189
admin_event_ticket:
171190
resource: "admin_event_ticket.yml"
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Phinx\Migration\AbstractMigration;
6+
7+
final class AddEventThemesFeature extends AbstractMigration
8+
{
9+
public function change(): void
10+
{
11+
$this
12+
->table('afup_forum')
13+
->addColumn('has_themes', 'boolean', ['null' => false, 'default' => false])
14+
->save()
15+
;
16+
$this
17+
->table('afup_sessions')
18+
->addColumn('theme', 'integer', ['null' => true])
19+
->save()
20+
;
21+
$this
22+
->table('afup_conference_theme')
23+
->addColumn('id_forum', 'integer', ['null' => false, 'signed' => false])
24+
->addColumn('name', 'string', ['limit' => 255])
25+
->addColumn('description', 'text', ['null' => true])
26+
->addColumn('priority', 'integer', ['null' => false, 'default' => 0])
27+
->save()
28+
;
29+
}
30+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AppBundle\Controller\Admin\Event;
6+
7+
use AppBundle\Event\AdminEventSelection;
8+
use AppBundle\Event\Model\Event;
9+
use AppBundle\Event\Model\Repository\EventThemeRepository;
10+
use AppBundle\Event\Model\Repository\TalkRepository;
11+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
12+
use Symfony\Component\HttpFoundation\JsonResponse;
13+
use Symfony\Component\HttpFoundation\Request;
14+
use Symfony\Component\HttpFoundation\Response;
15+
16+
class EventThemeAction extends AbstractController
17+
{
18+
public function __construct(
19+
private readonly EventThemeRepository $eventThemeRepository,
20+
private readonly TalkRepository $talkRepository,
21+
) {}
22+
23+
public function __invoke(Request $request, AdminEventSelection $eventSelection): Response
24+
{
25+
$event = $eventSelection->event;
26+
27+
// Handle AJAX requests for updating data
28+
if ($request->isXmlHttpRequest()) {
29+
return $this->handleAjaxRequest($request, $event);
30+
}
31+
if ($request->getMethod() === 'POST' && $request->request->has('delete')) {
32+
$theme = $this->eventThemeRepository->get($request->request->getInt('theme_id'));
33+
if ($theme === null) {
34+
$this->addFlash('error', 'Thème introuvable.');
35+
} else {
36+
$name = $theme->getName();
37+
$this->eventThemeRepository->delete($theme);
38+
$this->addFlash('notice', sprintf('Le thème "%s" a été supprimé.', $name));
39+
}
40+
return $this->redirectToRoute('admin_event_themes_list');
41+
}
42+
43+
$eventId = $event->getId() ?? 0;
44+
$themes = $this->eventThemeRepository->getByThemesOrderedByPriority($eventId);
45+
$scheduledTalks = $this->talkRepository->getScheduledTalksByEvent($eventId);
46+
47+
return $this->render('admin/event/theme_list.html.twig', [
48+
'themes' => $themes,
49+
'scheduled_talks' => $scheduledTalks,
50+
'event' => $event,
51+
'event_select_form' => $eventSelection->selectForm(),
52+
]);
53+
}
54+
55+
private function handleAjaxRequest(Request $request, Event $event): JsonResponse
56+
{
57+
$action = $request->request->get('action');
58+
59+
return match ($action) {
60+
'update_theme_priority' => $this->updateThemePriority($request),
61+
'update_talk_theme' => $this->updateTalkTheme($request),
62+
default => new JsonResponse(['error' => 'Action non reconnue'], 400),
63+
};
64+
}
65+
66+
private function updateThemePriority(Request $request): JsonResponse
67+
{
68+
$themeId = $request->request->getInt('theme_id');
69+
$priority = $request->request->getInt('priority');
70+
71+
$theme = $this->eventThemeRepository->get($themeId);
72+
if (!$theme) {
73+
return new JsonResponse(['error' => 'Thème non trouvé'], 404);
74+
}
75+
76+
$theme->setPriority($priority);
77+
$this->eventThemeRepository->save($theme);
78+
79+
return new JsonResponse(['success' => true]);
80+
}
81+
82+
private function updateTalkTheme(Request $request): JsonResponse
83+
{
84+
$talkId = $request->request->getInt('talk_id');
85+
$themeId = $request->request->get('theme_id');
86+
87+
$talk = $this->talkRepository->get($talkId);
88+
if (!$talk) {
89+
return new JsonResponse(['error' => 'Conférence non trouvée'], 404);
90+
}
91+
92+
$talk->setTheme($themeId ? (int) $themeId : null);
93+
$this->talkRepository->save($talk);
94+
95+
return new JsonResponse(['success' => true]);
96+
}
97+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AppBundle\Controller\Admin\Event;
6+
7+
use AppBundle\Event\Form\EventThemeType;
8+
use AppBundle\Event\Model\EventTheme;
9+
use AppBundle\Event\Model\Repository\EventRepository;
10+
use AppBundle\Event\Model\Repository\EventThemeRepository;
11+
use AppBundle\Event\Model\Repository\TalkRepository;
12+
use CCMBenchmark\TingBundle\Attribute\MapEntity;
13+
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
14+
use Symfony\Component\HttpFoundation\Request;
15+
use Symfony\Component\HttpFoundation\Response;
16+
17+
class EventThemeAddEditAction extends AbstractController
18+
{
19+
public function __construct(
20+
private readonly EventThemeRepository $eventThemeRepository,
21+
private readonly EventRepository $eventRepository,
22+
private readonly TalkRepository $talkRepository,
23+
) {}
24+
25+
public function __invoke(Request $request, #[MapEntity] ?EventTheme $eventTheme = null): Response
26+
{
27+
$new = false;
28+
if ($eventTheme === null) {
29+
$new = true;
30+
$eventTheme = new EventTheme();
31+
if ($request->query->has('idForum')) {
32+
$eventTheme->setIdForum($request->query->getInt('idForum'));
33+
}
34+
} else {
35+
$event = $this->eventRepository->get($eventTheme->getIdForum());
36+
if ($event !== null) {
37+
$talks = $this->talkRepository->getByEventWithSpeakers($event, false, false, $eventTheme->getId());
38+
}
39+
}
40+
41+
$form = $this->createForm(EventThemeType::class, $eventTheme);
42+
43+
$form->handleRequest($request);
44+
if ($form->isSubmitted() && $form->isValid()) {
45+
$this->eventThemeRepository->save($eventTheme);
46+
47+
$this->addFlash('notice', 'Thème ' . ($new ? 'ajouté' : 'modifié'));
48+
return $this->redirectToRoute('admin_event_themes_list', ['id' => $eventTheme->getIdForum()]);
49+
}
50+
51+
return $this->render('admin/event/theme_add_edit.html.twig', [
52+
'form' => $form->createView(),
53+
'eventTheme' => $eventTheme,
54+
'new' => $new,
55+
'talks' => $talks ?? null,
56+
]);
57+
}
58+
}

sources/AppBundle/Controller/Event/Blog/ProgramAction.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use AppBundle\Controller\Event\EventActionHelper;
88
use AppBundle\Event\JsonLd;
9+
use AppBundle\Event\Model\Repository\EventThemeRepository;
910
use AppBundle\Event\Model\Repository\TalkRepository;
1011
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
1112
use Symfony\Component\HttpFoundation\Request;
@@ -17,13 +18,20 @@ public function __construct(
1718
private readonly JsonLd $jsonLd,
1819
private readonly EventActionHelper $eventActionHelper,
1920
private readonly TalkRepository $talkRepository,
21+
private readonly EventThemeRepository $eventThemeRepository,
2022
) {}
2123

2224
public function __invoke(Request $request, $eventSlug): Response
2325
{
2426
$event = $this->eventActionHelper->getEvent($eventSlug);
2527
$jsonld = $this->jsonLd->getDataForEvent($event);
26-
$talkAggregates = $this->talkRepository->getByEventWithSpeakers($event, $request->query->getBoolean('apply-publication-date-filters', true));
28+
$talkAggregates = $this->talkRepository->getByEventWithSpeakers($event, $request->query->getBoolean('apply-publication-date-filters', true), $event->getHasThemes());
29+
$themes = null;
30+
if ($event->getHasThemes()) {
31+
$themes = iterator_to_array($this->eventThemeRepository->getBy(['idForum' => $event->getId()]));
32+
usort($themes, fn($a, $b): int => $a->getPriority() === $b->getPriority() ? $a->getName() <=> $b->getName() : $a->getPriority() <=> $b->getPriority());
33+
$themes = array_combine(array_map(fn($theme): int => (int) $theme->getId(), $themes), $themes);
34+
}
2735
$now = new \DateTime();
2836

2937
return $this->render(
@@ -34,6 +42,7 @@ public function __invoke(Request $request, $eventSlug): Response
3442
'jsonld' => $jsonld,
3543
'speakersPagePrefix' => $request->query->get('speakers-page-prefix', '/' . $event->getPath() . '/speakers/'),
3644
'display_joindin_links' => $now >= $event->getDateStart() && $now <= \DateTimeImmutable::createFromMutable($event->getDateEnd())->modify('+10 days'),
45+
'themes' => $themes,
3746
],
3847
);
3948
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AppBundle\Event\Form;
6+
7+
use AppBundle\Event\Form\Support\EventHelper;
8+
use AppBundle\Event\Model\Event;
9+
use AppBundle\Event\Model\EventTheme;
10+
use AppBundle\Event\Model\Repository\EventRepository;
11+
use Symfony\Component\Form\AbstractType;
12+
use Symfony\Component\Form\CallbackTransformer;
13+
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
14+
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
15+
use Symfony\Component\Form\Extension\Core\Type\TextType;
16+
use Symfony\Component\Form\FormBuilderInterface;
17+
use Symfony\Component\OptionsResolver\OptionsResolver;
18+
use Symfony\Component\Validator\Constraints as Assert;
19+
20+
/** @extends AbstractType<EventTheme> */
21+
class EventThemeType extends AbstractType
22+
{
23+
private readonly EventHelper $eventHelper;
24+
25+
public function __construct(private readonly EventRepository $eventRepository)
26+
{
27+
$this->eventHelper = new EventHelper();
28+
}
29+
public function buildForm(FormBuilderInterface $builder, array $options): void
30+
{
31+
/** @var array<Event> $events */
32+
$events = iterator_to_array($this->eventRepository->getAllActive());
33+
$idForumField = $builder->create('idForum', ChoiceType::class, [
34+
'label' => 'Évènement',
35+
'choice_label' => 'title',
36+
'choice_value' => fn(?Event $event): ?string => $event?->getId() !== null ? (string) $event->getId() : null,
37+
'choices' => $this->eventHelper->sortEventsByStartDate($events),
38+
'group_by' => fn(Event $choice): string => $this->eventHelper->groupByYear($choice),
39+
]);
40+
41+
$idForumField->addModelTransformer(new CallbackTransformer(
42+
fn(?int $idForum): ?Event => $idForum ? $this->eventRepository->getOneBy(['id' => $idForum]) : null,
43+
fn(?Event $event): ?int => $event?->getId(),
44+
));
45+
$builder->add($idForumField)
46+
->add('name', TextType::class, [
47+
'label' => 'Nom du thème',
48+
'constraints' => [new Assert\NotBlank(message: 'Titre du forum manquant')],
49+
])
50+
->add('description', TextareaType::class, [
51+
'label' => 'Description',
52+
'help' => 'Le thème de description apparait sur la page de programme',
53+
'constraints' => [new Assert\NotBlank(), new Assert\Length(max: 600)],
54+
'attr' => ['class' => 'simplemde'],
55+
])
56+
;
57+
}
58+
59+
public function configureOptions(OptionsResolver $resolver): void
60+
{
61+
$resolver->setDefaults([
62+
'data_class' => EventTheme::class,
63+
]);
64+
}
65+
}

sources/AppBundle/Event/Form/EventType.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
116116
'label' => 'Date annonce planning',
117117
'required' => false,
118118
])
119+
->add('hasThemes', CheckboxType::class, [
120+
'label' => 'Activer le support des thèmes',
121+
'required' => false,
122+
])
119123
->add('dateEndSalesSponsorToken', DateTimeType::class, [
120124
'widget' => 'single_text',
121125
'label' => 'Date fin saisie token sponsor',

sources/AppBundle/Event/Model/Event.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ class Event implements NotifyPropertyInterface
9999

100100
private ?DateTime $archivedAt = null;
101101

102+
private bool $hasThemes = false;
103+
102104
/**
103105
* @return int
104106
*/
@@ -730,6 +732,19 @@ public function isOnline(): bool
730732
return str_contains($this->getPath(), 'enligne');
731733
}
732734

735+
public function getHasThemes(): bool
736+
{
737+
return $this->hasThemes;
738+
}
739+
740+
public function setHasThemes(bool $hasThemes): self
741+
{
742+
$this->propertyChanged('hasThemes', $this->hasThemes, $hasThemes);
743+
$this->hasThemes = $hasThemes;
744+
745+
return $this;
746+
}
747+
733748
public static function getInscriptionAttachmentDir(): string
734749
{
735750
return __DIR__ . '/../../../../htdocs/uploads/mail_inscription_attachment/';

0 commit comments

Comments
 (0)