diff --git a/app/Controllers/Admin/Episode.php b/app/Controllers/Admin/Episode.php
index 92f9a8ea..0ab9a8e5 100644
--- a/app/Controllers/Admin/Episode.php
+++ b/app/Controllers/Admin/Episode.php
@@ -141,6 +141,7 @@ class Episode extends BaseController
: null,
'type' => $this->request->getPost('type'),
'is_blocked' => $this->request->getPost('block') == 'yes',
+ 'custom_rss_string' => $this->request->getPost('custom_rss'),
'created_by' => user(),
'updated_by' => user(),
'published_at' => $publicationDate
@@ -236,6 +237,9 @@ class Episode extends BaseController
: null;
$this->episode->type = $this->request->getPost('type');
$this->episode->is_blocked = $this->request->getPost('block') == 'yes';
+ $this->episode->custom_rss_string = $this->request->getPost(
+ 'custom_rss'
+ );
$publicationDate = $this->request->getPost('publication_date');
$this->episode->published_at = $publicationDate
diff --git a/app/Controllers/Admin/Podcast.php b/app/Controllers/Admin/Podcast.php
index 342280c5..5e467a3b 100644
--- a/app/Controllers/Admin/Podcast.php
+++ b/app/Controllers/Admin/Podcast.php
@@ -163,6 +163,7 @@ class Podcast extends BaseController
'copyright' => $this->request->getPost('copyright'),
'location' => $this->request->getPost('location_name'),
'payment_pointer' => $this->request->getPost('payment_pointer'),
+ 'custom_rss_string' => $this->request->getPost('custom_rss'),
'is_blocked' => $this->request->getPost('block') === 'yes',
'is_completed' => $this->request->getPost('complete') === 'yes',
'is_locked' => $this->request->getPost('lock') === 'yes',
@@ -259,6 +260,9 @@ class Podcast extends BaseController
$this->podcast->payment_pointer = $this->request->getPost(
'payment_pointer'
);
+ $this->podcast->custom_rss_string = $this->request->getPost(
+ 'custom_rss'
+ );
$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 19a81616..e926d36f 100644
--- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
+++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php
@@ -138,6 +138,10 @@ class AddPodcasts extends Migration
'constraint' => 12,
'null' => true,
],
+ 'custom_rss' => [
+ 'type' => 'JSON',
+ 'null' => true,
+ ],
'created_by' => [
'type' => 'INT',
'unsigned' => true,
diff --git a/app/Database/Migrations/2020-06-05-170000_add_episodes.php b/app/Database/Migrations/2020-06-05-170000_add_episodes.php
index 7d33d942..72f7f199 100644
--- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php
+++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php
@@ -124,6 +124,10 @@ class AddEpisodes extends Migration
'constraint' => 12,
'null' => true,
],
+ 'custom_rss' => [
+ 'type' => 'JSON',
+ 'null' => true,
+ ],
'created_by' => [
'type' => 'INT',
'unsigned' => true,
diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php
index 99bc4804..f79dfbe0 100644
--- a/app/Entities/Episode.php
+++ b/app/Entities/Episode.php
@@ -106,6 +106,13 @@ class Episode extends Entity
*/
protected $publication_status;
+ /**
+ * Return custom rss as string
+ *
+ * @var string
+ */
+ protected $custom_rss_string;
+
protected $dates = [
'published_at',
'created_at',
@@ -136,6 +143,7 @@ class Episode extends Entity
'location_name' => '?string',
'location_geo' => '?string',
'location_osmid' => '?string',
+ 'custom_rss' => '?json-array',
'created_by' => 'integer',
'updated_by' => 'integer',
];
@@ -564,4 +572,56 @@ class Episode extends Entity
}
return $this;
}
+
+ /**
+ * Get custom rss tag as XML String
+ *
+ * @return string
+ *
+ */
+ function getCustomRssString()
+ {
+ helper('rss');
+ if (empty($this->attributes['custom_rss'])) {
+ return '';
+ } else {
+ $xmlNode = (new \App\Libraries\SimpleRSSElement(
+ ''
+ ))
+ ->addChild('channel')
+ ->addChild('item');
+ array_to_rss(
+ [
+ 'elements' => $this->custom_rss,
+ ],
+ $xmlNode
+ );
+ return str_replace(['- ', '
'], '', $xmlNode->asXML());
+ }
+ }
+
+ /**
+ * Saves custom rss tag into json
+ *
+ * @param string $customRssString
+ *
+ */
+ function setCustomRssString($customRssString)
+ {
+ helper('rss');
+ $customRssArray = rss_to_array(
+ simplexml_load_string(
+ '- ' .
+ $customRssString .
+ '
'
+ )
+ )['elements'][0]['elements'][0];
+ if (array_key_exists('elements', $customRssArray)) {
+ $this->attributes['custom_rss'] = json_encode(
+ $customRssArray['elements']
+ );
+ } else {
+ $this->attributes['custom_rss'] = null;
+ }
+ }
}
diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php
index aa4a4afe..813123f5 100644
--- a/app/Entities/Podcast.php
+++ b/app/Entities/Podcast.php
@@ -80,6 +80,13 @@ class Podcast extends Entity
*/
protected $description;
+ /**
+ * Return custom rss as string
+ *
+ * @var string
+ */
+ protected $custom_rss_string;
+
protected $casts = [
'id' => 'integer',
'title' => 'string',
@@ -106,6 +113,7 @@ class Podcast extends Entity
'location_geo' => '?string',
'location_osmid' => '?string',
'payment_pointer' => '?string',
+ 'custom_rss' => '?json-array',
'created_by' => 'integer',
'updated_by' => 'integer',
];
@@ -480,4 +488,58 @@ class Podcast extends Entity
}
return $this;
}
+
+ /**
+ * Get custom rss tag as XML String
+ *
+ * @return string
+ *
+ */
+ function getCustomRssString()
+ {
+ helper('rss');
+ if (empty($this->attributes['custom_rss'])) {
+ return '';
+ } else {
+ $xmlNode = (new \App\Libraries\SimpleRSSElement(
+ ''
+ ))->addChild('channel');
+ array_to_rss(
+ [
+ 'elements' => $this->custom_rss,
+ ],
+ $xmlNode
+ );
+ return str_replace(
+ ['', ''],
+ '',
+ $xmlNode->asXML()
+ );
+ }
+ }
+
+ /**
+ * Saves custom rss tag into json
+ *
+ * @param string $customRssString
+ *
+ */
+ function setCustomRssString($customRssString)
+ {
+ helper('rss');
+ $customRssArray = rss_to_array(
+ simplexml_load_string(
+ '' .
+ $customRssString .
+ ''
+ )
+ )['elements'][0];
+ if (array_key_exists('elements', $customRssArray)) {
+ $this->attributes['custom_rss'] = json_encode(
+ $customRssArray['elements']
+ );
+ } else {
+ $this->attributes['custom_rss'] = null;
+ }
+ }
}
diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php
index 69a08539..debb495d 100644
--- a/app/Helpers/rss_helper.php
+++ b/app/Helpers/rss_helper.php
@@ -242,6 +242,15 @@ function get_rss_feed($podcast, $serviceSlug = '')
$image->addChild('title', $podcast->title);
$image->addChild('link', $podcast->link);
+ if (!empty($podcast->custom_rss)) {
+ array_to_rss(
+ [
+ 'elements' => $podcast->custom_rss,
+ ],
+ $channel
+ );
+ }
+
foreach ($episodes as $episode) {
$item = $channel->addChild('item');
$item->addChild('title', $episode->title);
@@ -393,6 +402,15 @@ function get_rss_feed($podcast, $serviceSlug = '')
$episode->is_blocked &&
$item->addChild('block', 'Yes', $itunes_namespace);
+
+ if (!empty($episode->custom_rss)) {
+ array_to_rss(
+ [
+ 'elements' => $episode->custom_rss,
+ ],
+ $item
+ );
+ }
}
return $rss->asXML();
@@ -429,3 +447,71 @@ function add_category_tag($node, $category)
}
$node->addChild('category', $category->apple_category);
}
+
+/**
+ * Converts XML to array
+ *
+ * @param \SimpleRSSElement $xmlNode
+ *
+ * @return array
+ */
+function rss_to_array($xmlNode)
+{
+ $nameSpaces = [
+ '',
+ 'http://www.itunes.com/dtds/podcast-1.0.dtd',
+ 'https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md',
+ ];
+ $arrayNode = [];
+ $arrayNode['name'] = $xmlNode->getName();
+ $arrayNode['namespace'] = $xmlNode->getNamespaces(false);
+ if (count($xmlNode->attributes()) > 0) {
+ foreach ($xmlNode->attributes() as $key => $value) {
+ $arrayNode['attributes'][$key] = (string) $value;
+ }
+ }
+ $textcontent = trim((string) $xmlNode);
+ if (strlen($textcontent) > 0) {
+ $arrayNode['content'] = $textcontent;
+ }
+ foreach ($nameSpaces as $currentNameSpace) {
+ foreach ($xmlNode->children($currentNameSpace) as $childXmlNode) {
+ $arrayNode['elements'][] = rss_to_array($childXmlNode);
+ }
+ }
+ return $arrayNode;
+}
+
+/**
+ * Inserts array (converted to XML node) in XML node
+ *
+ * @param array $arrayNode
+ * @param \SimpleRSSElement $xmlNode The XML parent node where this arrayNode should be attached
+ *
+ */
+function array_to_rss($arrayNode, &$xmlNode)
+{
+ if (array_key_exists('elements', $arrayNode)) {
+ foreach ($arrayNode['elements'] as $childArrayNode) {
+ $childXmlNode = $xmlNode->addChild(
+ $childArrayNode['name'],
+ array_key_exists('content', $childArrayNode)
+ ? $childArrayNode['content']
+ : null,
+ empty($childArrayNode['namespace'])
+ ? null
+ : current($childArrayNode['namespace'])
+ );
+ if (array_key_exists('attributes', $childArrayNode)) {
+ foreach (
+ $childArrayNode['attributes']
+ as $attributeKey => $attributeValue
+ ) {
+ $childXmlNode->addAttribute($attributeKey, $attributeValue);
+ }
+ }
+ array_to_rss($childArrayNode, $childXmlNode);
+ }
+ }
+ return $xmlNode;
+}
diff --git a/app/Language/en/Episode.php b/app/Language/en/Episode.php
index 18df1b40..27f448e1 100644
--- a/app/Language/en/Episode.php
+++ b/app/Language/en/Episode.php
@@ -87,6 +87,11 @@ return [
'location_section_subtitle' => 'What place is this episode about?',
'location_name' => 'Location name or address',
'location_name_hint' => 'This can be a real place or fictional',
+ 'advanced_section_title' => 'Advanced Parameters',
+ 'advanced_section_subtitle' =>
+ 'If you need RSS tags that Castopod does not handle, set them here.',
+ 'custom_rss' => 'Custom RSS tags for the episode',
+ 'custom_rss_hint' => 'This will be injected within the ❬item❭ tag.',
'submit_create' => 'Create episode',
'submit_edit' => 'Save episode',
],
diff --git a/app/Language/en/Podcast.php b/app/Language/en/Podcast.php
index f7c87572..5afb4554 100644
--- a/app/Language/en/Podcast.php
+++ b/app/Language/en/Podcast.php
@@ -72,6 +72,11 @@ return [
'payment_pointer' => 'Payment Pointer for Web Monetization',
'payment_pointer_hint' =>
'This is your where you will receive money thanks to Web Monetization',
+ 'advanced_section_title' => 'Advanced Parameters',
+ 'advanced_section_subtitle' =>
+ '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.',
'status_section_title' => 'Status',
'status_section_subtitle' => 'Dead or alive?',
'block' => 'Podcast should be hidden from all platforms',
diff --git a/app/Language/fr/Episode.php b/app/Language/fr/Episode.php
index 78f94404..70f233af 100644
--- a/app/Language/fr/Episode.php
+++ b/app/Language/fr/Episode.php
@@ -88,6 +88,11 @@ return [
'location_section_subtitle' => 'De quel lieu cet épisode parle-t-il ?',
'location_name' => 'Nom ou adresse du lieu',
'location_name_hint' => 'Ce lieu peut être réel ou fictif',
+ 'advanced_section_title' => 'Paramètres avancés',
+ 'advanced_section_subtitle' =>
+ 'Si vous avez besoin d’une balise que nous n’avons pas couverte, définissez-la ici.',
+ 'custom_rss' => 'Balises RSS personnalisées pour l’épisode',
+ 'custom_rss_hint' => 'Ceci sera injecté dans la balise ❬item❭.',
'submit_create' => 'Créer l’épisode',
'submit_edit' => 'Enregistrer l’épisode',
],
diff --git a/app/Language/fr/Podcast.php b/app/Language/fr/Podcast.php
index 20672a4c..02d3cfb9 100644
--- a/app/Language/fr/Podcast.php
+++ b/app/Language/fr/Podcast.php
@@ -74,6 +74,11 @@ return [
'Adresse de paiement (Payment Pointer) pour Web Monetization',
'payment_pointer_hint' =>
'L’adresse où vous recevrez de l’argent grâce à Web Monetization',
+ 'advanced_section_title' => 'Paramètres avancés',
+ 'advanced_section_subtitle' =>
+ '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❭.',
'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/EpisodeModel.php b/app/Models/EpisodeModel.php
index 3237daaf..6a071434 100644
--- a/app/Models/EpisodeModel.php
+++ b/app/Models/EpisodeModel.php
@@ -38,6 +38,7 @@ class EpisodeModel extends Model
'location_name',
'location_geo',
'location_osmid',
+ 'custom_rss',
'published_at',
'created_by',
'updated_by',
diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php
index fc23d5dd..6f0e360c 100644
--- a/app/Models/PodcastModel.php
+++ b/app/Models/PodcastModel.php
@@ -41,6 +41,7 @@ class PodcastModel extends Model
'location_geo',
'location_osmid',
'payment_pointer',
+ 'custom_rss',
'created_by',
'updated_by',
];
diff --git a/app/Views/admin/episode/create.php b/app/Views/admin/episode/create.php
index 35f6fc0d..2c5dffda 100644
--- a/app/Views/admin/episode/create.php
+++ b/app/Views/admin/episode/create.php
@@ -324,6 +324,25 @@
]) ?>
= form_section_close() ?>
+= form_section(
+ lang('Episode.form.advanced_section_title'),
+ lang('Episode.form.advanced_section_subtitle')
+) ?>
+= form_label(
+ lang('Episode.form.custom_rss'),
+ 'custom_rss',
+ [],
+ lang('Episode.form.custom_rss_hint'),
+ true
+) ?>
+= form_textarea([
+ 'id' => 'custom_rss',
+ 'name' => 'custom_rss',
+ 'class' => 'form-textarea',
+ 'value' => old('custom_rss'),
+]) ?>
+= form_section_close() ?>
+
= button(
lang('Episode.form.submit_create'),
null,
diff --git a/app/Views/admin/episode/edit.php b/app/Views/admin/episode/edit.php
index 9783ce5b..710cda7e 100644
--- a/app/Views/admin/episode/edit.php
+++ b/app/Views/admin/episode/edit.php
@@ -388,6 +388,25 @@
= form_section_close() ?>
+= form_section(
+ lang('Episode.form.advanced_section_title'),
+ lang('Episode.form.advanced_section_subtitle')
+) ?>
+= form_label(
+ lang('Episode.form.custom_rss'),
+ 'custom_rss',
+ [],
+ lang('Episode.form.custom_rss_hint'),
+ true
+) ?>
+= form_textarea([
+ 'id' => 'custom_rss',
+ 'name' => 'custom_rss',
+ 'class' => 'form-textarea',
+ 'value' => old('custom_rss', $episode->custom_rss_string),
+]) ?>
+= form_section_close() ?>
+
= button(
lang('Episode.form.submit_edit'),
null,
diff --git a/app/Views/admin/podcast/create.php b/app/Views/admin/podcast/create.php
index 0989f639..33d6d3e8 100644
--- a/app/Views/admin/podcast/create.php
+++ b/app/Views/admin/podcast/create.php
@@ -27,7 +27,6 @@
'id' => 'image',
'name' => 'image',
'class' => 'form-input',
-
'required' => 'required',
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
@@ -59,27 +58,21 @@
'required' => 'required',
]) ?>
-= form_fieldset('', ['class' => 'mb-4']) ?>
+= form_fieldset('', [
+ 'class' => 'mb-4',
+]) ?>
= form_radio(
- [
- 'id' => 'episodic',
- 'name' => 'type',
- 'class' => 'form-radio-btn',
- ],
+ ['id' => 'episodic', 'name' => 'type', 'class' => 'form-radio-btn'],
'episodic',
old('type') ? old('type') == 'episodic' : true
) ?>
= form_radio(
- [
- 'id' => 'serial',
- 'name' => 'type',
- 'class' => 'form-radio-btn',
- ],
+ ['id' => 'serial', 'name' => 'type', 'class' => 'form-radio-btn'],
'serial',
old('type') ? old('type') == 'serial' : false
) ?>
@@ -288,6 +281,25 @@
]) ?>
= form_section_close() ?>
+= form_section(
+ lang('Podcast.form.advanced_section_title'),
+ lang('Podcast.form.advanced_section_subtitle')
+) ?>
+= form_label(
+ lang('Podcast.form.custom_rss'),
+ 'custom_rss',
+ [],
+ lang('Podcast.form.custom_rss_hint'),
+ true
+) ?>
+= form_textarea([
+ 'id' => 'custom_rss',
+ 'name' => 'custom_rss',
+ 'class' => 'form-textarea',
+ 'value' => old('custom_rss'),
+]) ?>
+= form_section_close() ?>
+
= form_section(
lang('Podcast.form.status_section_title'),
lang('Podcast.form.status_section_subtitle')
diff --git a/app/Views/admin/podcast/edit.php b/app/Views/admin/podcast/edit.php
index 0150c65a..79b30f35 100644
--- a/app/Views/admin/podcast/edit.php
+++ b/app/Views/admin/podcast/edit.php
@@ -291,6 +291,25 @@
]) ?>
= form_section_close() ?>
+= form_section(
+ lang('Podcast.form.advanced_section_title'),
+ lang('Podcast.form.advanced_section_subtitle')
+) ?>
+= form_label(
+ lang('Podcast.form.custom_rss'),
+ 'custom_rss',
+ [],
+ lang('Podcast.form.custom_rss_hint'),
+ true
+) ?>
+= form_textarea([
+ 'id' => 'custom_rss',
+ 'name' => 'custom_rss',
+ 'class' => 'form-textarea',
+ 'value' => old('custom_rss', $podcast->custom_rss_string),
+]) ?>
+= form_section_close() ?>
+
= form_section(
lang('Podcast.form.status_section_title'),
lang('Podcast.form.status_section_subtitle')