From d0cb964b0fee894570f0c7bf98d4c9852faef892 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Wed, 5 Jan 2022 14:58:53 +0000
Subject: [PATCH] refactor: harmonize redirects after submitting forms

go back to form after submitting an edit form
---
 app/Views/Components/DropdownMenu.php         |  4 +-
 app/Views/Components/Forms/MarkdownEditor.php |  5 ++-
 app/Views/Components/Forms/MultiSelect.php    |  4 +-
 app/Views/Components/Forms/Select.php         |  2 +-
 app/Views/Components/Forms/Textarea.php       |  2 +-
 app/Views/errors/html/error_404.php           |  2 +-
 .../Controllers/ContributorController.php     | 11 ++++--
 .../Admin/Controllers/EpisodeController.php   | 38 +++++++++++++------
 modules/Admin/Controllers/PageController.php  |  2 +-
 .../Admin/Controllers/PersonController.php    | 11 ++++--
 .../Admin/Controllers/PodcastController.php   | 10 ++++-
 .../Admin/Controllers/SoundbiteController.php | 10 ++++-
 modules/Admin/Language/en/Contributor.php     |  4 +-
 modules/Admin/Language/en/Episode.php         |  4 ++
 modules/Admin/Language/en/Fediverse.php       |  6 +++
 modules/Admin/Language/en/Page.php            |  1 +
 modules/Admin/Language/en/Person.php          |  5 +++
 modules/Admin/Language/en/Podcast.php         |  5 +++
 modules/Admin/Language/en/Soundbite.php       |  4 ++
 modules/Admin/Language/en/VideoClip.php       |  4 ++
 modules/Admin/Language/fr/Contributor.php     |  4 +-
 modules/Admin/Language/fr/Episode.php         |  4 ++
 modules/Admin/Language/fr/Fediverse.php       |  6 +++
 modules/Admin/Language/fr/Page.php            |  3 +-
 modules/Admin/Language/fr/Person.php          |  5 +++
 modules/Admin/Language/fr/Podcast.php         |  5 +++
 modules/Admin/Language/fr/Soundbite.php       |  4 ++
 modules/Admin/Language/fr/VideoClip.php       |  4 ++
 .../Fediverse/Controllers/BlockController.php | 26 +++++++++----
 .../Fediverse/Controllers/PostController.php  |  5 ++-
 themes/cp_admin/episode/_card.php             |  9 ++++-
 themes/cp_admin/episode/list.php              |  9 ++++-
 themes/cp_admin/episode/persons.php           |  8 ++--
 themes/cp_admin/person/edit.php               |  3 +-
 themes/cp_admin/podcast/edit.php              |  4 +-
 themes/cp_admin/user/edit.php                 |  3 +-
 36 files changed, 179 insertions(+), 57 deletions(-)

diff --git a/app/Views/Components/DropdownMenu.php b/app/Views/Components/DropdownMenu.php
index 23609c27f6..7803b9fc63 100644
--- a/app/Views/Components/DropdownMenu.php
+++ b/app/Views/Components/DropdownMenu.php
@@ -21,7 +21,7 @@ class DropdownMenu extends Component
 
     public function setItems(string $value): void
     {
-        $this->items = json_decode(html_entity_decode($value), true);
+        $this->items = json_decode(htmlspecialchars_decode($value), true);
     }
 
     public function render(): string
@@ -39,7 +39,7 @@ class DropdownMenu extends Component
                     ]);
                     break;
                 case 'html':
-                    $menuItems .= html_entity_decode($item['content']);
+                    $menuItems .= htmlspecialchars_decode($item['content']);
                     break;
                 case 'separator':
                     $menuItems .= '<hr class="my-2 border border-subtle">';
diff --git a/app/Views/Components/Forms/MarkdownEditor.php b/app/Views/Components/Forms/MarkdownEditor.php
index 24c3abcc0a..9fab63a06a 100644
--- a/app/Views/Components/Forms/MarkdownEditor.php
+++ b/app/Views/Components/Forms/MarkdownEditor.php
@@ -13,7 +13,10 @@ class MarkdownEditor extends FormComponent
         $this->attributes['class'] = 'bg-elevated border-none focus:border-none focus:outline-none focus:ring-0 w-full h-full';
         $this->attributes['rows'] = 6;
 
