diff --git a/src/Controller/EpisodeController.php b/src/Controller/EpisodeController.php new file mode 100644 index 0000000..e4bca37 --- /dev/null +++ b/src/Controller/EpisodeController.php @@ -0,0 +1,151 @@ +render('episode/index.html.twig', [ + 'episodes' => $episodeRepository->findAll(), + ]); + } + + #[Route('/new', name: 'app_episode_new', methods: ['GET', 'POST'])] + public function new( + Request $request, + EntityManagerInterface $entityManager, + PodcastRepository $podcastRepository, + SluggerInterface $slugger, + ): Response + { + $queryPodcastId = $request->query->getInt('podcast', 0); + + $episode = new Episode(); + $form = $this->createForm( + EpisodeType::class, + $episode, + [ + 'owner' => $this->getUser(), + 'selectedPodcast' => $queryPodcastId, + ] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $this->logger->info("FLUMZO " . json_encode($form->getData())); + + // Check that the user is the owner of the podcast this episode should belong to. + $podcastId = $form->get('podcast')->getData(); + $podcast = $podcastRepository->find($podcastId ?? $queryPodcastId); + if ( + $podcast === null + || $podcast->getOwner()->getId() !== $this->getUser()->getId() + ) { + $form->get('podcast')->addError(new FormError('Invalid podcast.')); + return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); + } + $episode->setPodcast($podcast); + + // Ensure the uploaded audio file is saved properly. + if (!$this->handleAudioChange($form, $episode, $slugger)) { + $form->get('logo')->addError(new FormError('Could not upload audio.')); + return $this->render('episode/new.html.twig', ['episode' => $episode, 'form' => $form]); + } + + $entityManager->persist($episode); + $entityManager->flush(); + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('episode/new.html.twig', [ + 'episode' => $episode, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_episode_show', methods: ['GET'])] + public function show(Episode $episode): Response + { + return $this->render('episode/show.html.twig', [ + 'episode' => $episode, + ]); + } + + #[Route('/{id}/edit', name: 'app_episode_edit', methods: ['GET', 'POST'])] + public function edit(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response + { + $form = $this->createForm( + EpisodeType::class, + $episode, + ['selectedPodcast' => $episode->getPodcast()->getId()] + ); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->flush(); + + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->render('episode/edit.html.twig', [ + 'episode' => $episode, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_episode_delete', methods: ['POST'])] + public function delete(Request $request, Episode $episode, EntityManagerInterface $entityManager): Response + { + if ($this->isCsrfTokenValid('delete'.$episode->getId(), $request->request->get('_token'))) { + $entityManager->remove($episode); + $entityManager->flush(); + } + + return $this->redirectToRoute('app_episode_index', [], Response::HTTP_SEE_OTHER); + } + + protected function handleAudioChange( + FormInterface $form, + Episode $episode, + SluggerInterface $slugger + ): bool + { + $audioFile = $form->get('audio')->getData(); + if ($audioFile) { + $originalFilename = pathinfo($audioFile->getClientOriginalName(), PATHINFO_FILENAME); + $safeFilename = $slugger->slug($originalFilename); + $newFilename = $safeFilename . '-' . uniqid() . '.' . $audioFile->guessExtension(); + + try { + $audioFile->move($this->getParameter('audio_directory'), $newFilename); + } catch (FileException $e) { + return false; + } + + $episode->setAudioFilename($newFilename); + } + + return true; + } +} diff --git a/src/Controller/PodcastController.php b/src/Controller/PodcastController.php index 4032406..da401b6 100644 --- a/src/Controller/PodcastController.php +++ b/src/Controller/PodcastController.php @@ -4,6 +4,7 @@ namespace App\Controller; use App\Entity\Podcast; use App\Form\PodcastType; +use App\Repository\EpisodeRepository; use App\Repository\PodcastRepository; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -97,7 +98,11 @@ class PodcastController extends AbstractController } #[Route('/{id}', name: 'app_podcast_delete', methods: ['POST'])] - public function delete(Request $request, Podcast $podcast, EntityManagerInterface $entityManager): Response + public function delete( + Request $request, + Podcast $podcast, + EntityManagerInterface $entityManager + ): Response { if ($this->isCsrfTokenValid('delete'.$podcast->getId(), $request->request->get('_token'))) { $entityManager->remove($podcast); @@ -130,4 +135,16 @@ class PodcastController extends AbstractController return true; } + + #[Route('/{id}/episodes', name: 'app_podcast_episodes_index', methods: ['GET'])] + public function episodes_index( + Podcast $podcast, + EpisodeRepository $episodeRepository + ): Response + { + return $this->render('episode/index.html.twig', [ + 'podcast' => $podcast, + 'episodes' => $episodeRepository->findBy(['podcast' => $podcast]), + ]); + } } diff --git a/src/Form/EpisodeType.php b/src/Form/EpisodeType.php new file mode 100644 index 0000000..bdd23ed --- /dev/null +++ b/src/Form/EpisodeType.php @@ -0,0 +1,71 @@ +add('title') + ->add('description') + ->add('audio', FileType::class, [ + 'help' => 'The audio file of your episode.', + 'mapped' => false, + 'required' => false, + 'constraints' => new File([ + 'maxSize' => '4m', + 'mimeTypes' => ['audio/mpeg', 'audio/ogg', 'audio/opus', 'audio/aac', 'audio/flac', 'audio/webm'], + 'mimeTypesMessage' => 'Please select an audio file (MP3, OGG audio, Opus, AAC, FLAC, WebM audio).', + ]), + ]) + ->add('publicationDate') + ->add('podcast', EntityType::class, [ + 'help' => 'The podcast this episode belongs to. Cannot be changed after creation.', + 'class' => Podcast::class, + 'query_builder' => function (EntityRepository $er) use ($owner, $podcastId): QueryBuilder { + $qb = $er->createQueryBuilder('p'); + if ($owner !== null) { + $qb + ->andWhere('p.owner = :ownerId') + ->setParameter('ownerId', $owner->getId()); + } + if ($podcastId !== null) { + $qb + ->andWhere('p.id = :podcastId') + ->setParameter('podcastId', $podcastId); + } + return $qb->orderBy('p.name', 'ASC'); + + }, + 'placeholder' => false, + 'disabled' => $podcastId !== null, + 'empty_data' => "$podcastId", + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => Episode::class, + 'owner' => null, + 'selectedPodcast' => null, + ]); + $resolver->setAllowedTypes('selectedPodcast', 'int'); + } +} diff --git a/templates/base.html.twig b/templates/base.html.twig index 7f9ff9b..efff7b6 100644 --- a/templates/base.html.twig +++ b/templates/base.html.twig @@ -13,6 +13,12 @@ {% endblock %} + {% if app.user %} + + {% endif %} {% block body %}{% endblock %} diff --git a/templates/episode/_delete_form.html.twig b/templates/episode/_delete_form.html.twig new file mode 100644 index 0000000..3945db5 --- /dev/null +++ b/templates/episode/_delete_form.html.twig @@ -0,0 +1,4 @@ +
+ + +
diff --git a/templates/episode/_form.html.twig b/templates/episode/_form.html.twig new file mode 100644 index 0000000..bf20b98 --- /dev/null +++ b/templates/episode/_form.html.twig @@ -0,0 +1,4 @@ +{{ form_start(form) }} + {{ form_widget(form) }} + +{{ form_end(form) }} diff --git a/templates/episode/edit.html.twig b/templates/episode/edit.html.twig new file mode 100644 index 0000000..9063978 --- /dev/null +++ b/templates/episode/edit.html.twig @@ -0,0 +1,13 @@ +{% extends 'base.html.twig' %} + +{% block title %}Edit Episode{% endblock %} + +{% block body %} +

