From ad07bb9330dc9493813368e969e1f3a3def44614 Mon Sep 17 00:00:00 2001
From: Benjamin Bellamy <ben@podlibre.org>
Date: Tue, 30 Mar 2021 16:21:00 +0000
Subject: [PATCH] feat(partner): add link and image in episode description

---
 app/Controllers/Admin/Podcast.php             | 10 ++++
 .../2020-05-30-101500_add_podcasts.php        | 14 +++++
 app/Entities/Episode.php                      | 46 ++++++++++++-----
 app/Entities/Podcast.php                      |  3 ++
 app/Helpers/rss_helper.php                    |  5 +-
 app/Language/en/Podcast.php                   |  7 +++
 app/Language/fr/Podcast.php                   |  7 +++
 app/Models/PodcastModel.php                   |  3 ++
 app/Views/admin/podcast/create.php            | 51 +++++++++++++++++++
 app/Views/admin/podcast/edit.php              | 49 ++++++++++++++++++
 app/Views/episode.php                         |  2 +-
 11 files changed, 183 insertions(+), 14 deletions(-)

diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php
index 5e467a3b1b..22a5b79e9c 100644
--- a/app/Controllers/Admin/Podcast.php
+++ b/app/Controllers/Admin/Podcast.php
@@ -164,6 +164,9 @@ class Podcast extends BaseController
             'location' => $this->request->getPost('location_name'),
             'payment_pointer' => $this->request->getPost('payment_pointer'),
             'custom_rss_string' => $this->request->getPost('custom_rss'),
+            'partner_id' => $this->request->getPost('partner_id'),
+            'partner_link_url' => $this->request->getPost('partner_link_url'),
+            'partner_image_url' => $this->request->getPost('partner_image_url'),
             'is_blocked' => $this->request->getPost('block') === 'yes',
             'is_completed' => $this->request->getPost('complete') === 'yes',
             'is_locked' => $this->request->getPost('lock') === 'yes',
@@ -263,6 +266,13 @@ class Podcast extends BaseController
         $this->podcast->custom_rss_string = $this->request->getPost(
             'custom_rss'
         );
+        $this->podcast->partner_id = $this->request->getPost('partner_id');
+        $this->podcast->partner_link_url = $this->request->getPost(
+            'partner_link_url'
+        );
+        $this->podcast->partner_image_url = $this->request->getPost(
+            'partner_image_url'
+        );
         $this->podcast->is_blocked = $this->request->getPost('block') === 'yes';
         $this->podcast->is_completed =
             $this->request->getPost('complete') === 'yes';
diff --git a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
index e926d36f62..2351f51a3e 100644
--- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
+++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
@@ -140,6 +140,20 @@ class AddPodcasts extends Migration
             ],
             'custom_rss' => [
                 'type' => 'JSON',
+            ],
+            'partner_id' => [
+                'type' => 'VARCHAR',
+                'constraint' => 32,
+                'null' => true,
+            ],
+            'partner_link_url' => [
+                'type' => 'VARCHAR',
+                'constraint' => 512,
+                'null' => true,
+            ],
+            'partner_image_url' => [
+                'type' => 'VARCHAR',
+                'constraint' => 512,
                 'null' => true,
             ],
             'created_by' => [
diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php
index f79dfbe083..6d3042ddb5 100644
--- a/app/Entities/Episode.php
+++ b/app/Entities/Episode.php
@@ -483,19 +483,21 @@ class Episode extends Entity
         return $this;
     }
 
-    public function getDescriptionHtml()
+    public function getDescriptionHtml($serviceSlug = null)
     {
-        if (
-            $descriptionFooter = $this->getPodcast()
-                ->episode_description_footer_html
-        ) {
-            return $this->attributes['description_html'] .
-                '<footer>' .
-                $descriptionFooter .
-                '</footer>';
-        }
-
-        return $this->attributes['description_html'];
+        return (empty($this->getPodcast()->partner_id) ||
+        empty($this->getPodcast()->partner_link_url) ||
+        empty($this->getPodcast()->partner_image_url)
+            ? ''
+            : "<div><a href=\"{$this->getPartnerLink(
+                $serviceSlug
+            )}\" rel=\"sponsored noopener noreferrer\" target=\"_blank\"><img src=\"{$this->getPartnerImage(
+                $serviceSlug
+            )}\" alt=\"Partner image\" /></a></div>") .
+            $this->attributes['description_html'] .
+            (empty($this->getPodcast()->episode_description_footer_html)
+                ? ''
+                : "<footer>{$this->getPodcast()->episode_description_footer_html}</footer>");
     }
 
     public function getDescription()