-        $textarea = form_textarea($this->attributes, old($this->name, html_entity_decode($this->value), false));
+        // dd(htmlspecialchars_decode($this->value));
+        $value = htmlspecialchars_decode($this->value);
+
+        $textarea = form_textarea($this->attributes, old($this->name, $value, false));
         $icons = [
             'heading' => icon('heading'),
             'bold' => icon('bold'),
diff --git a/app/Views/Components/Forms/MultiSelect.php b/app/Views/Components/Forms/MultiSelect.php
index f04d6f536c..67d0efd2fa 100644
--- a/app/Views/Components/Forms/MultiSelect.php
+++ b/app/Views/Components/Forms/MultiSelect.php
@@ -18,12 +18,12 @@ class MultiSelect extends FormComponent
 
     public function setOptions(string $value): void
     {
-        $this->options = json_decode(html_entity_decode($value), true);
+        $this->options = json_decode(htmlspecialchars_decode($value), true);
     }
 
     public function setSelected(string $selected): void
     {
-        $this->selected = json_decode($selected);
+        $this->selected = json_decode(htmlspecialchars_decode($selected), true);
     }
 
     public function render(): string
diff --git a/app/Views/Components/Forms/Select.php b/app/Views/Components/Forms/Select.php
index 02d6d31bfa..1dce33930f 100644
--- a/app/Views/Components/Forms/Select.php
+++ b/app/Views/Components/Forms/Select.php
@@ -15,7 +15,7 @@ class Select extends FormComponent
 
     public function setOptions(string $value): void
     {
-        $this->options = json_decode(html_entity_decode($value), true);
+        $this->options = json_decode(htmlspecialchars_decode($value), true);
     }
 
     public function render(): string
diff --git a/app/Views/Components/Forms/Textarea.php b/app/Views/Components/Forms/Textarea.php
index 9e429258ed..705ec0f35c 100644
--- a/app/Views/Components/Forms/Textarea.php
+++ b/app/Views/Components/Forms/Textarea.php
@@ -9,7 +9,7 @@ class Textarea extends FormComponent
     public function setValue(?string $value): void
     {
         if ($value) {
-            $this->value = html_entity_decode($value);
+            $this->value = htmlspecialchars_decode($value);
         }
     }
 
diff --git a/app/Views/errors/html/error_404.php b/app/Views/errors/html/error_404.php
index 0feb6b7d53..13cd335791 100644
--- a/app/Views/errors/html/error_404.php
+++ b/app/Views/errors/html/error_404.php
@@ -21,7 +21,7 @@
             Sorry! Cannot seem to find the page you were looking for.
         <?php endif; ?>
     </p>
-    <button class="inline-flex items-center justify-center px-3 py-1 text-sm font-semibold rounded-full shadow-xs text-accent-contrast focus:ring-accent md:px-4 md:py-2 md:text-base bg-accent-base hover:bg-accent-hover"><?= lang('Common.go_back') ?></button>
+    <a href="<?= previous_url() ?>" class="inline-flex items-center justify-center px-3 py-1 text-sm font-semibold rounded-full shadow-xs text-accent-contrast focus:ring-accent md:px-4 md:py-2 md:text-base bg-accent-base hover:bg-accent-hover"><?= lang('Common.go_back') ?></a>
 </body>
 
 </html>
diff --git a/modules/Admin/Controllers/ContributorController.php b/modules/Admin/Controllers/ContributorController.php
index ad79f24b50..07bfd114ac 100644
--- a/modules/Admin/Controllers/ContributorController.php
+++ b/modules/Admin/Controllers/ContributorController.php
@@ -166,7 +166,10 @@ class ContributorController extends BaseController
             (int) $this->request->getPost('role'),
         );
 
-        return redirect()->route('contributor-list', [$this->podcast->id]);
+        return redirect()->route('contributor-edit', [$this->podcast->id, $this->user->id])->with(
+            'message',
+            lang('Contributor.messages.editSuccess')
+        );
     }
 
     public function remove(): RedirectResponse
@@ -174,7 +177,7 @@ class ContributorController extends BaseController
         if ($this->podcast->created_by === $this->user->id) {
             return redirect()
                 ->back()
-                ->with('errors', [lang('Contributor.messages.removeOwnerContributorError')]);
+                ->with('errors', [lang('Contributor.messages.removeOwnerError')]);
         }
 
         $podcastModel = new PodcastModel();
@@ -187,10 +190,10 @@ class ContributorController extends BaseController
         }
 
         return redirect()
