From f75bd76458eeb01a2d37912695e33f77d03b7a69 Mon Sep 17 00:00:00 2001
From: Yassine Doghri <yassine@doghri.fr>
Date: Fri, 1 Oct 2021 10:59:44 +0000
Subject: [PATCH] fix: unpublish episode before deleting it + add validation
 step before deletion

fixes #112, closes #55
---
 app/Views/Components/Forms/Checkbox.php       |  2 +-
 modules/Admin/Config/Routes.php               |  8 +++
 .../Admin/Controllers/EpisodeController.php   | 58 ++++++++++++++++++-
 modules/Admin/Language/en/Breadcrumb.php      |  1 +
 modules/Admin/Language/en/Episode.php         |  8 ++-
 modules/Admin/Language/fr/Breadcrumb.php      |  1 +
 modules/Admin/Language/fr/Episode.php         | 12 ++++
 themes/cp_admin/episode/delete.php            | 27 +++++++++
 themes/cp_admin/episode/unpublish.php         |  2 +-
 9 files changed, 114 insertions(+), 5 deletions(-)
 create mode 100644 themes/cp_admin/episode/delete.php

diff --git a/app/Views/Components/Forms/Checkbox.php b/app/Views/Components/Forms/Checkbox.php
index b93160cae9..02f3af06a6 100644
--- a/app/Views/Components/Forms/Checkbox.php
+++ b/app/Views/Components/Forms/Checkbox.php
@@ -30,7 +30,7 @@ class Checkbox extends FormComponent
         $hint = $this->hint === null ? '' : hint_tooltip($this->hint, 'ml-1');
 
         return <<<HTML
-            <label class="leading-8">
+            <label class="leading-8 {$this->class}">
                 {$checkboxInput}
                 <span class="ml-2">{$this->slot}{$hint}</label>
             </label>
diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php
index bb5b6a4391..626803b719 100644
--- a/modules/Admin/Config/Routes.php
+++ b/modules/Admin/Config/Routes.php
@@ -272,6 +272,14 @@ $routes->group(
                                     'permission:podcast_episodes-delete',
                             ],
                         );
+                        $routes->post(
+                            'delete',
+                            'EpisodeController::attemptDelete/$1/$2',
+                            [
+                                'filter' =>
+                                    'permission:podcast_episodes-delete',
+                            ],
+                        );
                         $routes->get(
                             'transcript-delete',
                             'EpisodeController::transcriptDelete/$1/$2',
diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php
index 026a625528..078fad1cd2 100644
--- a/modules/Admin/Controllers/EpisodeController.php
+++ b/modules/Admin/Controllers/EpisodeController.php
@@ -684,9 +684,63 @@ class EpisodeController extends BaseController
         return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
     }
 
-    public function delete(): RedirectResponse
+    public function delete(): string
     {
-        (new EpisodeModel())->delete($this->episode->id);
+        helper(['form']);
+
+        $data = [
+            'podcast' => $this->podcast,
+            'episode' => $this->episode,
+        ];
+
+        replace_breadcrumb_params([
+            0 => $this->podcast->title,
+            1 => $this->episode->title,
+        ]);
+        return view('episode/delete', $data);
+    }
+
+    public function attemptDelete(): RedirectResponse
+    {
+        $rules = [
+            'understand' => 'required',
+        ];
+
+        if (! $this->validate($rules)) {
+            return redirect()
+                ->back()
+                ->withInput()
+                ->with('errors', $this->validator->getErrors());
+        }
+
+        $db = db_connect();
+
+        $db->transStart();
+
+        $allPostsLinkedToEpisode = (new PostModel())
+            ->where([
+                'episode_id' => $this->episode->id,
+            ])
+            ->findAll();
+        foreach ($allPostsLinkedToEpisode as $post) {
+            (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());
+        }
+
+        $episodeModel->delete($this->episode->id);
+
+        $db->transComplete();
 
         return redirect()->route('episode-list', [$this->podcast->id]);
     }