@@ -624,4 +626,24 @@ class Episode extends Entity
             $this->attributes['custom_rss'] = null;
         }
     }
+
+    function getPartnerLink($serviceSlug = null)
+    {
+        return rtrim($this->getPodcast()->partner_link_url, '/') .
+            '?pid=' .
+            $this->getPodcast()->partner_id .
+            '&guid=' .
+            urlencode($this->attributes['guid']) .
+            (empty($serviceSlug) ? '' : '&_from=' . $serviceSlug);
+    }
+
+    function getPartnerImage($serviceSlug = null)
+    {
+        return rtrim($this->getPodcast()->partner_image_url, '/') .
+            '?pid=' .
+            $this->getPodcast()->partner_id .
+            '&guid=' .
+            urlencode($this->attributes['guid']) .
+            (empty($serviceSlug) ? '' : '&_from=' . $serviceSlug);
+    }
 }
diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php
index 813123f5ff..ec6c8e3f06 100644
--- a/app/Entities/Podcast.php
+++ b/app/Entities/Podcast.php
@@ -114,6 +114,9 @@ class Podcast extends Entity
         'location_osmid' => '?string',
         'payment_pointer' => '?string',
         'custom_rss' => '?json-array',
+        'partner_id' => '?string',
+        'partner_link_url' => '?string',
+        'partner_image_url' => '?string',
         'created_by' => 'integer',
         'updated_by' => 'integer',
     ];
diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php
index debb495df0..0da88a191a 100644
--- a/app/Helpers/rss_helper.php
+++ b/app/Helpers/rss_helper.php
@@ -282,7 +282,10 @@ function get_rss_feed($podcast, $serviceSlug = '')
                 $locationElement->addAttribute('osm', $episode->location_osmid);
             }
         }
-        $item->addChildWithCDATA('description', $episode->description_html);
+        $item->addChildWithCDATA(
+            'description',
+            $episode->getDescriptionHtml($serviceSlug)
+        );
         $item->addChild(
             'duration',
             $episode->enclosure_duration,
diff --git a/app/Language/en/Podcast.php b/app/Language/en/Podcast.php
index 5afb455433..67f135b26b 100644
--- a/app/Language/en/Podcast.php
+++ b/app/Language/en/Podcast.php
@@ -77,6 +77,13 @@ return [
             'If you need RSS tags that Castopod does not handle, set them here.',
         'custom_rss' => 'Custom RSS tags for the podcast',
         'custom_rss_hint' => 'This will be injected within the ❬channel❭ tag.',
+        'partnership' => 'Partnership',
+        'partner_id' => 'ID',
+        'partner_link_url' => 'Link URL',
+        'partner_image_url' => 'Image URL',
+        'partner_id_hint' => 'Your own partner ID',
+        'partner_link_url_hint' => 'The generic partner link address',
+        'partner_image_url_hint' => 'The generic partner image address',
         'status_section_title' => 'Status',
         'status_section_subtitle' => 'Dead or alive?',
         'block' => 'Podcast should be hidden from all platforms',
diff --git a/app/Language/fr/Podcast.php b/app/Language/fr/Podcast.php
index 02d3cfb9a8..7daaf77149 100644
--- a/app/Language/fr/Podcast.php
+++ b/app/Language/fr/Podcast.php
@@ -79,6 +79,13 @@ return [
             'Si vous avez besoin d’une balise que nous n’avons pas couverte, définissez-la ici.',
         'custom_rss' => 'Balises RSS personnalisées pour le podcast',
         'custom_rss_hint' => 'Ceci sera injecté dans la balise ❬channel❭.',
+        'partnership' => 'Partenariat',
+        'partner_id' => 'ID',
+        'partner_link_url' => 'URL lien',
+        'partner_image_url' => 'URL image',
+        'partner_id_hint' => 'Votre identifiant personnel partenaire',
+        'partner_link_url_hint' => 'L’adresse générique des liens partenaire',
+        'partner_image_url_hint' => 'L’adresse générique des images partenaire',
         'status_section_title' => 'Statut',
         'status_section_subtitle' => 'Vivant ou mort ?',
         'block' => 'Le podcast doit être masqué sur toutes les plateformes',
diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php
index 6f0e360cd8..ffdcbfe7c6 100644
--- a/app/Models/PodcastModel.php
+++ b/app/Models/PodcastModel.php
@@ -42,6 +42,9 @@ class PodcastModel extends Model
         'location_osmid',
         'payment_pointer',
         'custom_rss',
+        'partner_id',
+        'partner_link_url',
+        'partner_image_url',
         'created_by',
         'updated_by',
     ];
diff --git a/app/Views/admin/podcast/create.php b/app/Views/admin/podcast/create.php
index 33d6d3e8df..b30e6abf8d 100644
--- a/app/Views/admin/podcast/create.php
+++ b/app/Views/admin/podcast/create.php
@@ -279,6 +279,57 @@
     'class' => 'form-input mb-4',
     'value' => old('payment_pointer'),
 ]) ?>
