Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • adaures/castopod
  • mkljczk/castopod-host
  • spaetz/castopod-host
  • PatrykMis/castopod
  • jonas/castopod
  • ajeremias/castopod
  • misuzu/castopod
  • KrzysztofDomanczyk/castopod
  • Behel/castopod
  • nebulon/castopod
  • ewen/castopod
  • NeoluxConsulting/castopod
  • nateritter/castopod-og
  • prcutler/castopod
14 results
Show changes
Commits on Source (2)
Showing
with 336 additions and 14 deletions
# [1.0.0-alpha.40](https://code.podlibre.org/podlibre/castopod/compare/v1.0.0-alpha.39...v1.0.0-alpha.40) (2021-03-19)
### Features
* **custom-rss:** add custom xml tag injection in rss feed for ❬channel❭ and ❬item❭ ([6ecdaad](https://code.podlibre.org/podlibre/castopod/commit/6ecdaad911d06b7f7a2b7d24710968c7eb9118f6))
# [1.0.0-alpha.39](https://code.podlibre.org/podlibre/castopod/compare/v1.0.0-alpha.38...v1.0.0-alpha.39) (2021-03-01)
......
......@@ -7,7 +7,7 @@
//
// NOTE: this constant is updated upon release with Continuous Integration.
//
defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.39');
defined('CP_VERSION') || define('CP_VERSION', '1.0.0-alpha.40');
//--------------------------------------------------------------------
// App Namespace
......
......@@ -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')
......
{
"name": "podlibre/castopod",
"version": "1.0.0-alpha39",
"version": "1.0.0-alpha40",
"type": "project",
"description": "Castopod is an open-source hosting platform made for podcasters who want engage and interact with their audience.",
"homepage": "https://castopod.org",
......