mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
feat(custom-rss): add custom xml tag injection in rss feed for ❬channel❭ and ❬item❭
This commit is contained in:
parent
ea5383643c
commit
6ecdaad911
@ -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
|
||||
|
@ -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';
|
||||
|
@ -138,6 +138,10 @@ class AddPodcasts extends Migration
|
||||
'constraint' => 12,
|
||||
'null' => true,
|
||||
],
|
||||
'custom_rss' => [
|
||||
'type' => 'JSON',
|
||||
'null' => true,
|
||||
],
|
||||
'created_by' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
|
@ -124,6 +124,10 @@ class AddEpisodes extends Migration
|
||||
'constraint' => 12,
|
||||
'null' => true,
|
||||
],
|
||||
'custom_rss' => [
|
||||
'type' => 'JSON',
|
||||
'null' => true,
|
||||
],
|
||||
'created_by' => [
|
||||
'type' => 'INT',
|
||||
'unsigned' => true,
|
||||
|
@ -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(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>'
|
||||
))
|
||||
->addChild('channel')
|
||||
->addChild('item');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode
|
||||
);
|
||||
return str_replace(['<item>', '</item>'], '', $xmlNode->asXML());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves custom rss tag into json
|
||||
*
|
||||
* @param string $customRssString
|
||||
*
|
||||
*/
|
||||
function setCustomRssString($customRssString)
|
||||
{
|
||||
helper('rss');
|
||||
$customRssArray = rss_to_array(
|
||||
simplexml_load_string(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel><item>' .
|
||||
$customRssString .
|
||||
'</item></channel></rss>'
|
||||
)
|
||||
)['elements'][0]['elements'][0];
|
||||
if (array_key_exists('elements', $customRssArray)) {
|
||||
$this->attributes['custom_rss'] = json_encode(
|
||||
$customRssArray['elements']
|
||||
);
|
||||
} else {
|
||||
$this->attributes['custom_rss'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"/>'
|
||||
))->addChild('channel');
|
||||
array_to_rss(
|
||||
[
|
||||
'elements' => $this->custom_rss,
|
||||
],
|
||||
$xmlNode
|
||||
);
|
||||
return str_replace(
|
||||
['<channel>', '</channel>'],
|
||||
'',
|
||||
$xmlNode->asXML()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves custom rss tag into json
|
||||
*
|
||||
* @param string $customRssString
|
||||
*
|
||||
*/
|
||||
function setCustomRssString($customRssString)
|
||||
{
|
||||
helper('rss');
|
||||
$customRssArray = rss_to_array(
|
||||
simplexml_load_string(
|
||||
'<?xml version="1.0" encoding="utf-8"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:podcast="https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md" xmlns:content="http://purl.org/rss/1.0/modules/content/" version="2.0"><channel>' .
|
||||
$customRssString .
|
||||
'</channel></rss>'
|
||||
)
|
||||
)['elements'][0];
|
||||
if (array_key_exists('elements', $customRssArray)) {
|
||||
$this->attributes['custom_rss'] = json_encode(
|
||||
$customRssArray['elements']
|
||||
);
|
||||
} else {
|
||||
$this->attributes['custom_rss'] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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',
|
||||
],
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
],
|
||||
|
@ -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',
|
||||
|
@ -38,6 +38,7 @@ class EpisodeModel extends Model
|
||||
'location_name',
|
||||
'location_geo',
|
||||
'location_osmid',
|
||||
'custom_rss',
|
||||
'published_at',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
|
@ -41,6 +41,7 @@ class PodcastModel extends Model
|
||||
'location_geo',
|
||||
'location_osmid',
|
||||
'payment_pointer',
|
||||
'custom_rss',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
];
|
||||
|
@ -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,
|
||||
|
@ -388,6 +388,25 @@
|
||||
</div>
|
||||
<?= 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,
|
||||
|
@ -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',
|
||||
]) ?>
|
||||
<legend>
|
||||
<?= lang('Podcast.form.type.label') .
|
||||
hint_tooltip(lang('Podcast.form.type.hint'), 'ml-1') ?>
|
||||
</legend>
|
||||
<?= 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
|
||||
) ?>
|
||||
<label for="episodic"><?= lang('Podcast.form.type.episodic') ?></label>
|
||||
<?= 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')
|
||||
|
@ -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')
|
||||
|
Loading…
x
Reference in New Issue
Block a user