-            ->back()
+            ->route('contributor-list', [$this->podcast->id])
             ->with(
                 'message',
-                lang('Contributor.messages.removeContributorSuccess', [
+                lang('Contributor.messages.removeSuccess', [
                     'username' => $this->user->username,
                     'podcastTitle' => $this->podcast->title,
                 ]),
diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php
index a2a2c57e2b..2d297ff98d 100644
--- a/modules/Admin/Controllers/EpisodeController.php
+++ b/modules/Admin/Controllers/EpisodeController.php
@@ -205,7 +205,10 @@ class EpisodeController extends BaseController
 
         $db->transComplete();
 
-        return redirect()->route('episode-view', [$this->podcast->id, $newEpisodeId]);
+        return redirect()->route('episode-view', [$this->podcast->id, $newEpisodeId])->with(
+            'message',
+            lang('Episode.messages.createSuccess')
+        );
     }
 
     public function edit(): string
@@ -334,11 +337,18 @@ class EpisodeController extends BaseController
 
         $db->transComplete();
 
-        return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
+        return redirect()->route('episode-edit', [$this->podcast->id, $this->episode->id])->with(
+            'message',
+            lang('Episode.messages.editSuccess')
+        );
     }
 
     public function transcriptDelete(): RedirectResponse
     {
+        if ($this->episode->transcript === null) {
+            return redirect()->back();
+        }
+
         $mediaModel = new MediaModel();
         if (! $mediaModel->deleteMedia($this->episode->transcript)) {
             return redirect()
@@ -352,6 +362,10 @@ class EpisodeController extends BaseController
 
     public function chaptersDelete(): RedirectResponse
     {
+        if ($this->episode->chapters === null) {
+            return redirect()->back();
+        }
+
         $mediaModel = new MediaModel();
         if (! $mediaModel->deleteMedia($this->episode->chapters)) {
             return redirect()
@@ -699,16 +713,18 @@ class EpisodeController extends BaseController
             (new PostModel())->removePost($post);
         }
 
-        // set episode published_at to null to unpublish before deletion
-        $this->episode->published_at = null;
-
         $episodeModel = new EpisodeModel();
-        if (! $episodeModel->update($this->episode->id, $this->episode)) {
-            $db->transRollback();
-            return redirect()
-                ->back()
-                ->withInput()
-                ->with('errors', $episodeModel->errors());
+        if ($this->episode->published_at !== null) {
+            // if episode is published, set episode published_at to null to unpublish before deletion
+            $this->episode->published_at = null;
+
+            if (! $episodeModel->update($this->episode->id, $this->episode)) {
+                $db->transRollback();
+                return redirect()
+                    ->back()
+                    ->withInput()
+                    ->with('errors', $episodeModel->errors());
+            }
         }
 
         $episodeModel->delete($this->episode->id);
diff --git a/modules/Admin/Controllers/PageController.php b/modules/Admin/Controllers/PageController.php
index 7458719ee1..1a13abcc46 100644
--- a/modules/Admin/Controllers/PageController.php
+++ b/modules/Admin/Controllers/PageController.php
@@ -106,7 +106,7 @@ class PageController extends BaseController
                 ->with('errors', $pageModel->errors());
         }
 
-        return redirect()->route('page-list');
+        return redirect()->route('page-edit', [$this->page->id])->with('message', lang('Page.messages.editSuccess'));
     }
 
     public function delete(): RedirectResponse
diff --git a/modules/Admin/Controllers/PersonController.php b/modules/Admin/Controllers/PersonController.php
index da410de48d..a62914a4a4 100644
--- a/modules/Admin/Controllers/PersonController.php
+++ b/modules/Admin/Controllers/PersonController.php
@@ -99,7 +99,8 @@ class PersonController extends BaseController
 
         $db->transComplete();
 
-        return redirect()->route('person-list');
+        return redirect()->route('person-list')
+            ->with('message', lang('Person.messages.createSuccess'));
     }
 
     public function edit(): string
@@ -145,13 +146,17 @@ class PersonController extends BaseController
                 ->with('errors', $personModel->errors());
         }
 
-        return redirect()->route('person-view', [$this->person->id]);
+        return redirect()->route('person-edit', [$this->person->id])->with(
+            'message',
+            lang('Person.messages.editSuccess')
+        );
     }
 
     public function delete(): RedirectResponse
     {
         (new PersonModel())->delete($this->person->id);
 
-        return redirect()->route('person-list');
+        return redirect()->route('person-list')
+            ->with('message', lang('Person.messages.deleteSuccess'));
     }
 }
diff --git a/modules/Admin/Controllers/PodcastController.php b/modules/Admin/Controllers/PodcastController.php
index 22e9a68ea9..5f39fe124f 100644
--- a/modules/Admin/Controllers/PodcastController.php
+++ b/modules/Admin/Controllers/PodcastController.php
@@ -255,7 +255,10 @@ class PodcastController extends BaseController
 
         $db->transComplete();
 