Edit Episode

+ + {{ include('episode/_form.html.twig', {'button_label': 'Update'}) }} + + Go back to list + + {{ include('episode/_delete_form.html.twig') }} +{% endblock %} diff --git a/templates/episode/index.html.twig b/templates/episode/index.html.twig new file mode 100644 index 0000000..1ebbf53 --- /dev/null +++ b/templates/episode/index.html.twig @@ -0,0 +1,56 @@ +{% extends 'base.html.twig' %} + +{% block title %}Episode index{% endblock %} + +{% block body %} +

+ {% if podcast is defined %} + Episodes for {{ podcast.name }} + {% else %} + All your episodes + {% endif %} +

+ + + + + + + + + + + + {% for episode in episodes %} + + + + + + + {% else %} + + + + {% endfor %} + +
IDTitlePublication dateActions
{{ episode.id }}{{ episode.title }}{{ episode.publicationDate ? episode.publicationDate|date('Y-m-d H:i:s') : '' }} + + +
No episodes found.
+ + {% if podcast is defined %} + + Back to podcast + {% else %} + +

+ To manage episodes of a specific podcast, open the podcast page and use + the “Manage podcasts” link. +

+ {% endif %} + +{% endblock %} diff --git a/templates/episode/new.html.twig b/templates/episode/new.html.twig new file mode 100644 index 0000000..16af182 --- /dev/null +++ b/templates/episode/new.html.twig @@ -0,0 +1,19 @@ +{% extends 'base.html.twig' %} + +{% block title %}New episode{% endblock %} + +{% block stylesheets %} + {{ parent() }} + +{% endblock %} + + +{% block body %} +