diff --git a/modules/Admin/Language/en/Breadcrumb.php b/modules/Admin/Language/en/Breadcrumb.php
index a0fa5e44e7..a8df8411e5 100644
--- a/modules/Admin/Language/en/Breadcrumb.php
+++ b/modules/Admin/Language/en/Breadcrumb.php
@@ -23,6 +23,7 @@ return [
     'publish' => 'publish',
     'publish-edit' => 'edit publication',
     'unpublish' => 'unpublish',
+    'delete' => 'delete',
     'fediverse' => 'fediverse',
     'block-lists' => 'block lists',
     'users' => 'users',
diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php
index 181e446cc9..1761148314 100644
--- a/modules/Admin/Language/en/Episode.php
+++ b/modules/Admin/Language/en/Episode.php
@@ -137,10 +137,16 @@ return [
     ],
     'unpublish_form' => [
         'disclaimer' =>
-            "Unpublishing the episode will delete all the notes associated with the episode and remove it from the podcast's RSS feed.",
+            "Unpublishing the episode will delete all the posts associated with it and remove it from the podcast's RSS feed.",
         'understand' => 'I understand, I want to unpublish the episode',
         'submit' => 'Unpublish',
     ],
+    'delete_form' => [
+        'disclaimer' =>
+            "Deleting the episode will delete all the posts associated with it and remove it from the podcast's RSS feed.",
+        'understand' => 'I understand, I want to delete the episode',
+        'submit' => 'Delete',
+    ],
     'soundbites' => 'Soundbites',
     'soundbites_form' => [
         'title' => 'Edit soundbites',
diff --git a/modules/Admin/Language/fr/Breadcrumb.php b/modules/Admin/Language/fr/Breadcrumb.php
index 08d8d888ac..35598decca 100644
--- a/modules/Admin/Language/fr/Breadcrumb.php
+++ b/modules/Admin/Language/fr/Breadcrumb.php
@@ -23,6 +23,7 @@ return [
     'publish' => 'publier',
     'publish-edit' => 'modifier la publication',
     'unpublish' => 'dépublier',
+    'delete' => 'supprimer',
     'fediverse' => 'fédiverse',
     'block-lists' => 'listes de blocage',
     'users' => 'utilisateurs',
diff --git a/modules/Admin/Language/fr/Episode.php b/modules/Admin/Language/fr/Episode.php
index 1061e34352..bafa62319b 100644
--- a/modules/Admin/Language/fr/Episode.php
+++ b/modules/Admin/Language/fr/Episode.php
@@ -141,6 +141,18 @@ return [
         'message_warning_hint' => 'Ajouter un message augmente l’engagement social, menant à une meilleure visibilité pour votre épisode.',
         'message_warning_submit' => 'Publish quand même',
     ],
+    'unpublish_form' => [
+        'disclaimer' =>
+            'Dépublier l’épisode supprimera toutes les publications qui lui sont associées et le retirera du flux RSS du podcast.',
+        'understand' => 'Je comprends, je veux dépublier l’épisode',
+        'submit' => 'Dépublier',
+    ],
+    'delete_form' => [
+        'disclaimer' =>
+            'Supprimer l’épisode supprimera toutes les publications qui lui sont associées et le retirera du flux RSS du podcast.',
+        'understand' => 'Je comprends, Je veux supprimer l’épisode',
+        'submit' => 'Supprimer',
+    ],
     'soundbites' => 'Extraits sonores',
     'soundbites_form' => [
         'title' => 'Modifier les extraits sonores',
diff --git a/themes/cp_admin/episode/delete.php b/themes/cp_admin/episode/delete.php
new file mode 100644
index 0000000000..35a327eb0e
--- /dev/null
+++ b/themes/cp_admin/episode/delete.php
@@ -0,0 +1,27 @@
+<?= $this->extend('_layout') ?>
+
+<?= $this->section('title') ?>
+<?= lang('Episode.delete') ?>
+<?= $this->endSection() ?>
+
+<?= $this->section('pageTitle') ?>
+<?= lang('Episode.delete') ?>
+<?= $this->endSection() ?>
+
+<?= $this->section('content') ?>
+
+<form action="<?= route_to('episode-delete', $podcast->id, $episode->id) ?>" method="POST" class="flex flex-col max-w-xl mx-auto">
+<?= csrf_field() ?>
+
+<Alert variant="danger" glyph="alert" class="font-semibold"><?= lang('Episode.delete_form.disclaimer') ?></Alert>
+
+<Forms.Checkbox class="mt-2" name="understand" required="true" isChecked="false"><?= lang('Episode.delete_form.understand') ?></Forms.Checkbox>
+
+<div class="self-end mt-4">
+    <Button uri="<?= route_to('episode-view', $podcast->id, $episode->id) ?>"><?= lang('Common.cancel') ?></Button>
+    <Button type="submit" variant="danger"><?= lang('Episode.delete_form.submit') ?></Button>
+</div>
+
+</form>
+
+<?= $this->endSection() ?>
diff --git a/themes/cp_admin/episode/unpublish.php b/themes/cp_admin/episode/unpublish.php
index 8f8f4ce9dd..a6d420822e 100644
--- a/themes/cp_admin/episode/unpublish.php
+++ b/themes/cp_admin/episode/unpublish.php
@@ -15,7 +15,7 @@
 
 <Alert variant="danger" glyph="alert" class="font-semibold"><?= lang('Episode.unpublish_form.disclaimer') ?></Alert>
 
-<Forms.Checkbox name="understand" required="true" isChecked="false"><?= lang('Episode.unpublish_form.understand') ?></Forms.Checkbox>
+<Forms.Checkbox class="mt-2" name="understand" required="true" isChecked="false"><?= lang('Episode.unpublish_form.understand') ?></Forms.Checkbox>
 
 <div class="self-end mt-4">
     <Button uri="<?= route_to('episode-view', $podcast->id, $episode->id) ?>"><?= lang('Common.cancel') ?></Button>
-- 
GitLab