+
+<?= form_label(lang('Podcast.form.partnership')) ?>
+<div class="flex flex-col mb-4 gap-x-2 gap-y-4 md:flex-row">
+    <div class="flex flex-col flex-shrink w-32">
+        <?= form_label(
+            lang('Podcast.form.partner_id'),
+            'partner_id',
+            [],
+            lang('Podcast.form.partner_id_hint'),
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_id',
+            'name' => 'partner_id',
+            'class' => 'form-input w-full',
+            'value' => old('partner_id'),
+        ]) ?>
+    </div>
+    <div class="flex flex-col flex-1">
+        <?= form_label(
+            lang('Podcast.form.partner_link_url'),
+            'partner_link_url',
+            [],
+            lang('Podcast.form.partner_link_url_hint'),
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_link_url',
+            'name' => 'partner_link_url',
+            'class' => 'form-input w-full',
+            'value' => old('partner_link_url'),
+        ]) ?>
+    </div>
+    <div class="flex flex-col flex-1">
+        <?= form_label(
+            lang('Podcast.form.partner_image_url'),
+            'partner_image_url',
+            [],
+            lang('Podcast.form.partner_image_url_hint'),
+
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_image_url',
+            'name' => 'partner_image_url',
+            'class' => 'form-input w-full',
+            'value' => old('partner_image_url'),
+        ]) ?>
+    </div>
+</div>
+
 <?= form_section_close() ?>
 
 <?= form_section(
diff --git a/app/Views/admin/podcast/edit.php b/app/Views/admin/podcast/edit.php
index 79b30f357b..749f995f08 100644
--- a/app/Views/admin/podcast/edit.php
+++ b/app/Views/admin/podcast/edit.php
@@ -289,6 +289,55 @@
     'class' => 'form-input mb-4',
     'value' => old('payment_pointer', $podcast->payment_pointer),
 ]) ?>
+
+<?= form_label(lang('Podcast.form.partnership')) ?>
+<div class="flex flex-col mb-4 gap-x-2 gap-y-4 md:flex-row">
+    <div class="flex flex-col flex-shrink w-32">
+        <?= form_label(
+            lang('Podcast.form.partner_id'),
+            'partner_id',
+            [],
+            lang('Podcast.form.partner_id_hint'),
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_id',
+            'name' => 'partner_id',
+            'class' => 'form-input w-full',
+            'value' => old('partner_id', $podcast->partner_id),
+        ]) ?>
+    </div>
+    <div class="flex flex-col flex-1">
+        <?= form_label(
+            lang('Podcast.form.partner_link_url'),
+            'partner_link_url',
+            [],
+            lang('Podcast.form.partner_link_url_hint'),
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_link_url',
+            'name' => 'partner_link_url',
+            'class' => 'form-input w-full',
+            'value' => old('partner_link_url', $podcast->partner_link_url),
+        ]) ?>
+    </div>
+    <div class="flex flex-col flex-1">
+        <?= form_label(
+            lang('Podcast.form.partner_image_url'),
+            'partner_image_url',
+            [],
+            lang('Podcast.form.partner_image_url_hint'),
+            true
+        ) ?>
+        <?= form_input([
+            'id' => 'partner_image_url',
+            'name' => 'partner_image_url',
+            'class' => 'form-input w-full',
+            'value' => old('partner_image_url', $podcast->partner_image_url),
+        ]) ?>
+    </div>
+</div>
 <?= form_section_close() ?>
 
 <?= form_section(
diff --git a/app/Views/episode.php b/app/Views/episode.php
index 5570d4e7fa..2f5ec68408 100644
--- a/app/Views/episode.php
+++ b/app/Views/episode.php
@@ -131,7 +131,7 @@
         </div>
       </header>      
       <section class="w-full max-w-3xl px-2 py-6 mx-auto prose md:px-6">
-      <?= $episode->description_html ?>
+      <?= $episode->getDescriptionHtml('-+Website+-') ?>
       </section>
     </main>
     <footer class="px-2 py-4 border-t ">
-- 
GitLab