Create new episode

+ + {{ include('episode/_form.html.twig') }} + + Go back to list +{% endblock %} diff --git a/templates/episode/show.html.twig b/templates/episode/show.html.twig new file mode 100644 index 0000000..254a7f2 --- /dev/null +++ b/templates/episode/show.html.twig @@ -0,0 +1,49 @@ +{% extends 'base.html.twig' %} + +{% block title %}Episode “{{ episode.title }}”{% endblock %} + +{% block stylesheets %} + {{ parent() }} + +{% endblock %} + +{% block body %} +

Episode “{{ episode.title }}”

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID{{ episode.id }}
Title{{ episode.title }}
Description{{ episode.description }}
Audio file name{{ episode.audioFilename }}
AudioTODO
Publication date{{ episode.publicationDate ? episode.publicationDate|date('Y-m-d H:i:s') : '' }}
+ + back to list + + edit + + {{ include('episode/_delete_form.html.twig') }} +{% endblock %} diff --git a/templates/podcast/edit.html.twig b/templates/podcast/edit.html.twig index c3153f5..123904a 100644 --- a/templates/podcast/edit.html.twig +++ b/templates/podcast/edit.html.twig @@ -1,13 +1,13 @@ {% extends 'base.html.twig' %} -{% block title %}Edit Podcast{% endblock %} +{% block title %}Edit podcast {{ podcast.name }}{% endblock %} {% block body %} -

Edit Podcast

+

Edit podcast {{ podcast.name }}

{{ include('podcast/_form.html.twig', {'button_label': 'Update'}) }} - back to list + Go back to list {{ include('podcast/_delete_form.html.twig') }} {% endblock %} diff --git a/templates/podcast/index.html.twig b/templates/podcast/index.html.twig index ec396b6..97901cb 100644 --- a/templates/podcast/index.html.twig +++ b/templates/podcast/index.html.twig @@ -15,11 +15,11 @@ - + - + @@ -44,5 +44,5 @@
IdID Name Author LogoactionsActions
- + {% endblock %} diff --git a/templates/podcast/new.html.twig b/templates/podcast/new.html.twig index d2b229a..c78a258 100644 --- a/templates/podcast/new.html.twig +++ b/templates/podcast/new.html.twig @@ -1,11 +1,11 @@ {% extends 'base.html.twig' %} -{% block title %}New Podcast{% endblock %} +{% block title %}New podcast{% endblock %} {% block body %} -

Create new Podcast

+

Create a new podcast

{{ include('podcast/_form.html.twig') }} - back to list + Go back to list {% endblock %} diff --git a/templates/podcast/show.html.twig b/templates/podcast/show.html.twig index b7e5386..71650a2 100644 --- a/templates/podcast/show.html.twig +++ b/templates/podcast/show.html.twig @@ -1,6 +1,6 @@ {% extends 'base.html.twig' %} -{% block title %}Podcast{% endblock %} +{% block title %}Podcast {{ podcast.name }}{% endblock %} {% block stylesheets %} {{ parent() }} @@ -15,7 +15,7 @@ - + @@ -43,7 +43,7 @@ - + @@ -54,12 +54,25 @@ + + + +
IdID {{ podcast.id }}
{{ podcast.email }}
LogoFilenameLogo file name {{ podcast.logoFilename }}
Owner {{ podcast.owner }}
Episodes + Manage episodes +
    + {% for episode in podcast.episodes %} +
  1. {{ episode.title }}
  2. + {% endfor %} +
+
- back to list + Go back to list - edit + {{ include('podcast/_delete_form.html.twig') }} {% endblock %}