-        return redirect()->route('podcast-view', [$newPodcastId]);
+        return redirect()->route('podcast-view', [$newPodcastId])->with(
+            'message',
+            lang('Podcast.messages.createSuccess')
+        );
     }
 
     public function edit(): string
@@ -354,7 +357,10 @@ class PodcastController extends BaseController
 
         $db->transComplete();
 
-        return redirect()->back();
+        return redirect()->route('podcast-edit', [$this->podcast->id])->with(
+            'message',
+            lang('Podcast.messages.editSuccess')
+        );
     }
 
     public function deleteBanner(): RedirectResponse
diff --git a/modules/Admin/Controllers/SoundbiteController.php b/modules/Admin/Controllers/SoundbiteController.php
index c13b2453c6..d9abe12726 100644
--- a/modules/Admin/Controllers/SoundbiteController.php
+++ b/modules/Admin/Controllers/SoundbiteController.php
@@ -134,7 +134,10 @@ class SoundbiteController extends BaseController
                 ->with('errors', $clipModel->errors());
         }
 
-        return redirect()->route('soundbites-list', [$this->podcast->id, $this->episode->id]);
+        return redirect()->route('soundbites-list', [$this->podcast->id, $this->episode->id])->with(
+            'message',
+            lang('Soundbite.messages.createSuccess')
+        );
     }
 
     public function delete(string $soundbiteId): RedirectResponse
@@ -158,6 +161,9 @@ class SoundbiteController extends BaseController
             }
         }
 
-        return redirect()->route('soundbites-list', [$this->podcast->id, $this->episode->id]);
+        return redirect()->route('soundbites-list', [$this->podcast->id, $this->episode->id])->with(
+            'message',
+            lang('Soundbite.messages.deleteSuccess')
+        );
     }
 }
diff --git a/modules/Admin/Language/en/Contributor.php b/modules/Admin/Language/en/Contributor.php
index 087e4f6d5f..7d9c99b4b8 100644
--- a/modules/Admin/Language/en/Contributor.php
+++ b/modules/Admin/Language/en/Contributor.php
@@ -32,8 +32,8 @@ return [
         'podcast_admin' => 'Podcast admin',
     ],
     'messages' => [
-        'removeOwnerContributorError' => "You can't remove the podcast owner!",
-        'removeContributorSuccess' =>
+        'removeOwnerError' => "You can't remove the podcast owner!",
+        'removeSuccess' =>
             'You have successfully removed {username} from {podcastTitle}',
         'alreadyAddedError' =>
             "The contributor you're trying to add has already been added!",
diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php
index 7b3d32189d..cce9992ff7 100644
--- a/modules/Admin/Language/en/Episode.php
+++ b/modules/Admin/Language/en/Episode.php
@@ -43,6 +43,10 @@ return [
         'comments' => 'Comments',
         'actions' => 'Actions',
     ],
+    'messages' => [
+        'createSuccess' => 'Episode has been successfully created!',
+        'editSuccess' => 'Episode has been successfully updated!',
+    ],
     'form' => [
         'warning' =>
             'In case of fatal error, try increasing the `memory_limit`, `upload_max_filesize` and `post_max_size` values in your php configuration file then restart your web server.<br />These values must be higher than the audio file you wish to upload.',
diff --git a/modules/Admin/Language/en/Fediverse.php b/modules/Admin/Language/en/Fediverse.php
index a445724791..6ef8d71dd4 100644
--- a/modules/Admin/Language/en/Fediverse.php
+++ b/modules/Admin/Language/en/Fediverse.php
@@ -9,6 +9,12 @@ declare(strict_types=1);
  */
 
 return [
+    'messages' => [
+        'blockActorSuccess' => '{actor} has been blocked!',
+        'unblockActorSuccess' => 'Actor has been unblocked!',
+        'blockDomainSuccess' => '{domain} has been blocked!',
+        'unblockDomainSuccess' => '{domain} has been unblocked!',
+    ],
     'blocked_actors' => 'Blocked accounts',
     'blocked_domains' => 'Blocked domains',
     'block_lists_form' => [
diff --git a/modules/Admin/Language/en/Page.php b/modules/Admin/Language/en/Page.php
index e4a78f89d5..c6b4b8fe87 100644
--- a/modules/Admin/Language/en/Page.php
+++ b/modules/Admin/Language/en/Page.php
@@ -25,5 +25,6 @@ return [
     ],
     'messages' => [
         'createSuccess' => 'The page “{pageTitle}” was created successfully!',
+        'editSuccess' => 'The page was successfully updated!',
     ],
 ];
diff --git a/modules/Admin/Language/en/Person.php b/modules/Admin/Language/en/Person.php
index b9e122d2e4..114597ccbb 100644
--- a/modules/Admin/Language/en/Person.php
+++ b/modules/Admin/Language/en/Person.php
@@ -16,6 +16,11 @@ return [
     'view' => 'View person',
     'edit' => 'Edit person',
     'delete' => 'Delete person',
+    'messages' => [
+        'createSuccess' => 'Person has been successfully created!',
+        'editSuccess' => 'Person has been successfully updated!',
+        'deleteSuccess' => 'Person has been removed!',
+    ],
     'form' => [
         'avatar' => 'Avatar',
         'avatar_size_hint' =>
diff --git a/modules/Admin/Language/en/Podcast.php b/modules/Admin/Language/en/Podcast.php
index c03e46f8a7..54a5e210f0 100644
--- a/modules/Admin/Language/en/Podcast.php
+++ b/modules/Admin/Language/en/Podcast.php
@@ -22,6 +22,11 @@ return [
     'go_to_page' => 'Go to page',
     'latest_episodes' => 'Latest episodes',
     'see_all_episodes' => 'See all episodes',
+    'messages' => [
+        'createSuccess' => 'Podcast has been successfully created!',
+        'editSuccess' => 'Podcast has been successfully updated!',
+        'importSuccess' => 'Podcast has been successfully imported!',
+    ],
     'form' => [
         'identity_section_title' => 'Podcast identity',
         'identity_section_subtitle' => 'These fields allow you to get noticed.',
diff --git a/modules/Admin/Language/en/Soundbite.php b/modules/Admin/Language/en/Soundbite.php
index 8faed932fe..059cb1bc72 100644
--- a/modules/Admin/Language/en/Soundbite.php
+++ b/modules/Admin/Language/en/Soundbite.php
@@ -13,6 +13,10 @@ return [
         'title' => 'Soundbites',
         'soundbite' => 'Soundbite',
     ],
+    'messages' => [
+        'createSuccess' => 'Soundbite has been successfully created!',
+        'deleteSuccess' => 'Soundbite has been successfully removed!',
+    ],
     'form' => [
         'title' => 'New soundbite',
         'soundbite_title' => 'Soundbite title',
diff --git a/modules/Admin/Language/en/VideoClip.php b/modules/Admin/Language/en/VideoClip.php
index ef936401a4..1f7644314b 100644
--- a/modules/Admin/Language/en/VideoClip.php
+++ b/modules/Admin/Language/en/VideoClip.php
@@ -34,6 +34,10 @@ return [
     'retry' => 'Retry clip generation',
     'delete' => 'Delete clip',
     'logs' => 'Job logs',
+    'messages' => [
+        'createSuccess' => 'Video clip has been successfully created!',
+        'deleteSuccess' => 'Video clip has been successfully removed!',
+    ],
     'form' => [
         'title' => 'New video clip',
         'params_section_title' => 'Video clip parameters',
diff --git a/modules/Admin/Language/fr/Contributor.php b/modules/Admin/Language/fr/Contributor.php
index 7e62309e4c..1e73e9a11c 100644
--- a/modules/Admin/Language/fr/Contributor.php
+++ b/modules/Admin/Language/fr/Contributor.php
@@ -32,9 +32,9 @@ return [
         'podcast_admin' => 'Administrateur de Podcasts',
     ],
     'messages' => [
-        'removeOwnerContributorError' =>
+        'removeOwnerError' =>
             'Vous ne pouvez pas retirer le propriétaire du podcast !',
-        'removeContributorSuccess' =>
+        'removeSuccess' =>
             'Vous avez retiré {username} de {podcastTitle}',
         'alreadyAddedError' =>
             'Le contributeur que vous essayez d’ajouter est déjà présent.',
diff --git a/modules/Admin/Language/fr/Episode.php b/modules/Admin/Language/fr/Episode.php
index 38cb216d1b..1ff5a800af 100644
--- a/modules/Admin/Language/fr/Episode.php
+++ b/modules/Admin/Language/fr/Episode.php
@@ -44,6 +44,10 @@ return [
         'comments' => 'Commentaires',
         'actions' => 'Actions',
     ],
+    'messages' => [
+        'createSuccess' => 'L’épisode a été créé avec succès !',
+        'editSuccess' => 'L’épisode a bien été mis à jour !',
+    ],
     'form' => [
         'warning' =>
             'En cas d’erreur fatale, essayez d’augmenter les valeurs de `memory_limit`, `upload_max_filesize` et `post_max_size` dans votre fichier de configuration php puis redémarrez votre serveur web.<br />Les valeurs doivent être plus grandes que le fichier audio que vous souhaitez téléverser.',
diff --git a/modules/Admin/Language/fr/Fediverse.php b/modules/Admin/Language/fr/Fediverse.php
index fa6b48972c..1d647d3dc7 100644
--- a/modules/Admin/Language/fr/Fediverse.php
+++ b/modules/Admin/Language/fr/Fediverse.php
@@ -9,6 +9,12 @@ declare(strict_types=1);
  */
 
 return [
+    'messages' => [
+        'blockActorSuccess' => '{actor} a été bloqué !',
+        'unblockActorSuccess' => 'L’utilisateur a été débloqué !',
+        'blockDomainSuccess' => '{domain} a été bloqué !',
+        'unblockDomainSuccess' => '{domain} a été débloqué !',
+    ],
     'block_lists' => 'Listes de blocage',
     'block_lists_form' => [
         'blocked_users' => 'Utilisateurs bloqués',
diff --git a/modules/Admin/Language/fr/Page.php b/modules/Admin/Language/fr/Page.php
index 2a0b21a552..6993274cb1 100644
--- a/modules/Admin/Language/fr/Page.php
+++ b/modules/Admin/Language/fr/Page.php
@@ -24,6 +24,7 @@ return [
         'submit_edit' => 'Enregistrer',
     ],
     'messages' => [
-        'createSuccess' => 'La page {pageTitle} a été créée avec succès !',
+        'createSuccess' => 'La page “{pageTitle}” a été créée avec succès !',
+        'editSuccess' => 'La page a bien été mise à jour !',
     ],
 ];
diff --git a/modules/Admin/Language/fr/Person.php b/modules/Admin/Language/fr/Person.php
index aa77d8777a..241e5dfee0 100644
--- a/modules/Admin/Language/fr/Person.php
+++ b/modules/Admin/Language/fr/Person.php
@@ -16,6 +16,11 @@ return [
     'view' => 'Voir l’intervenant',
     'edit' => 'Modifier l’intervenant',
     'delete' => 'Supprimer l’intervenant',
+    'messages' => [
+        'createSuccess' => 'L’intervenant a été créé avec succès !',
+        'editSuccess' => 'L’intervenant a bien été mis à jour !',
+        'deleteSuccess' => 'L’intervenant a bien été retiré !',
+    ],
     'form' => [
         'avatar' => 'Avatar',
         'avatar_size_hint' =>
diff --git a/modules/Admin/Language/fr/Podcast.php b/modules/Admin/Language/fr/Podcast.php
index 8129f592c3..211e9459e1 100644
--- a/modules/Admin/Language/fr/Podcast.php
+++ b/modules/Admin/Language/fr/Podcast.php
@@ -22,6 +22,11 @@ return [
     'go_to_page' => 'Aller à la page',
     'latest_episodes' => 'Derniers épisodes',
     'see_all_episodes' => 'Voir tous les épisodes',
+    'messages' => [
+        'createSuccess' => 'Le podcast a été créé avec succès !',
+        'editSuccess' => 'Le podcast a bien été mis à jour !',
+        'importSuccess' => 'Le podcast a été importé avec succès !',
+    ],
     'form' => [
         'identity_section_title' => 'Informations sur le Podcast',
         'identity_section_subtitle' =>
diff --git a/modules/Admin/Language/fr/Soundbite.php b/modules/Admin/Language/fr/Soundbite.php
index 0cb3dddb95..0445a06180 100644
--- a/modules/Admin/Language/fr/Soundbite.php
+++ b/modules/Admin/Language/fr/Soundbite.php
@@ -13,6 +13,10 @@ return [
         'title' => 'Extraits sonores',
         'soundbite' => 'Extrait sonore',
     ],
+    'messages' => [
+        'createSuccess' => 'L’extrait sonore a été créé avec succès !',
+        'deleteSuccess' => 'L’extrait sonore a bien été supprimé !',
+    ],
     'form' => [
         'title' => 'Nouvel extrait sonore',
         'soundbite_title' => 'Titre de l’extrait',
diff --git a/modules/Admin/Language/fr/VideoClip.php b/modules/Admin/Language/fr/VideoClip.php
index c467714f83..e2e1b79dc0 100644
--- a/modules/Admin/Language/fr/VideoClip.php
+++ b/modules/Admin/Language/fr/VideoClip.php
@@ -34,6 +34,10 @@ return [
     'retry' => 'Relancer la génération de l’extrait',
     'delete' => 'Supprimer l’extrait',
     'logs' => 'Historique d’exécution',
+    'messages' => [
+        'createSuccess' => 'L’extrait vidéo a été créé avec succès !',
+        'deleteSuccess' => 'L’extrait vidéo a bien été supprimé !',
+    ],
     'form' => [
         'title' => 'Nouvel extrait vidéo',
         'params_section_title' => 'Paramètres de l’extrait vidéo',
diff --git a/modules/Fediverse/Controllers/BlockController.php b/modules/Fediverse/Controllers/BlockController.php
index 162cc56344..9537edf212 100644
--- a/modules/Fediverse/Controllers/BlockController.php
+++ b/modules/Fediverse/Controllers/BlockController.php
@@ -42,14 +42,17 @@ class BlockController extends Controller
                 return redirect()
                     ->back()
                     ->withInput()
-                    ->with('error', 'Actor not found.');
+                    ->with('error', lang('Fediverse.messages.actorNotFound'));
             }
 
             model('ActorModel')
                 ->blockActor($actor->id);
         }
 
-        return redirect()->back();
+        return redirect()->back()
+            ->with('message', lang('Fediverse.messages.blockActorSuccess', [
+                'actor' => $handle,
+            ]));
     }
 
     public function attemptUnblockActor(): RedirectResponse
@@ -68,7 +71,8 @@ class BlockController extends Controller
         model('ActorModel')
             ->unblockActor((int) $this->request->getPost('actor_id'));
 
-        return redirect()->back();
+        return redirect()->back()
+            ->with('message', lang('Fediverse.messages.unblockActorSuccess'));
     }
 
     public function attemptBlockDomain(): RedirectResponse
@@ -84,10 +88,14 @@ class BlockController extends Controller
                 ->with('errors', $this->validator->getErrors());
         }
 
+        $domain = $this->request->getPost('domain');
         model('BlockedDomainModel')
-            ->blockDomain($this->request->getPost('domain'));
+            ->blockDomain($domain);
 
-        return redirect()->back();
+        return redirect()->back()
+            ->with('message', lang('Fediverse.messages.blockDomainSuccess', [
+                'domain' => $domain,
+            ]));
     }
 
     public function attemptUnblockDomain(): RedirectResponse
@@ -103,9 +111,13 @@ class BlockController extends Controller
                 ->with('errors', $this->validator->getErrors());
         }
 
+        $domain = $this->request->getPost('domain');
         model('BlockedDomainModel')
-            ->unblockDomain($this->request->getPost('domain'));
+            ->unblockDomain($domain);
 
-        return redirect()->back();
+        return redirect()->back()
+            ->with('message', lang('Fediverse.messages.unblockDomainSuccess', [
+                'domain' => $domain,
+            ]));
     }
 }
diff --git a/modules/Fediverse/Controllers/PostController.php b/modules/Fediverse/Controllers/PostController.php
index 4240e9f7bf..c375111c8e 100644
--- a/modules/Fediverse/Controllers/PostController.php
+++ b/modules/Fediverse/Controllers/PostController.php
@@ -124,12 +124,13 @@ class PostController extends Controller
             'published_at' => Time::now(),
         ]);
 
-        if (! model('PostModel')->addPost($newPost)) {
+        $postModel = model('PostModel');
+        if (! $postModel->addPost($newPost)) {
             return redirect()
                 ->back()
                 ->withInput()
                 // TODO: translate
-                ->with('error', "Couldn't create Post");
+                ->with('error', $postModel->errors());
         }
 
         // Post without preview card has been successfully created
diff --git a/themes/cp_admin/episode/_card.php b/themes/cp_admin/episode/_card.php
index 20a2e9f9f7..43199d5553 100644
--- a/themes/cp_admin/episode/_card.php
+++ b/themes/cp_admin/episode/_card.php
@@ -34,8 +34,13 @@
         ],
         [
             'type' => 'link',
-            'title' => lang('Episode.soundbites'),
-            'uri' => route_to('soundbites-edit', $episode->podcast->id, $episode->id),
+            'title' => lang('VideoClip.list.title'),
+            'uri' => route_to('video-clips-list', $episode->podcast->id, $episode->id),
+        ],
+        [
+            'type' => 'link',
+            'title' => lang('Soundbite.list.title'),
+            'uri' => route_to('soundbites-list', $episode->podcast->id, $episode->id),
         ],
         [
             'type' => 'separator',
diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php
index 3ca83c14d4..81ab34e8c3 100644
--- a/themes/cp_admin/episode/list.php
+++ b/themes/cp_admin/episode/list.php
@@ -99,8 +99,13 @@
                             ],
                             [
                                 'type' => 'link',
-                                'title' => lang('Episode.soundbites'),
-                                'uri' => route_to('soundbites-edit', $podcast->id, $episode->id),
+                                'title' => lang('VideoClip.list.title'),
+                                'uri' => route_to('video-clips-list', $episode->podcast->id, $episode->id),
+                            ],
+                            [
+                                'type' => 'link',
+                                'title' => lang('Soundbite.list.title'),
+                                'uri' => route_to('soundbites-list', $podcast->id, $episode->id),
                             ],
                             [
                                 'type' => 'separator',
diff --git a/themes/cp_admin/episode/persons.php b/themes/cp_admin/episode/persons.php
index 534d0af9dc..57f57ce3e5 100644
--- a/themes/cp_admin/episode/persons.php
+++ b/themes/cp_admin/episode/persons.php
@@ -28,8 +28,8 @@
         name="persons[]"
         label="<?= lang('Person.episode_form.persons') ?>"
         hint="<?= lang('Person.episode_form.persons_hint') ?>"
-        options="<?= htmlspecialchars(json_encode($personOptions)) ?>"
-        selected="<?= htmlspecialchars(json_encode(old('persons', []))) ?>"
+        options="<?= esc(json_encode($personOptions)) ?>"
+        selected="<?= esc(json_encode(old('persons', []))) ?>"
         required="true"
     />
 
@@ -39,8 +39,8 @@
         name="roles[]"
         label="<?= lang('Person.episode_form.roles') ?>"
         hint="<?= lang('Person.episode_form.roles_hint') ?>"
-        options="<?= htmlspecialchars(json_encode($taxonomyOptions)) ?>"
-        selected="<?= htmlspecialchars(json_encode(old('roles', []))) ?>"
+        options="<?= esc(json_encode($taxonomyOptions)) ?>"
+        selected="<?= esc(json_encode(old('roles', []))) ?>"
         required="true"
     />
 
diff --git a/themes/cp_admin/person/edit.php b/themes/cp_admin/person/edit.php
index 803bd98439..2c23a2eaf5 100644
--- a/themes/cp_admin/person/edit.php
+++ b/themes/cp_admin/person/edit.php
@@ -34,7 +34,8 @@
     value="<?= $person->unique_name ?>"
     label="<?= lang('Person.form.unique_name') ?>"
     hint="<?= lang('Person.form.unique_name_hint') ?>"
-    required="true" />
+    required="true"
+    data-slugify="slug" />
 
 <Forms.Field
     name="information_url"
diff --git a/themes/cp_admin/podcast/edit.php b/themes/cp_admin/podcast/edit.php
index 682febfa45..6b7aea1361 100644
--- a/themes/cp_admin/podcast/edit.php
+++ b/themes/cp_admin/podcast/edit.php
@@ -107,13 +107,13 @@
         selected="<?= $podcast->category_id ?>"
         options="<?= esc(json_encode($categoryOptions)) ?>"
         required="true" />
-
+    
     <Forms.Field
         as="MultiSelect"
         name="other_categories[]"
         label="<?= lang('Podcast.form.other_categories') ?>"
-        selected="<?= json_encode($podcast->other_categories_ids) ?>"
         data-max-item-count="2"
+        selected="<?= esc(json_encode($podcast->other_categories_ids)) ?>"
         options="<?= esc(json_encode($categoryOptions)) ?>" />
 
     <fieldset class="mb-4">
diff --git a/themes/cp_admin/user/edit.php b/themes/cp_admin/user/edit.php
index 1331e2378d..3fdd98bc61 100644
--- a/themes/cp_admin/user/edit.php
+++ b/themes/cp_admin/user/edit.php
@@ -19,6 +19,7 @@
 <?= csrf_field() ?>
 
 <Forms.Field
+    as="MultiSelect"
     id="roles"
     name="roles[]"
     label="<?= lang('User.form.roles') ?>"
@@ -26,7 +27,7 @@
     options="<?= esc(json_encode($roleOptions)) ?>"
     selected="<?= esc(json_encode($user->roles)) ?>" />
 
-<Button variant="primary" type="submit" class="self-end"><?= lang('User.form.submit_edit') ?></Button>
+<Button variant="primary" type="submit" class="self-end mt-4"><?= lang('User.form.submit_edit') ?></Button>
 
 </form>
 
-- 
GitLab