From b869acb3a988a3616d883a41c25d9c8409bd5518 Mon Sep 17 00:00:00 2001 From: Yassine Doghri Date: Sun, 15 Dec 2024 17:34:36 +0000 Subject: [PATCH] refactor: remove fields from podcast and episode entities to be replaced with plugins --- ...170000_drop_deprecated_podcasts_fields.php | 86 ++++++++ ...180000_drop_deprecated_episodes_fields.php | 37 ++++ app/Entities/Episode.php | 206 +++--------------- app/Entities/Podcast.php | 177 +++------------ app/Helpers/rss_helper.php | 149 ++++--------- app/Helpers/seo_helper.php | 8 - .../{SimpleRSSElement.php => RssFeed.php} | 58 ++++- app/Models/EpisodeModel.php | 1 - app/Models/PodcastModel.php | 10 - app/Resources/js/modules/xml-editor.ts | 27 +-- app/Views/Components/Alert.php | 2 +- .../Components/Charts/ChartsComponent.php | 2 + app/Views/Components/Forms/Checkbox.php | 2 +- app/Views/Components/Forms/Field.php | 6 +- app/Views/Components/Forms/RadioButton.php | 2 +- app/Views/Components/Forms/Toggler.php | 2 +- app/Views/Components/Forms/XMLEditor.php | 2 +- app/Views/Components/IconButton.php | 4 +- app/Views/Components/Pill.php | 12 +- modules/Admin/Config/Routes.php | 8 - .../Admin/Controllers/EpisodeController.php | 58 +---- .../Admin/Controllers/PodcastController.php | 83 +------ modules/Analytics/Config/Analytics.php | 13 -- modules/Analytics/OP3.php | 34 --- .../Rest/V1/Controllers/EpisodeController.php | 1 - .../Plugins/Controllers/PluginController.php | 2 + modules/Plugins/Core/BasePlugin.php | 6 +- modules/Plugins/Core/PluginInterface.php | 6 +- modules/Plugins/Core/Plugins.php | 12 +- modules/Plugins/Core/RSS.php | 43 ++++ modules/Plugins/Manifest/Field.php | 4 +- modules/Plugins/Manifest/Repository.php | 4 +- modules/Plugins/Manifest/manifest.schema.json | 4 +- .../Seeds/FakeSinglePodcastApiSeeder.php | 72 +++--- tests/modules/Plugins/PluginsTest.php | 12 +- .../mocks/plugins/acme/all-hooks/Plugin.php | 6 +- .../plugins/acme/undeclared-hook/Plugin.php | 4 +- themes/cp_admin/episode/_sidebar.php | 12 +- themes/cp_admin/episode/create.php | 14 -- themes/cp_admin/episode/edit.php | 15 -- themes/cp_admin/plugins/_field.php | 12 + themes/cp_admin/podcast/_sidebar.php | 17 +- themes/cp_admin/podcast/create.php | 51 ----- themes/cp_admin/podcast/edit.php | 54 ----- .../cp_admin/podcast/monetization_other.php | 47 ---- themes/cp_app/episode/_layout-preview.php | 4 +- themes/cp_app/episode/_layout.php | 4 +- 47 files changed, 465 insertions(+), 930 deletions(-) create mode 100644 app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php create mode 100644 app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php rename app/Libraries/{SimpleRSSElement.php => RssFeed.php} (56%) delete mode 100644 modules/Analytics/OP3.php create mode 100644 modules/Plugins/Core/RSS.php delete mode 100644 themes/cp_admin/podcast/monetization_other.php diff --git a/app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php b/app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php new file mode 100644 index 00000000..99cde454 --- /dev/null +++ b/app/Database/Migrations/2024-12-10-170000_drop_deprecated_podcasts_fields.php @@ -0,0 +1,86 @@ +forge->dropColumn( + 'podcasts', + 'episode_description_footer_markdown,episode_description_footer_html,is_owner_email_removed_from_feed,medium,payment_pointer,verify_txt,custom_rss,partner_id,partner_link_url,partner_image_url' + ); + } + + #[Override] + public function down(): void + { + $fields = [ + 'episode_description_footer_markdown' => [ + 'type' => 'TEXT', + 'null' => true, + ], + 'episode_description_footer_html' => [ + 'type' => 'TEXT', + 'null' => true, + ], + 'is_owner_email_removed_from_feed' => [ + 'type' => 'BOOLEAN', + 'null' => false, + 'default' => 0, + 'after' => 'owner_email', + ], + 'medium' => [ + 'type' => "ENUM('podcast','music','audiobook')", + 'null' => false, + 'default' => 'podcast', + 'after' => 'type', + ], + 'payment_pointer' => [ + 'type' => 'VARCHAR', + 'constraint' => 128, + 'comment' => 'Wallet address for Web Monetization payments', + 'null' => true, + ], + 'verify_txt' => [ + 'type' => 'TEXT', + 'null' => true, + 'after' => 'location_osm', + ], + 'custom_rss' => [ + 'type' => 'JSON', + 'null' => true, + ], + '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, + ], + ]; + + $this->forge->addColumn('podcasts', $fields); + } +} diff --git a/app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php b/app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php new file mode 100644 index 00000000..2946cdc5 --- /dev/null +++ b/app/Database/Migrations/2024-12-10-180000_drop_deprecated_episodes_fields.php @@ -0,0 +1,37 @@ +forge->dropColumn('episodes', 'custom_rss'); + } + + #[Override] + public function down(): void + { + $fields = [ + 'custom_rss' => [ + 'type' => 'JSON', + 'null' => true, + ], + ]; + + $this->forge->addColumn('episodes', $fields); + } +} diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 44651d07..38604f69 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -11,7 +11,6 @@ declare(strict_types=1); namespace App\Entities; use App\Entities\Clip\Soundbite; -use App\Libraries\SimpleRSSElement; use App\Models\ClipModel; use App\Models\EpisodeCommentModel; use App\Models\EpisodeModel; @@ -29,14 +28,13 @@ use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension; use League\CommonMark\Extension\DisallowedRawHtml\DisallowedRawHtmlExtension; use League\CommonMark\Extension\SmartPunct\SmartPunctExtension; use League\CommonMark\MarkdownConverter; -use Modules\Analytics\OP3; use Modules\Media\Entities\Audio; use Modules\Media\Entities\Chapters; use Modules\Media\Entities\Image; use Modules\Media\Entities\Transcript; use Modules\Media\Models\MediaModel; +use Override; use RuntimeException; -use SimpleXMLElement; /** * @property int $id @@ -73,8 +71,6 @@ use SimpleXMLElement; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm - * @property array|null $custom_rss - * @property string $custom_rss_string * @property bool $is_published_on_hubs * @property int $posts_count * @property int $comments_count @@ -94,19 +90,19 @@ use SimpleXMLElement; */ class Episode extends Entity { - protected Podcast $podcast; + public string $link = ''; - protected string $link; + public string $audio_url = ''; + + public string $audio_web_url = ''; + + public string $audio_opengraph_url = ''; + + protected Podcast $podcast; protected ?Audio $audio = null; - protected string $audio_url; - - protected string $audio_web_url; - - protected string $audio_opengraph_url; - - protected string $embed_url; + protected string $embed_url = ''; protected ?Image $cover = null; @@ -140,8 +136,6 @@ class Episode extends Entity protected ?Location $location = null; - protected string $custom_rss_string; - protected ?string $publication_status = null; /** @@ -176,7 +170,6 @@ class Episode extends Entity 'location_name' => '?string', 'location_geo' => '?string', 'location_osm' => '?string', - 'custom_rss' => '?json-array', 'is_published_on_hubs' => 'boolean', 'posts_count' => 'integer', 'comments_count' => 'integer', @@ -185,6 +178,31 @@ class Episode extends Entity 'updated_by' => 'integer', ]; + /** + * @param array $data + */ + #[Override] + public function injectRawData(array $data): static + { + parent::injectRawData($data); + + $this->link = url_to('episode', esc($this->getPodcast()->handle, 'url'), esc($this->attributes['slug'], 'url')); + + $this->audio_url = url_to( + 'episode-audio', + $this->getPodcast() +->handle, + $this->slug, + $this->getAudio() +->file_extension + ); + + $this->audio_opengraph_url = $this->audio_url . '?_from=-+Open+Graph+-'; + $this->audio_web_url = $this->audio_url . '?_from=-+Website+-'; + + return $this; + } + public function setCover(UploadedFile | File $file = null): self { if (! $file instanceof File || ($file instanceof UploadedFile && ! $file->isValid())) { @@ -342,40 +360,6 @@ class Episode extends Entity return $this->chapters; } - public function getAudioUrl(): string - { - $audioURL = url_to( - 'episode-audio', - $this->getPodcast() -->handle, - $this->slug, - $this->getAudio() -->file_extension - ); - - // Wrap episode url with OP3 if episode is public and OP3 is enabled on this podcast - if (! $this->is_premium && service('settings')->get( - 'Analytics.enableOP3', - 'podcast:' . $this->podcast_id - )) { - $op3 = new OP3(config('Analytics')->OP3); - - return $op3->wrap($audioURL, $this); - } - - return $audioURL; - } - - public function getAudioWebUrl(): string - { - return $this->getAudioUrl() . '?_from=-+Website+-'; - } - - public function getAudioOpengraphUrl(): string - { - return $this->getAudioUrl() . '?_from=-+Open+Graph+-'; - } - /** * Gets transcript url from transcript file uri if it exists or returns the transcript_remote_url which can be null. */ @@ -468,11 +452,6 @@ class Episode extends Entity return $this->comments; } - public function getLink(): string - { - return url_to('episode', esc($this->getPodcast()->handle), esc($this->attributes['slug'])); - } - public function getEmbedUrl(string $theme = null): string { return $theme @@ -482,7 +461,7 @@ class Episode extends Entity public function setGuid(?string $guid = null): static { - $this->attributes['guid'] = $guid ?? $this->getLink(); + $this->attributes['guid'] = $guid ?? $this->link; return $this; } @@ -513,34 +492,6 @@ class Episode extends Entity return $this; } - public function getDescriptionHtml(?string $serviceSlug = null): string - { - $descriptionHtml = ''; - if ( - $this->getPodcast() - ->partner_id !== null && - $this->getPodcast() - ->partner_link_url !== null && - $this->getPodcast() - ->partner_image_url !== null - ) { - $descriptionHtml .= "
getPartnerLink( - $serviceSlug, - )}\" rel=\"sponsored noopener noreferrer\" target=\"_blank\">getPartnerImageUrl( - $serviceSlug, - )}\" alt=\"Partner image\" />
"; - } - - $descriptionHtml .= $this->attributes['description_html']; - - if ($this->getPodcast()->episode_description_footer_html) { - $descriptionHtml .= "
{$this->getPodcast() -->episode_description_footer_html}
"; - } - - return $descriptionHtml; - } - public function getDescription(): string { if ($this->description === null) { @@ -609,91 +560,6 @@ class Episode extends Entity return $this->location; } - /** - * Get custom rss tag as XML String - */ - public function getCustomRssString(): string - { - if ($this->custom_rss === null) { - return ''; - } - - helper('rss'); - - $xmlNode = (new SimpleRSSElement( - '', - )) - ->addChild('channel') - ->addChild('item'); - array_to_rss([ - 'elements' => $this->custom_rss, - ], $xmlNode); - - return str_replace(['', ''], '', (string) $xmlNode->asXML()); - } - - /** - * Saves custom rss tag into json - */ - public function setCustomRssString(?string $customRssString = null): static - { - if ($customRssString === '') { - $this->attributes['custom_rss'] = null; - return $this; - } - - helper('rss'); - - $customXML = simplexml_load_string( - '' . - $customRssString . - '', - ); - - if (! $customXML instanceof SimpleXMLElement) { - // TODO: Failed to parse custom xml, should return error? - return $this; - } - - $customRssArray = rss_to_array($customXML)['elements'][0]['elements'][0]; - - if (array_key_exists('elements', $customRssArray)) { - $this->attributes['custom_rss'] = json_encode($customRssArray['elements']); - } else { - $this->attributes['custom_rss'] = null; - } - - return $this; - } - - public function getPartnerLink(?string $serviceSlug = null): string - { - $partnerLink = - rtrim((string) $this->getPodcast()->partner_link_url, '/') . - '?pid=' . - $this->getPodcast() - ->partner_id . - '&guid=' . - urlencode((string) $this->attributes['guid']); - - if ($serviceSlug !== null) { - $partnerLink .= '&_from=' . $serviceSlug; - } - - return $partnerLink; - } - - public function getPartnerImageUrl(string $serviceSlug = null): string - { - return rtrim((string) $this->getPodcast()->partner_image_url, '/') . - '?pid=' . - $this->getPodcast() - ->partner_id . - '&guid=' . - urlencode((string) $this->attributes['guid']) . - ($serviceSlug !== null ? '&_from=' . $serviceSlug : ''); - } - public function getPreviewLink(): string { if ($this->preview_id === null) { diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index db74d409..f39c2826 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace App\Entities; -use App\Libraries\SimpleRSSElement; use App\Models\ActorModel; use App\Models\CategoryModel; use App\Models\EpisodeModel; @@ -62,12 +61,8 @@ use RuntimeException; * @property string|null $publisher * @property string $owner_name * @property string $owner_email - * @property bool $is_owner_email_removed_from_feed * @property string $type - * @property string $medium * @property string|null $copyright - * @property string|null $episode_description_footer_markdown - * @property string|null $episode_description_footer_html * @property bool $is_blocked * @property bool $is_completed * @property bool $is_locked @@ -77,15 +72,7 @@ use RuntimeException; * @property string|null $location_name * @property string|null $location_geo * @property string|null $location_osm - * @property string|null $payment_pointer - * @property array|null $custom_rss - * @property bool $is_op3_enabled - * @property string $op3_url - * @property string $custom_rss_string * @property bool $is_published_on_hubs - * @property string|null $partner_id - * @property string|null $partner_link_url - * @property string|null $partner_image_url * @property int $created_by * @property int $updated_by * @property string $publication_status @@ -166,8 +153,6 @@ class Podcast extends Entity protected ?Location $location = null; - protected string $custom_rss_string; - protected ?string $publication_status = null; /** @@ -180,44 +165,35 @@ class Podcast extends Entity * @var array */ protected $casts = [ - 'id' => 'integer', - 'guid' => 'string', - 'actor_id' => 'integer', - 'handle' => 'string', - 'title' => 'string', - 'description_markdown' => 'string', - 'description_html' => 'string', - 'cover_id' => 'int', - 'banner_id' => '?int', - 'language_code' => 'string', - 'category_id' => 'integer', - 'parental_advisory' => '?string', - 'publisher' => '?string', - 'owner_name' => 'string', - 'owner_email' => 'string', - 'is_owner_email_removed_from_feed' => 'boolean', - 'type' => 'string', - 'medium' => 'string', - 'copyright' => '?string', - 'episode_description_footer_markdown' => '?string', - 'episode_description_footer_html' => '?string', - 'is_blocked' => 'boolean', - 'is_completed' => 'boolean', - 'is_locked' => 'boolean', - 'is_premium_by_default' => 'boolean', - 'imported_feed_url' => '?string', - 'new_feed_url' => '?string', - 'location_name' => '?string', - 'location_geo' => '?string', - 'location_osm' => '?string', - 'payment_pointer' => '?string', - 'custom_rss' => '?json-array', - 'is_published_on_hubs' => 'boolean', - 'partner_id' => '?string', - 'partner_link_url' => '?string', - 'partner_image_url' => '?string', - 'created_by' => 'integer', - 'updated_by' => 'integer', + 'id' => 'integer', + 'guid' => 'string', + 'actor_id' => 'integer', + 'handle' => 'string', + 'title' => 'string', + 'description_markdown' => 'string', + 'description_html' => 'string', + 'cover_id' => 'int', + 'banner_id' => '?int', + 'language_code' => 'string', + 'category_id' => 'integer', + 'parental_advisory' => '?string', + 'publisher' => '?string', + 'owner_name' => 'string', + 'owner_email' => 'string', + 'type' => 'string', + 'copyright' => '?string', + 'is_blocked' => 'boolean', + 'is_completed' => 'boolean', + 'is_locked' => 'boolean', + 'is_premium_by_default' => 'boolean', + 'imported_feed_url' => '?string', + 'new_feed_url' => '?string', + 'location_name' => '?string', + 'location_geo' => '?string', + 'location_osm' => '?string', + 'is_published_on_hubs' => 'boolean', + 'created_by' => 'integer', + 'updated_by' => 'integer', ]; public function getAtHandle(): string @@ -454,42 +430,6 @@ class Podcast extends Entity return $this; } - public function setEpisodeDescriptionFooterMarkdown(?string $episodeDescriptionFooterMarkdown = null): static - { - if ($episodeDescriptionFooterMarkdown === null || $episodeDescriptionFooterMarkdown === '') { - $this->attributes[ - 'episode_description_footer_markdown' - ] = null; - $this->attributes[ - 'episode_description_footer_html' - ] = null; - - return $this; - } - - $config = [ - 'html_input' => 'escape', - 'allow_unsafe_links' => false, - ]; - - $environment = new Environment($config); - $environment->addExtension(new CommonMarkCoreExtension()); - $environment->addExtension(new AutolinkExtension()); - $environment->addExtension(new SmartPunctExtension()); - $environment->addExtension(new DisallowedRawHtmlExtension()); - - $converter = new MarkdownConverter($environment); - - $this->attributes[ - 'episode_description_footer_markdown' - ] = $episodeDescriptionFooterMarkdown; - $this->attributes[ - 'episode_description_footer_html' - ] = $converter->convert($episodeDescriptionFooterMarkdown); - - return $this; - } - public function getDescription(): string { if ($this->description === null) { @@ -638,68 +578,9 @@ class Podcast extends Entity return $this->location; } - /** - * Get custom rss tag as XML String - */ - public function getCustomRssString(): string - { - if ($this->attributes['custom_rss'] === null) { - return ''; - } - - helper('rss'); - - $xmlNode = (new SimpleRSSElement( - '', - ))->addChild('channel'); - array_to_rss([ - 'elements' => $this->custom_rss, - ], $xmlNode); - - return str_replace(['', ''], '', (string) $xmlNode->asXML()); - } - - /** - * Saves custom rss tag into json - */ - public function setCustomRssString(string $customRssString): static - { - if ($customRssString === '') { - $this->attributes['custom_rss'] = null; - return $this; - } - - 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; - } - - return $this; - } - public function getIsPremium(): bool { // podcast is premium if at least one of its episodes is set as premium return (new EpisodeModel())->doesPodcastHavePremiumEpisodes($this->id); } - - public function getIsOp3Enabled(): bool - { - return service('settings')->get('Analytics.enableOP3', 'podcast:' . $this->id); - } - - public function getOp3Url(): string - { - return 'https://op3.dev/show/' . $this->guid; - } } diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index 3bb5ce35..da39f1c7 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -10,7 +10,7 @@ declare(strict_types=1); use App\Entities\Category; use App\Entities\Location; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use App\Models\PodcastModel; use CodeIgniter\I18n\Time; use Config\Mimes; @@ -37,21 +37,13 @@ if (! function_exists('get_rss_feed')) { $episodes = $podcast->episodes; - $itunesNamespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - - $podcastNamespace = 'https://podcastindex.org/namespace/1.0'; - - $atomNamespace = 'http://www.w3.org/2005/Atom'; - - $rss = new SimpleRSSElement( - "" - ); + $rss = new RssFeed(); $plugins->rssBeforeChannel($podcast); $channel = $rss->addChild('channel'); - $atomLink = $channel->addChild('link', null, $atomNamespace); + $atomLink = $channel->addChild('link', null, RssFeed::ATOM_NAMESPACE); $atomLink->addAttribute('href', $podcast->feed_url); $atomLink->addAttribute('rel', 'self'); $atomLink->addAttribute('type', 'application/rss+xml'); @@ -60,14 +52,14 @@ if (! function_exists('get_rss_feed')) { $websubHubs = config('WebSub') ->hubs; foreach ($websubHubs as $websubHub) { - $atomLinkHub = $channel->addChild('link', null, $atomNamespace); + $atomLinkHub = $channel->addChild('link', null, RssFeed::ATOM_NAMESPACE); $atomLinkHub->addAttribute('href', $websubHub); $atomLinkHub->addAttribute('rel', 'hub'); $atomLinkHub->addAttribute('type', 'application/rss+xml'); } if ($podcast->new_feed_url !== null) { - $channel->addChild('new-feed-url', $podcast->new_feed_url, $itunesNamespace); + $channel->addChild('new-feed-url', $podcast->new_feed_url, RssFeed::ITUNES_NAMESPACE); } // the last build date corresponds to the creation of the feed.xml cache @@ -85,19 +77,17 @@ if (! function_exists('get_rss_feed')) { (new PodcastModel())->save($podcast); } - $channel->addChild('guid', $podcast->guid, $podcastNamespace); + $channel->addChild('guid', $podcast->guid, RssFeed::PODCAST_NAMESPACE); $channel->addChild('title', $podcast->title, null, false); $channel->addChildWithCDATA('description', $podcast->description_html); - $channel->addChild('medium', $podcast->medium, $podcastNamespace); - - $itunesImage = $channel->addChild('image', null, $itunesNamespace); + $itunesImage = $channel->addChild('image', null, RssFeed::ITUNES_NAMESPACE); $itunesImage->addAttribute('href', $podcast->cover->feed_url); $channel->addChild('language', $podcast->language_code); if ($podcast->location instanceof Location) { - $locationElement = $channel->addChild('location', $podcast->location->name, $podcastNamespace); + $locationElement = $channel->addChild('location', $podcast->location->name, RssFeed::PODCAST_NAMESPACE); if ($podcast->location->geo !== null) { $locationElement->addAttribute('geo', $podcast->location->geo); } @@ -107,38 +97,16 @@ if (! function_exists('get_rss_feed')) { } } - if ($podcast->payment_pointer !== null) { - $valueElement = $channel->addChild('value', null, $podcastNamespace); - $valueElement->addAttribute('type', 'webmonetization'); - $valueElement->addAttribute('method', 'ILP'); - $recipientElement = $valueElement->addChild('valueRecipient', null, $podcastNamespace); - $recipientElement->addAttribute('name', $podcast->owner_name); - $recipientElement->addAttribute('type', 'paymentpointer'); - $recipientElement->addAttribute('address', $podcast->payment_pointer); - $recipientElement->addAttribute('split', '100'); - } - - if ($podcast->is_owner_email_removed_from_feed) { - $channel - ->addChild('locked', $podcast->is_locked ? 'yes' : 'no', $podcastNamespace); - } else { - $channel - ->addChild('locked', $podcast->is_locked ? 'yes' : 'no', $podcastNamespace) - ->addAttribute('owner', $podcast->owner_email); - } - - if ($podcast->verify_txt !== null) { - $channel - ->addChild('txt', $podcast->verify_txt, $podcastNamespace) - ->addAttribute('purpose', 'verify'); - } + $channel + ->addChild('locked', $podcast->is_locked ? 'yes' : 'no', RssFeed::PODCAST_NAMESPACE) + ->addAttribute('owner', $podcast->owner_email); if ($podcast->imported_feed_url !== null) { - $channel->addChild('previousUrl', $podcast->imported_feed_url, $podcastNamespace); + $channel->addChild('previousUrl', $podcast->imported_feed_url, RssFeed::PODCAST_NAMESPACE); } foreach ($podcast->podcasting_platforms as $podcastingPlatform) { - $podcastingPlatformElement = $channel->addChild('id', null, $podcastNamespace); + $podcastingPlatformElement = $channel->addChild('id', null, RssFeed::PODCAST_NAMESPACE); $podcastingPlatformElement->addAttribute('platform', $podcastingPlatform->slug); if ($podcastingPlatform->account_id !== null) { $podcastingPlatformElement->addAttribute('id', $podcastingPlatform->account_id); @@ -149,7 +117,7 @@ if (! function_exists('get_rss_feed')) { } } - $castopodSocialElement = $channel->addChild('social', null, $podcastNamespace); + $castopodSocialElement = $channel->addChild('social', null, RssFeed::PODCAST_NAMESPACE); $castopodSocialElement->addAttribute('priority', '1'); $castopodSocialElement->addAttribute('platform', 'castopod'); $castopodSocialElement->addAttribute('protocol', 'activitypub'); @@ -157,7 +125,7 @@ if (! function_exists('get_rss_feed')) { $castopodSocialElement->addAttribute('accountUrl', $podcast->link); foreach ($podcast->social_platforms as $socialPlatform) { - $socialElement = $channel->addChild('social', null, $podcastNamespace); + $socialElement = $channel->addChild('social', null, RssFeed::PODCAST_NAMESPACE); $socialElement->addAttribute('priority', '2'); $socialElement->addAttribute('platform', $socialPlatform->slug); @@ -181,7 +149,7 @@ if (! function_exists('get_rss_feed')) { } if ($socialPlatform->slug === 'mastodon') { - $socialSignUpelement = $socialElement->addChild('socialSignUp', null, $podcastNamespace); + $socialSignUpelement = $socialElement->addChild('socialSignUp', null, RssFeed::PODCAST_NAMESPACE); $socialSignUpelement->addAttribute('priority', '1'); $socialSignUpelement->addAttribute( 'homeUrl', @@ -200,7 +168,7 @@ if (! function_exists('get_rss_feed')) { $castopodSocialSignUpelement = $castopodSocialElement->addChild( 'socialSignUp', null, - $podcastNamespace + RssFeed::PODCAST_NAMESPACE ); $castopodSocialSignUpelement->addAttribute('priority', '1'); $castopodSocialSignUpelement->addAttribute( @@ -224,7 +192,7 @@ if (! function_exists('get_rss_feed')) { $fundingPlatformElement = $channel->addChild( 'funding', $fundingPlatform->account_id, - $podcastNamespace, + RssFeed::PODCAST_NAMESPACE, ); $fundingPlatformElement->addAttribute('platform', $fundingPlatform->slug); if ($fundingPlatform->link_url !== null) { @@ -234,7 +202,7 @@ if (! function_exists('get_rss_feed')) { foreach ($podcast->persons as $person) { foreach ($person->roles as $role) { - $personElement = $channel->addChild('person', $person->full_name, $podcastNamespace); + $personElement = $channel->addChild('person', $person->full_name, RssFeed::PODCAST_NAMESPACE); $personElement->addAttribute('img', get_avatar_url($person, 'medium')); @@ -263,29 +231,26 @@ if (! function_exists('get_rss_feed')) { $channel->addChild( 'explicit', $podcast->parental_advisory === 'explicit' ? 'true' : 'false', - $itunesNamespace, + RssFeed::ITUNES_NAMESPACE, ); - $channel->addChild('author', $podcast->publisher ?: $podcast->owner_name, $itunesNamespace, false); + $channel->addChild('author', $podcast->publisher ?: $podcast->owner_name, RssFeed::ITUNES_NAMESPACE, false); $channel->addChild('link', $podcast->link); - $owner = $channel->addChild('owner', null, $itunesNamespace); + $owner = $channel->addChild('owner', null, RssFeed::ITUNES_NAMESPACE); - $owner->addChild('name', $podcast->owner_name, $itunesNamespace, false); + $owner->addChild('name', $podcast->owner_name, RssFeed::ITUNES_NAMESPACE, false); + $owner->addChild('email', $podcast->owner_email, RssFeed::ITUNES_NAMESPACE); - if (! $podcast->is_owner_email_removed_from_feed) { - $owner->addChild('email', $podcast->owner_email, $itunesNamespace); - } - - $channel->addChild('type', $podcast->type, $itunesNamespace); + $channel->addChild('type', $podcast->type, RssFeed::ITUNES_NAMESPACE); $podcast->copyright && $channel->addChild('copyright', $podcast->copyright); if ($podcast->is_blocked || $subscription instanceof Subscription) { - $channel->addChild('block', 'Yes', $itunesNamespace); + $channel->addChild('block', 'Yes', RssFeed::ITUNES_NAMESPACE); } if ($podcast->is_completed) { - $channel->addChild('complete', 'Yes', $itunesNamespace); + $channel->addChild('complete', 'Yes', RssFeed::ITUNES_NAMESPACE); } $image = $channel->addChild('image'); @@ -293,12 +258,6 @@ if (! function_exists('get_rss_feed')) { $image->addChild('title', $podcast->title, null, false); $image->addChild('link', $podcast->link); - if ($podcast->custom_rss !== null) { - array_to_rss([ - 'elements' => $podcast->custom_rss, - ], $channel); - } - // run plugins hook at the end $plugins->rssAfterChannel($podcast, $channel); @@ -328,7 +287,7 @@ if (! function_exists('get_rss_feed')) { $item->addChild('guid', $episode->guid); $item->addChild('pubDate', $episode->published_at->format(DATE_RFC1123)); if ($episode->location instanceof Location) { - $locationElement = $item->addChild('location', $episode->location->name, $podcastNamespace); + $locationElement = $item->addChild('location', $episode->location->name, RssFeed::PODCAST_NAMESPACE); if ($episode->location->geo !== null) { $locationElement->addAttribute('geo', $episode->location->geo); } @@ -338,10 +297,10 @@ if (! function_exists('get_rss_feed')) { } } - $item->addChildWithCDATA('description', $episode->getDescriptionHtml($serviceSlug)); - $item->addChild('duration', (string) round($episode->audio->duration), $itunesNamespace); + $item->addChildWithCDATA('description', $episode->description_html); + $item->addChild('duration', (string) round($episode->audio->duration), RssFeed::ITUNES_NAMESPACE); $item->addChild('link', $episode->link); - $episodeItunesImage = $item->addChild('image', null, $itunesNamespace); + $episodeItunesImage = $item->addChild('image', null, RssFeed::ITUNES_NAMESPACE); $episodeItunesImage->addAttribute('href', $episode->cover->feed_url); $episode->parental_advisory && @@ -350,18 +309,18 @@ if (! function_exists('get_rss_feed')) { $episode->parental_advisory === 'explicit' ? 'true' : 'false', - $itunesNamespace, + RssFeed::ITUNES_NAMESPACE, ); $episode->number && - $item->addChild('episode', (string) $episode->number, $itunesNamespace); + $item->addChild('episode', (string) $episode->number, RssFeed::ITUNES_NAMESPACE); $episode->season_number && - $item->addChild('season', (string) $episode->season_number, $itunesNamespace); - $item->addChild('episodeType', $episode->type, $itunesNamespace); + $item->addChild('season', (string) $episode->season_number, RssFeed::ITUNES_NAMESPACE); + $item->addChild('episodeType', $episode->type, RssFeed::ITUNES_NAMESPACE); // If episode is of type trailer, add podcast:trailer tag on channel level if ($episode->type === 'trailer') { - $trailer = $channel->addChild('trailer', $episode->title, $podcastNamespace); + $trailer = $channel->addChild('trailer', $episode->title, RssFeed::PODCAST_NAMESPACE); $trailer->addAttribute('pubdate', $episode->published_at->format(DATE_RFC2822)); $trailer->addAttribute( 'url', @@ -374,21 +333,15 @@ if (! function_exists('get_rss_feed')) { } } - // add podcast namespace tags for season and episode - $episode->season_number && - $item->addChild('season', (string) $episode->season_number, $podcastNamespace); - $episode->number && - $item->addChild('episode', (string) $episode->number, $podcastNamespace); - // add link to episode comments as podcast-activity format - $comments = $item->addChild('comments', null, $podcastNamespace); + $comments = $item->addChild('comments', null, RssFeed::PODCAST_NAMESPACE); $comments->addAttribute('uri', url_to('episode-comments', $podcast->handle, $episode->slug)); $comments->addAttribute('contentType', 'application/podcast-activity+json'); if ($episode->getPosts()) { $socialInteractUri = $episode->getPosts()[0] ->uri; - $socialInteractElement = $item->addChild('socialInteract', null, $podcastNamespace); + $socialInteractElement = $item->addChild('socialInteract', null, RssFeed::PODCAST_NAMESPACE); $socialInteractElement->addAttribute('uri', $socialInteractUri); $socialInteractElement->addAttribute('priority', '1'); $socialInteractElement->addAttribute('platform', 'castopod'); @@ -405,7 +358,7 @@ if (! function_exists('get_rss_feed')) { } if ($episode->transcript instanceof Transcript) { - $transcriptElement = $item->addChild('transcript', null, $podcastNamespace); + $transcriptElement = $item->addChild('transcript', null, RssFeed::PODCAST_NAMESPACE); $transcriptElement->addAttribute('url', $episode->transcript->file_url); $transcriptElement->addAttribute( 'type', @@ -420,21 +373,21 @@ if (! function_exists('get_rss_feed')) { } if ($episode->getChapters() instanceof Chapters) { - $chaptersElement = $item->addChild('chapters', null, $podcastNamespace); + $chaptersElement = $item->addChild('chapters', null, RssFeed::PODCAST_NAMESPACE); $chaptersElement->addAttribute('url', $episode->chapters->file_url); $chaptersElement->addAttribute('type', 'application/json+chapters'); } foreach ($episode->soundbites as $soundbite) { // TODO: differentiate video from soundbites? - $soundbiteElement = $item->addChild('soundbite', $soundbite->title, $podcastNamespace); + $soundbiteElement = $item->addChild('soundbite', $soundbite->title, RssFeed::PODCAST_NAMESPACE); $soundbiteElement->addAttribute('startTime', (string) $soundbite->start_time); $soundbiteElement->addAttribute('duration', (string) round($soundbite->duration, 3)); } foreach ($episode->persons as $person) { foreach ($person->roles as $role) { - $personElement = $item->addChild('person', esc($person->full_name), $podcastNamespace); + $personElement = $item->addChild('person', esc($person->full_name), RssFeed::PODCAST_NAMESPACE); $personElement->addAttribute( 'role', @@ -455,13 +408,7 @@ if (! function_exists('get_rss_feed')) { } if ($episode->is_blocked) { - $item->addChild('block', 'Yes', $itunesNamespace); - } - - if ($episode->custom_rss !== null) { - array_to_rss([ - 'elements' => $episode->custom_rss, - ], $item); + $item->addChild('block', 'Yes', RssFeed::ITUNES_NAMESPACE); } $plugins->rssAfterItem($episode, $item); @@ -477,9 +424,7 @@ if (! function_exists('add_category_tag')) { */ function add_category_tag(SimpleXMLElement $node, Category $category): void { - $itunesNamespace = 'http://www.itunes.com/dtds/podcast-1.0.dtd'; - - $itunesCategory = $node->addChild('category', null, $itunesNamespace); + $itunesCategory = $node->addChild('category', null, RssFeed::ITUNES_NAMESPACE); $itunesCategory->addAttribute( 'text', $category->parent instanceof Category @@ -488,7 +433,7 @@ if (! function_exists('add_category_tag')) { ); if ($category->parent instanceof Category) { - $itunesCategoryChild = $itunesCategory->addChild('category', null, $itunesNamespace); + $itunesCategoryChild = $itunesCategory->addChild('category', null, RssFeed::ITUNES_NAMESPACE); $itunesCategoryChild->addAttribute('text', $category->apple_category); $node->addChild('category', $category->parent->apple_category); } @@ -535,9 +480,9 @@ if (! function_exists('array_to_rss')) { * 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 + * @param RssFeed $xmlNode The XML parent node where this arrayNode should be attached */ - function array_to_rss(array $arrayNode, SimpleRSSElement &$xmlNode): SimpleRSSElement + function array_to_rss(array $arrayNode, RssFeed &$xmlNode): RssFeed { if (array_key_exists('elements', $arrayNode)) { foreach ($arrayNode['elements'] as $childArrayNode) { diff --git a/app/Helpers/seo_helper.php b/app/Helpers/seo_helper.php index e18e659a..9076cc3b 100644 --- a/app/Helpers/seo_helper.php +++ b/app/Helpers/seo_helper.php @@ -65,10 +65,6 @@ if (! function_exists('get_podcast_metatags')) { 'href' => url_to('podcast-activity', esc($podcast->handle)), ]); - if ($podcast->payment_pointer) { - $metatags->meta('monetization', $podcast->payment_pointer); - } - return '' . PHP_EOL . $metatags->__toString() . PHP_EOL . $schema->__toString(); @@ -123,10 +119,6 @@ if (! function_exists('get_episode_metatags')) { 'href' => url_to('episode', $episode->podcast->handle, $episode->slug), ]); - if ($episode->podcast->payment_pointer) { - $metatags->meta('monetization', $episode->podcast->payment_pointer); - } - return $metatags->__toString() . PHP_EOL . '%s", + $this::ATOM_NAMESPACE, + $this::ITUNES_NAMESPACE, + $this::PODCAST_NAMESPACE, + $contents + )); + } + /** * Adds a child with $value inside CDATA * @@ -67,4 +90,37 @@ class SimpleRSSElement extends SimpleXMLElement return $newChild; } + + /** + * Add RssFeed code into a RssFeed + * + * adapted from: https://stackoverflow.com/a/23527002 + * + * @param self|array $nodes + */ + public function appendNodes(self|array $nodes): void + { + if (! is_array($nodes)) { + $nodes = [$nodes]; + } + + foreach ($nodes as $element) { + $namespaces = $element->getNamespaces(); + $namespace = $namespaces[array_key_first($namespaces)] ?? null; + + if (trim((string) $element) === '') { + $simpleRSS = $this->addChild($element->getName(), null, $namespace); + } else { + $simpleRSS = $this->addChild($element->getName(), (string) $element, $namespace); + } + + foreach ($element->children() as $child) { + $simpleRSS->appendNodes($child); + } + + foreach ($element->attributes() as $name => $value) { + $simpleRSS->addAttribute($name, (string) $value); + } + } + } } diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 1757161c..69e3bf07 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -87,7 +87,6 @@ class EpisodeModel extends UuidModel 'location_name', 'location_geo', 'location_osm', - 'custom_rss', 'is_published_on_hubs', 'posts_count', 'comments_count', diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index 2385b391..90f62bf0 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -38,8 +38,6 @@ class PodcastModel extends Model 'handle', 'description_markdown', 'description_html', - 'episode_description_footer_markdown', - 'episode_description_footer_html', 'cover_id', 'banner_id', 'language_code', @@ -47,10 +45,8 @@ class PodcastModel extends Model 'parental_advisory', 'owner_name', 'owner_email', - 'is_owner_email_removed_from_feed', 'publisher', 'type', - 'medium', 'copyright', 'imported_feed_url', 'new_feed_url', @@ -60,13 +56,7 @@ class PodcastModel extends Model 'location_name', 'location_geo', 'location_osm', - 'verify_txt', - 'payment_pointer', - 'custom_rss', 'is_published_on_hubs', - 'partner_id', - 'partner_link_url', - 'partner_image_url', 'is_premium_by_default', 'published_at', 'created_by', diff --git a/app/Resources/js/modules/xml-editor.ts b/app/Resources/js/modules/xml-editor.ts index f4f5de57..8f57a1b2 100644 --- a/app/Resources/js/modules/xml-editor.ts +++ b/app/Resources/js/modules/xml-editor.ts @@ -5,7 +5,7 @@ import { syntaxHighlighting, } from "@codemirror/language"; import { Compartment, EditorState } from "@codemirror/state"; -import { keymap } from "@codemirror/view"; +import { keymap, ViewUpdate } from "@codemirror/view"; import { basicSetup, EditorView } from "codemirror"; import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, queryAssignedNodes, state } from "lit/decorators.js"; @@ -63,6 +63,12 @@ export class XMLEditor extends LitElement { language.of(xml()), minHeightEditor, syntaxHighlighting(defaultHighlightStyle), + EditorView.updateListener.of((viewUpdate: ViewUpdate) => { + if (viewUpdate.docChanged) { + // Document changed, update textarea value + this._textarea[0].value = viewUpdate.state.doc.toString(); + } + }), ], }); @@ -72,20 +78,11 @@ export class XMLEditor extends LitElement { parent: this.shadowRoot as ShadowRoot, }); - this._textarea[0].hidden = true; - if (this._textarea[0].form) { - this._textarea[0].form.addEventListener("submit", () => { - this._textarea[0].value = this.editorView.state.doc.toString(); - }); - } - } - - disconnectedCallback(): void { - if (this._textarea[0].form) { - this._textarea[0].form.removeEventListener("submit", () => { - this._textarea[0].value = this.editorView.state.doc.toString(); - }); - } + // hide textarea + this._textarea[0].style.position = "absolute"; + this._textarea[0].style.opacity = "0"; + this._textarea[0].style.zIndex = "-9999"; + this._textarea[0].style.pointerEvents = "none"; } static styles = css` diff --git a/app/Views/Components/Alert.php b/app/Views/Components/Alert.php index 840a17a1..a4587873 100644 --- a/app/Views/Components/Alert.php +++ b/app/Views/Components/Alert.php @@ -9,7 +9,7 @@ use ViewComponents\Component; class Alert extends Component { - protected array $props = ['glyph', 'title']; + protected array $props = ['glyph', 'title', 'variant']; protected string $glyph = ''; diff --git a/app/Views/Components/Charts/ChartsComponent.php b/app/Views/Components/Charts/ChartsComponent.php index ec16c29a..7d680f5a 100644 --- a/app/Views/Components/Charts/ChartsComponent.php +++ b/app/Views/Components/Charts/ChartsComponent.php @@ -9,6 +9,8 @@ use ViewComponents\Component; class ChartsComponent extends Component { + protected array $props = ['title', 'subtitle', 'dataUrl', 'type']; + protected string $title; protected string $subtitle = ''; diff --git a/app/Views/Components/Forms/Checkbox.php b/app/Views/Components/Forms/Checkbox.php index 8bf1bdaa..be7af505 100644 --- a/app/Views/Components/Forms/Checkbox.php +++ b/app/Views/Components/Forms/Checkbox.php @@ -9,7 +9,7 @@ use Override; class Checkbox extends FormComponent { - protected array $props = ['hint', 'isChecked']; + protected array $props = ['hint', 'helper', 'isChecked']; protected array $casts = [ 'isChecked' => 'boolean', diff --git a/app/Views/Components/Forms/Field.php b/app/Views/Components/Forms/Field.php index bb370367..62a39f2d 100644 --- a/app/Views/Components/Forms/Field.php +++ b/app/Views/Components/Forms/Field.php @@ -15,8 +15,8 @@ class Field extends Component 'isRequired', 'isReadonly', 'as', - 'helper', 'hint', + 'helper', ]; protected array $casts = [ @@ -34,10 +34,10 @@ class Field extends Component protected string $as = 'Input'; - protected string $helper = ''; - protected string $hint = ''; + protected string $helper = ''; + #[Override] public function render(): string { diff --git a/app/Views/Components/Forms/RadioButton.php b/app/Views/Components/Forms/RadioButton.php index 2a5ff270..8ea3fe53 100644 --- a/app/Views/Components/Forms/RadioButton.php +++ b/app/Views/Components/Forms/RadioButton.php @@ -8,7 +8,7 @@ use Override; class RadioButton extends FormComponent { - protected array $props = ['isChecked', 'hint']; + protected array $props = ['isSelected', 'description']; protected array $casts = [ 'isSelected' => 'boolean', diff --git a/app/Views/Components/Forms/Toggler.php b/app/Views/Components/Forms/Toggler.php index 75a22c81..291fe263 100644 --- a/app/Views/Components/Forms/Toggler.php +++ b/app/Views/Components/Forms/Toggler.php @@ -9,7 +9,7 @@ use Override; class Toggler extends FormComponent { - protected array $props = ['size', 'hint', 'isChecked']; + protected array $props = ['size', 'hint', 'helper', 'isChecked']; protected array $casts = [ 'isChecked' => 'boolean', diff --git a/app/Views/Components/Forms/XMLEditor.php b/app/Views/Components/Forms/XMLEditor.php index 1d63efa5..4960378b 100644 --- a/app/Views/Components/Forms/XMLEditor.php +++ b/app/Views/Components/Forms/XMLEditor.php @@ -32,7 +32,7 @@ class XMLEditor extends FormComponent $textarea = form_textarea($this->attributes, $this->content); return <<{$textarea} + {$textarea} HTML; } } diff --git a/app/Views/Components/IconButton.php b/app/Views/Components/IconButton.php index e065bd05..7c6f44ad 100644 --- a/app/Views/Components/IconButton.php +++ b/app/Views/Components/IconButton.php @@ -6,10 +6,10 @@ namespace App\Views\Components; class IconButton extends Button { - public string $glyph; - protected array $props = ['glyph']; + protected string $glyph; + public function __construct(array $attributes) { $iconButtonAttributes = [ diff --git a/app/Views/Components/Pill.php b/app/Views/Components/Pill.php index cd9c18e1..9d76cc5e 100644 --- a/app/Views/Components/Pill.php +++ b/app/Views/Components/Pill.php @@ -9,18 +9,18 @@ use ViewComponents\Component; class Pill extends Component { + protected array $props = ['size', 'variant', 'icon', 'iconClass', 'hint']; + /** * @var 'small'|'base' */ - public string $size = 'base'; + protected string $size = 'base'; - public string $variant = 'default'; + protected string $variant = 'default'; - public string $icon = ''; + protected string $icon = ''; - public string $iconClass = ''; - - protected array $props = ['size', 'variant', 'icon', 'iconClass', 'hint']; + protected string $iconClass = ''; protected string $hint = ''; diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php index 3e9e28bb..5407eb74 100644 --- a/modules/Admin/Config/Routes.php +++ b/modules/Admin/Config/Routes.php @@ -180,14 +180,6 @@ $routes->group( ], ); }); - $routes->get('monetization-other', 'PodcastController::monetizationOther/$1', [ - 'as' => 'podcast-monetization-other', - 'filter' => 'permission:podcast$1.edit', - ]); - $routes->post('monetization-other', 'PodcastController::monetizationOtherAction/$1', [ - 'as' => 'podcast-monetization-other', - 'filter' => 'permission:podcast$1.edit', - ]); $routes->group('analytics', static function ($routes): void { $routes->get('/', 'PodcastController::viewAnalytics/$1', [ 'as' => 'podcast-analytics', diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php index d83bae5c..52a91d84 100644 --- a/modules/Admin/Controllers/EpisodeController.php +++ b/modules/Admin/Controllers/EpisodeController.php @@ -190,9 +190,6 @@ class EpisodeController extends BaseController ->with('error', lang('Episode.messages.sameSlugError')); } - $db = db_connect(); - $db->transStart(); - $newEpisode = new Episode([ 'created_by' => user_id(), 'updated_by' => user_id(), @@ -217,11 +214,10 @@ class EpisodeController extends BaseController 'season_number' => $this->request->getPost('season_number') ? (int) $this->request->getPost('season_number') : null, - 'type' => $this->request->getPost('type'), - 'is_blocked' => $this->request->getPost('block') === 'yes', - 'custom_rss_string' => $this->request->getPost('custom_rss'), - 'is_premium' => $this->request->getPost('premium') === 'yes', - 'published_at' => null, + 'type' => $this->request->getPost('type'), + 'is_blocked' => $this->request->getPost('block') === 'yes', + 'is_premium' => $this->request->getPost('premium') === 'yes', + 'published_at' => null, ]); $transcriptChoice = $this->request->getPost('transcript-choice'); @@ -244,32 +240,12 @@ class EpisodeController extends BaseController $episodeModel = new EpisodeModel(); if (! ($newEpisodeId = $episodeModel->insert($newEpisode, true))) { - $db->transRollback(); return redirect() ->back() ->withInput() ->with('errors', $episodeModel->errors()); } - // update podcast's episode_description_footer_markdown if changed - $this->podcast->episode_description_footer_markdown = $this->request->getPost( - 'description_footer' - ) === '' ? null : $this->request->getPost('description_footer'); - - if ($this->podcast->hasChanged('episode_description_footer_markdown')) { - $podcastModel = new PodcastModel(); - - if (! $podcastModel->update($this->podcast->id, $this->podcast)) { - $db->transRollback(); - return redirect() - ->back() - ->withInput() - ->with('errors', $podcastModel->errors()); - } - } - - $db->transComplete(); - return redirect()->route('episode-view', [$this->podcast->id, $newEpisodeId])->with( 'message', lang('Episode.messages.createSuccess') @@ -330,7 +306,6 @@ class EpisodeController extends BaseController $this->episode->season_number = $this->request->getPost('season_number') ?: 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'); $this->episode->is_premium = $this->request->getPost('premium') === 'yes'; $this->episode->updated_by = (int) user_id(); @@ -376,39 +351,14 @@ class EpisodeController extends BaseController $this->episode->chapters_remote_url = $chaptersRemoteUrl === '' ? null : $chaptersRemoteUrl; } - $db = db_connect(); - $db->transStart(); - $episodeModel = new EpisodeModel(); - if (! $episodeModel->update($this->episode->id, $this->episode)) { - $db->transRollback(); - return redirect() ->back() ->withInput() ->with('errors', $episodeModel->errors()); } - // update podcast's episode_description_footer_markdown if changed - $this->podcast->episode_description_footer_markdown = $this->request->getPost( - 'description_footer' - ) === '' ? null : $this->request->getPost('description_footer'); - - if ($this->podcast->hasChanged('episode_description_footer_markdown')) { - $podcastModel = new PodcastModel(); - if (! $podcastModel->update($this->podcast->id, $this->podcast)) { - $db->transRollback(); - - return redirect() - ->back() - ->withInput() - ->with('errors', $podcastModel->errors()); - } - } - - $db->transComplete(); - return redirect()->route('episode-edit', [$this->podcast->id, $this->episode->id])->with( 'message', lang('Episode.messages.editSuccess') diff --git a/modules/Admin/Controllers/PodcastController.php b/modules/Admin/Controllers/PodcastController.php index aa8c726f..9130d065 100644 --- a/modules/Admin/Controllers/PodcastController.php +++ b/modules/Admin/Controllers/PodcastController.php @@ -213,18 +213,14 @@ class PodcastController extends BaseController 'parental_advisory' => $this->request->getPost('parental_advisory') !== 'undefined' ? $this->request->getPost('parental_advisory') : null, - 'owner_name' => $this->request->getPost('owner_name'), - 'owner_email' => $this->request->getPost('owner_email'), - 'is_owner_email_removed_from_feed' => $this->request->getPost('is_owner_email_removed_from_feed') === 'yes', - 'publisher' => $this->request->getPost('publisher'), - 'type' => $this->request->getPost('type'), - 'medium' => $this->request->getPost('medium'), - 'copyright' => $this->request->getPost('copyright'), - 'location' => $this->request->getPost('location_name') === '' ? null : new Location( + 'owner_name' => $this->request->getPost('owner_name'), + 'owner_email' => $this->request->getPost('owner_email'), + 'publisher' => $this->request->getPost('publisher'), + 'type' => $this->request->getPost('type'), + 'copyright' => $this->request->getPost('copyright'), + 'location' => $this->request->getPost('location_name') === '' ? null : new Location( $this->request->getPost('location_name') ), - 'verify_txt' => $this->request->getPost('verify_txt'), - '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', @@ -253,10 +249,6 @@ class PodcastController extends BaseController $this->request->getPost('other_categories') ?? [], ); - // OP3 - service('settings') - ->set('Analytics.enableOP3', $this->request->getPost('enable_op3') === 'yes', 'podcast:' . $newPodcastId); - $db->transComplete(); return redirect()->route('podcast-view', [$newPodcastId])->with( @@ -314,19 +306,11 @@ class PodcastController extends BaseController $this->podcast->publisher = $this->request->getPost('publisher'); $this->podcast->owner_name = $this->request->getPost('owner_name'); $this->podcast->owner_email = $this->request->getPost('owner_email'); - $this->podcast->is_owner_email_removed_from_feed = $this->request->getPost( - 'is_owner_email_removed_from_feed' - ) === 'yes'; $this->podcast->type = $this->request->getPost('type'); - $this->podcast->medium = $this->request->getPost('medium'); $this->podcast->copyright = $this->request->getPost('copyright'); $this->podcast->location = $this->request->getPost('location_name') === '' ? null : new Location( $this->request->getPost('location_name') ); - $this->podcast->verify_txt = $this->request->getPost('verify_txt') === '' ? null : $this->request->getPost( - 'verify_txt' - ); - $this->podcast->custom_rss_string = $this->request->getPost('custom_rss'); $this->podcast->new_feed_url = $this->request->getPost('new_feed_url') === '' ? null : $this->request->getPost( 'new_feed_url' ); @@ -359,14 +343,6 @@ class PodcastController extends BaseController $this->request->getPost('other_categories') ?? [], ); - // enable/disable OP3? - service('settings') - ->set( - 'Analytics.enableOP3', - $this->request->getPost('enable_op3') === 'yes', - 'podcast:' . $this->podcast->id - ); - $db->transComplete(); return redirect()->route('podcast-edit', [$this->podcast->id])->with( @@ -375,53 +351,6 @@ class PodcastController extends BaseController ); } - public function monetizationOther(): string - { - helper('form'); - - $data = [ - 'podcast' => $this->podcast, - ]; - - replace_breadcrumb_params([ - 0 => $this->podcast->at_handle, - ]); - return view('podcast/monetization_other', $data); - } - - public function monetizationOtherAction(): RedirectResponse - { - if ( - ($partnerId = $this->request->getPost('partner_id')) === '' || - ($partnerLinkUrl = $this->request->getPost('partner_link_url')) === '' || - ($partnerImageUrl = $this->request->getPost('partner_image_url')) === '') { - $partnerId = null; - $partnerLinkUrl = null; - $partnerImageUrl = null; - } - - $this->podcast->payment_pointer = $this->request->getPost( - 'payment_pointer' - ) === '' ? null : $this->request->getPost('payment_pointer'); - - $this->podcast->partner_id = $partnerId; - $this->podcast->partner_link_url = $partnerLinkUrl; - $this->podcast->partner_image_url = $partnerImageUrl; - - $podcastModel = new PodcastModel(); - if (! $podcastModel->update($this->podcast->id, $this->podcast)) { - return redirect() - ->back() - ->withInput() - ->with('errors', $podcastModel->errors()); - } - - return redirect()->route('podcast-monetization-other', [$this->podcast->id])->with( - 'message', - lang('Podcast.messages.editSuccess') - ); - } - public function deleteBanner(): RedirectResponse { if (! $this->podcast->banner instanceof Image) { diff --git a/modules/Analytics/Config/Analytics.php b/modules/Analytics/Config/Analytics.php index 8eca95d3..ee70666e 100644 --- a/modules/Analytics/Config/Analytics.php +++ b/modules/Analytics/Config/Analytics.php @@ -37,17 +37,4 @@ class Analytics extends BaseConfig * Z&|qECKBrwgaaD>~;U/tXG1U%tSe_oi5Tzy)h>}5NC2npSrjvM0w_Q>cs=0o=H]* */ public string $salt = ''; - - /** - * -------------------------------------------------------------------------- - * The Open Podcast Prefix Project Config - * -------------------------------------------------------------------------- - * - * @var array - */ - public array $OP3 = [ - 'host' => 'https://op3.dev/', - ]; - - public bool $enableOP3 = false; } diff --git a/modules/Analytics/OP3.php b/modules/Analytics/OP3.php deleted file mode 100644 index 76eaa6e0..00000000 --- a/modules/Analytics/OP3.php +++ /dev/null @@ -1,34 +0,0 @@ - $config - */ - public function __construct(array $config) - { - $this->host = rtrim($config['host'], '/'); - } - - public function wrap(string $audioURL, Episode $episode): string - { - // remove scheme from audioURI if https - $audioURIWithoutHTTPS = preg_replace('(^https://)', '', $audioURL); - - return $this->host . '/e,pg=' . $episode->podcast->guid . '/' . $audioURIWithoutHTTPS; - } -} diff --git a/modules/Api/Rest/V1/Controllers/EpisodeController.php b/modules/Api/Rest/V1/Controllers/EpisodeController.php index 104d0318..342b81d7 100644 --- a/modules/Api/Rest/V1/Controllers/EpisodeController.php +++ b/modules/Api/Rest/V1/Controllers/EpisodeController.php @@ -72,7 +72,6 @@ class EpisodeController extends Controller { $episode->cover_url = $episode->getCover() ->file_url; - $episode->audio_url = $episode->getAudioUrl(); $episode->duration = round($episode->audio->duration); return $episode; diff --git a/modules/Plugins/Controllers/PluginController.php b/modules/Plugins/Controllers/PluginController.php index 4dfc785e..658d0495 100644 --- a/modules/Plugins/Controllers/PluginController.php +++ b/modules/Plugins/Controllers/PluginController.php @@ -16,6 +16,7 @@ use Modules\Admin\Controllers\BaseController; use Modules\Plugins\Core\BasePlugin; use Modules\Plugins\Core\Markdown; use Modules\Plugins\Core\Plugins; +use Modules\Plugins\Core\RSS; use Modules\Plugins\Manifest\Field; class PluginController extends BaseController @@ -330,6 +331,7 @@ class PluginController extends BaseController $this->request->getPost('client_timezone') )->setTimezone(app_timezone()), 'markdown' => new Markdown($value), + 'rss' => new RSS($value), default => $value, }; } diff --git a/modules/Plugins/Core/BasePlugin.php b/modules/Plugins/Core/BasePlugin.php index 6c86be84..9df37dc3 100644 --- a/modules/Plugins/Core/BasePlugin.php +++ b/modules/Plugins/Core/BasePlugin.php @@ -6,7 +6,7 @@ namespace Modules\Plugins\Core; use App\Entities\Episode; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use CodeIgniter\HTTP\URI; use League\CommonMark\Environment\Environment; use League\CommonMark\Event\DocumentParsedEvent; @@ -86,7 +86,7 @@ abstract class BasePlugin implements PluginInterface } #[Override] - public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void + public function rssAfterChannel(Podcast $podcast, RssFeed $channel): void { } @@ -96,7 +96,7 @@ abstract class BasePlugin implements PluginInterface } #[Override] - public function rssAfterItem(Episode $episode, SimpleRSSElement $item): void + public function rssAfterItem(Episode $episode, RssFeed $item): void { } diff --git a/modules/Plugins/Core/PluginInterface.php b/modules/Plugins/Core/PluginInterface.php index 586f6d42..c8b4f73e 100644 --- a/modules/Plugins/Core/PluginInterface.php +++ b/modules/Plugins/Core/PluginInterface.php @@ -6,17 +6,17 @@ namespace Modules\Plugins\Core; use App\Entities\Episode; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; interface PluginInterface { public function rssBeforeChannel(Podcast $podcast): void; - public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void; + public function rssAfterChannel(Podcast $podcast, RssFeed $channel): void; public function rssBeforeItem(Episode $episode): void; - public function rssAfterItem(Episode $episode, SimpleRSSElement $item): void; + public function rssAfterItem(Episode $episode, RssFeed $item): void; public function siteHead(): void; } diff --git a/modules/Plugins/Core/Plugins.php b/modules/Plugins/Core/Plugins.php index 7208a437..ab24a87f 100644 --- a/modules/Plugins/Core/Plugins.php +++ b/modules/Plugins/Core/Plugins.php @@ -6,15 +6,15 @@ namespace Modules\Plugins\Core; use App\Entities\Episode; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use Config\Database; use Modules\Plugins\Config\Plugins as PluginsConfig; /** * @method void rssBeforeChannel(Podcast $podcast) - * @method void rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel) + * @method void rssAfterChannel(Podcast $podcast, RssFeed $channel) * @method void rssBeforeItem(Episode $episode) - * @method void rssAfterItem(Episode $episode, SimpleRSSElement $item) + * @method void rssAfterItem(Episode $episode, RssFeed $item) * @method void siteHead() */ class Plugins @@ -28,25 +28,27 @@ class Plugins 'checkbox' => ['permit_empty'], 'datetime' => ['valid_date[Y-m-d H:i]'], 'email' => ['valid_email'], + 'group' => ['permit_empty', 'is_list'], 'markdown' => ['string'], 'number' => ['integer'], 'radio-group' => ['string'], + 'rss' => ['string'], 'select' => ['string'], 'select-multiple' => ['permit_empty', 'is_list'], 'text' => ['string'], 'textarea' => ['string'], 'toggler' => ['permit_empty'], 'url' => ['valid_url_strict'], - 'group' => ['permit_empty', 'is_list'], ]; public const FIELDS_CASTS = [ 'checkbox' => 'bool', 'datetime' => 'datetime', + 'markdown' => 'markdown', 'number' => 'int', + 'rss' => 'rss', 'toggler' => 'bool', 'url' => 'uri', - 'markdown' => 'markdown', ]; /** diff --git a/modules/Plugins/Core/RSS.php b/modules/Plugins/Core/RSS.php new file mode 100644 index 00000000..e7332afa --- /dev/null +++ b/modules/Plugins/Core/RSS.php @@ -0,0 +1,43 @@ +rss; + } + + /** + * @return ?RssFeed[] + */ + public function toSimpleRSS(): ?array + { + try { + $rssFeed = new RssFeed("{$this->rss}"); + } catch (Exception) { + return null; + } + + return [ + ...$rssFeed->children(), + ...$rssFeed->children(RssFeed::ATOM_NS, true), + ...$rssFeed->children(RssFeed::ITUNES_NS, true), + ...$rssFeed->children(RssFeed::PODCAST_NS, true), + ]; + } +} diff --git a/modules/Plugins/Manifest/Field.php b/modules/Plugins/Manifest/Field.php index 53e601ec..4ccb735f 100644 --- a/modules/Plugins/Manifest/Field.php +++ b/modules/Plugins/Manifest/Field.php @@ -7,7 +7,7 @@ namespace Modules\Plugins\Manifest; use Override; /** - * @property 'checkbox'|'datetime'|'email'|'markdown'|'number'|'radio-group'|'select-multiple'|'select'|'text'|'textarea'|'toggler'|'url'|'group' $type + * @property 'checkbox'|'datetime'|'email'|'group'|'markdown'|'number'|'radio-group'|'rss'|'select-multiple'|'select'|'text'|'textarea'|'toggler'|'url' $type * @property string $key * @property string $label * @property string $hint @@ -20,7 +20,7 @@ use Override; class Field extends ManifestObject { protected const VALIDATION_RULES = [ - 'type' => 'permit_empty|in_list[checkbox,datetime,email,markdown,number,radio-group,select-multiple,select,text,textarea,toggler,url,group]', + 'type' => 'permit_empty|in_list[checkbox,datetime,email,group,markdown,number,radio-group,rss,select-multiple,select,text,textarea,toggler,url]', 'key' => 'required|alpha_dash', 'label' => 'required|string', 'hint' => 'permit_empty|string', diff --git a/modules/Plugins/Manifest/Repository.php b/modules/Plugins/Manifest/Repository.php index 7ed718ac..b439bcd2 100644 --- a/modules/Plugins/Manifest/Repository.php +++ b/modules/Plugins/Manifest/Repository.php @@ -14,7 +14,7 @@ use CodeIgniter\HTTP\URI; class Repository extends ManifestObject { protected const VALIDATION_RULES = [ - 'type' => 'required|in_list[git]', + 'type' => 'permit_empty|in_list[git]', 'url' => 'required|valid_url_strict', 'directory' => 'permit_empty', ]; @@ -26,7 +26,7 @@ class Repository extends ManifestObject 'url' => URI::class, ]; - protected string $type; + protected string $type = 'git'; protected URI $url; diff --git a/modules/Plugins/Manifest/manifest.schema.json b/modules/Plugins/Manifest/manifest.schema.json index e76a4ab4..f08cb602 100644 --- a/modules/Plugins/Manifest/manifest.schema.json +++ b/modules/Plugins/Manifest/manifest.schema.json @@ -76,6 +76,7 @@ "monetization", "podcasting2", "privacy", + "productivity", "seo" ] } @@ -173,13 +174,14 @@ "properties": { "type": { "enum": [ - "group", "checkbox", "datetime", "email", + "group", "markdown", "number", "radio-group", + "rss", "select-multiple", "select", "text", diff --git a/tests/_support/Database/Seeds/FakeSinglePodcastApiSeeder.php b/tests/_support/Database/Seeds/FakeSinglePodcastApiSeeder.php index 27906d76..3fbd17c4 100644 --- a/tests/_support/Database/Seeds/FakeSinglePodcastApiSeeder.php +++ b/tests/_support/Database/Seeds/FakeSinglePodcastApiSeeder.php @@ -104,53 +104,46 @@ class FakeSinglePodcastApiSeeder extends Seeder } /** - * @return array{id: int, guid: string, actor_id: int, handle: string, title: string, description_markdown: string, description_html: string, cover_id: int, banner_id: int, language_code: string, category_id: int, parental_advisory: null, owner_name: string, owner_email: string, publisher: string, type: string, copyright: string, episode_description_footer_markdown: null, episode_description_footer_html: null, is_blocked: int, is_completed: int, is_locked: int, imported_feed_url: null, new_feed_url: null, payment_pointer: null, location_name: null, location_geo: null, location_osm: null, custom_rss: null, is_published_on_hubs: int, partner_id: null, partner_link_url: null, partner_image_url: null, created_by: int, updated_by: int, created_at: string, updated_at: string} + * @return array{id: int, guid: string, actor_id: int, handle: string, title: string, description_markdown: string, description_html: string, cover_id: int, banner_id: int, language_code: string, category_id: int, parental_advisory: null, owner_name: string, owner_email: string, publisher: string, type: string, copyright: string, is_blocked: int, is_completed: int, is_locked: int, imported_feed_url: null, new_feed_url: null, location_name: null, location_geo: null, location_osm: null, is_published_on_hubs: int, created_by: int, updated_by: int, created_at: string, updated_at: string} */ public static function podcast(): array { return [ - 'id' => 1, - 'guid' => '0d341200-0234-5de7-99a6-a7d02bea4ce2', - 'actor_id' => 1, - 'handle' => 'Handle', - 'title' => 'Title', - 'description_markdown' => 'description', - 'description_html' => '

description

', - 'cover_id' => 1, - 'banner_id' => 2, - 'language_code' => 'en', - 'category_id' => 1, - 'parental_advisory' => null, - 'owner_name' => 'Owner', - 'owner_email' => 'Owner@gmail.com', - 'publisher' => '', - 'type' => 'episodic', - 'copyright' => '', - 'episode_description_footer_markdown' => null, - 'episode_description_footer_html' => null, - 'is_blocked' => 0, - 'is_completed' => 0, - 'is_locked' => 1, - 'imported_feed_url' => null, - 'new_feed_url' => null, - 'payment_pointer' => null, - 'location_name' => null, - 'location_geo' => null, - 'location_osm' => null, - 'custom_rss' => null, - 'is_published_on_hubs' => 0, - 'partner_id' => null, - 'partner_link_url' => null, - 'partner_image_url' => null, - 'created_by' => 1, - 'updated_by' => 1, - 'created_at' => '2022-06-13 8:00:00', - 'updated_at' => '2022-06-13 8:00:00', + 'id' => 1, + 'guid' => '0d341200-0234-5de7-99a6-a7d02bea4ce2', + 'actor_id' => 1, + 'handle' => 'Handle', + 'title' => 'Title', + 'description_markdown' => 'description', + 'description_html' => '

description

', + 'cover_id' => 1, + 'banner_id' => 2, + 'language_code' => 'en', + 'category_id' => 1, + 'parental_advisory' => null, + 'owner_name' => 'Owner', + 'owner_email' => 'Owner@gmail.com', + 'publisher' => '', + 'type' => 'episodic', + 'copyright' => '', + 'is_blocked' => 0, + 'is_completed' => 0, + 'is_locked' => 1, + 'imported_feed_url' => null, + 'new_feed_url' => null, + 'location_name' => null, + 'location_geo' => null, + 'location_osm' => null, + 'is_published_on_hubs' => 0, + 'created_by' => 1, + 'updated_by' => 1, + 'created_at' => '2022-06-13 8:00:00', + 'updated_at' => '2022-06-13 8:00:00', ]; } /** - * @return array{id: int, podcast_id: int, guid: string, title: string, slug: string, audio_id: int, description_markdown: string, description_html: string, cover_id: int, transcript_id: null, transcript_remote_url: null, chapters_id: null, chapters_remote_url: null, parental_advisory: null, number: int, season_number: null, type: string, is_blocked: false, location_name: null, location_geo: null, location_osm: null, custom_rss: null, is_published_on_hubs: false, posts_count: int, comments_count: int, is_premium: false, created_by: int, updated_by: int, published_at: null, created_at: string, updated_at: string} + * @return array{id:int,podcast_id:int,guid:string,title:string,slug:string,audio_id:int,description_markdown:string,description_html:string,cover_id:int,transcript_id:null,transcript_remote_url:null,chapters_id:null,chapters_remote_url:null,parental_advisory:null,number:int,season_number:null,type:string,is_blocked:false,location_name:null,location_geo:null,location_osm:null,is_published_on_hubs:false,posts_count:int,comments_count:int,is_premium:false,created_by:int,updated_by:int,published_at:null,created_at:string,updated_at:string} */ public static function episode(): array { @@ -176,7 +169,6 @@ class FakeSinglePodcastApiSeeder extends Seeder 'location_name' => null, 'location_geo' => null, 'location_osm' => null, - 'custom_rss' => null, 'is_published_on_hubs' => false, 'posts_count' => 0, 'comments_count' => 0, diff --git a/tests/modules/Plugins/PluginsTest.php b/tests/modules/Plugins/PluginsTest.php index ce872082..b93c72d2 100644 --- a/tests/modules/Plugins/PluginsTest.php +++ b/tests/modules/Plugins/PluginsTest.php @@ -6,7 +6,7 @@ namespace Tests\Modules\Plugins; use App\Entities\Episode; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use CodeIgniter\Test\CIUnitTestCase; use CodeIgniter\Test\DatabaseTestTrait; use Modules\Plugins\Config\Plugins as PluginsConfig; @@ -96,7 +96,7 @@ final class PluginsTest extends CIUnitTestCase self::$plugins->runHook('rssBeforeChannel', [$podcast]); $this->assertEquals('Podcast test', $podcast->title); - $channel = new SimpleRSSElement(''); + $channel = new RssFeed(''); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertFalse(empty($channel->foo)); @@ -106,7 +106,7 @@ final class PluginsTest extends CIUnitTestCase self::$plugins->runHook('rssBeforeItem', [$episode]); $this->assertEquals('Episode test', $episode->title); - $item = new SimpleRSSElement(''); + $item = new RssFeed(''); $this->assertTrue(empty($item->efoo)); self::$plugins->runHook('rssAfterItem', [$episode, $item]); $this->assertFalse(empty($item->efoo)); @@ -133,7 +133,7 @@ final class PluginsTest extends CIUnitTestCase self::$plugins->runHook('rssBeforeChannel', [$podcast]); $this->assertEquals('', $podcast->title); - $channel = new SimpleRSSElement(''); + $channel = new RssFeed(''); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertTrue(empty($channel->foo)); @@ -143,7 +143,7 @@ final class PluginsTest extends CIUnitTestCase self::$plugins->runHook('rssBeforeItem', [$episode]); $this->assertEquals('', $episode->title); - $item = new SimpleRSSElement(''); + $item = new RssFeed(''); $this->assertTrue(empty($item->efoo)); self::$plugins->runHook('rssAfterItem', [$episode, $item]); $this->assertTrue(empty($item->efoo)); @@ -167,7 +167,7 @@ final class PluginsTest extends CIUnitTestCase $this->assertEquals('Podcast test undeclared', $podcast->title); // rssAfterChannel has not been declared in plugin manifest, should not be running - $channel = new SimpleRSSElement(''); + $channel = new RssFeed(''); $this->assertTrue(empty($channel->foo)); self::$plugins->runHook('rssAfterChannel', [$podcast, $channel]); $this->assertTrue(empty($channel->foo)); diff --git a/tests/modules/Plugins/mocks/plugins/acme/all-hooks/Plugin.php b/tests/modules/Plugins/mocks/plugins/acme/all-hooks/Plugin.php index 42489796..249977f7 100644 --- a/tests/modules/Plugins/mocks/plugins/acme/all-hooks/Plugin.php +++ b/tests/modules/Plugins/mocks/plugins/acme/all-hooks/Plugin.php @@ -4,7 +4,7 @@ declare(strict_types=1); use App\Entities\Episode; use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use Modules\Plugins\Core\BasePlugin; class AcmeAllHooksPlugin extends BasePlugin @@ -16,7 +16,7 @@ class AcmeAllHooksPlugin extends BasePlugin } #[Override] - public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void + public function rssAfterChannel(Podcast $podcast, RssFeed $channel): void { $channel->addChild('foo', 'bar'); } @@ -28,7 +28,7 @@ class AcmeAllHooksPlugin extends BasePlugin } #[Override] - public function rssAfterItem(Episode $episode, SimpleRSSElement $item): void + public function rssAfterItem(Episode $episode, RssFeed $item): void { $item->addChild('efoo', 'ebar'); } diff --git a/tests/modules/Plugins/mocks/plugins/acme/undeclared-hook/Plugin.php b/tests/modules/Plugins/mocks/plugins/acme/undeclared-hook/Plugin.php index 830282f6..59ac9f6a 100644 --- a/tests/modules/Plugins/mocks/plugins/acme/undeclared-hook/Plugin.php +++ b/tests/modules/Plugins/mocks/plugins/acme/undeclared-hook/Plugin.php @@ -3,7 +3,7 @@ declare(strict_types=1); use App\Entities\Podcast; -use App\Libraries\SimpleRSSElement; +use App\Libraries\RssFeed; use Modules\Plugins\Core\BasePlugin; class AcmeUndeclaredHookPlugin extends BasePlugin @@ -15,7 +15,7 @@ class AcmeUndeclaredHookPlugin extends BasePlugin } #[Override] - public function rssAfterChannel(Podcast $podcast, SimpleRSSElement $channel): void + public function rssAfterChannel(Podcast $podcast, RssFeed $channel): void { $channel->addChild('foo', 'bar'); } diff --git a/themes/cp_admin/episode/_sidebar.php b/themes/cp_admin/episode/_sidebar.php index 1852c7d4..b9b8f7cc 100644 --- a/themes/cp_admin/episode/_sidebar.php +++ b/themes/cp_admin/episode/_sidebar.php @@ -11,6 +11,12 @@ $episodeNavigation = [ 'embed-add' => 'episodes.edit', ], ], + 'plugins' => [ + 'icon' => 'puzzle-fill', // @icon('puzzle-fill') + 'items' => [], + 'items-labels' => [], + 'items-permissions' => [], + ], 'clips' => [ 'icon' => 'clapperboard-fill', // @icon('clapperboard-fill') 'items' => ['video-clips-list', 'video-clips-create', 'soundbites-list', 'soundbites-create'], @@ -24,12 +30,6 @@ $episodeNavigation = [ 'count-route' => 'video-clips-list', 'add-cta' => 'video-clips-create', ], - 'plugins' => [ - 'icon' => 'puzzle-fill', // @icon('puzzle-fill') - 'items' => [], - 'items-labels' => [], - 'items-permissions' => [], - ], ]; foreach (plugins()->getPluginsWithEpisodeSettings() as $plugin) { diff --git a/themes/cp_admin/episode/create.php b/themes/cp_admin/episode/create.php index f9fd9def..a63d5a95 100644 --- a/themes/cp_admin/episode/create.php +++ b/themes/cp_admin/episode/create.php @@ -127,14 +127,6 @@ isRequired="true" disallowList="header,quote" /> - - @@ -211,12 +203,6 @@ title="" subtitle="" > - diff --git a/themes/cp_admin/episode/edit.php b/themes/cp_admin/episode/edit.php index 01d8e039..701227ba 100644 --- a/themes/cp_admin/episode/edit.php +++ b/themes/cp_admin/episode/edit.php @@ -132,14 +132,6 @@ isRequired="true" disallowList="header,quote" /> - - @@ -279,13 +271,6 @@ title="" subtitle="" > - diff --git a/themes/cp_admin/plugins/_field.php b/themes/cp_admin/plugins/_field.php index 2ea528bf..f236e58c 100644 --- a/themes/cp_admin/plugins/_field.php +++ b/themes/cp_admin/plugins/_field.php @@ -118,6 +118,18 @@ case 'markdown': ?> value="" /> + + [ - 'subscription-list' => 'manage-subscriptions', - 'subscription-create' => 'manage-subscriptions', - 'platforms-funding' => 'manage-platforms', - 'podcast-monetization-other' => 'edit', + 'subscription-list' => 'manage-subscriptions', + 'subscription-create' => 'manage-subscriptions', + 'platforms-funding' => 'manage-platforms', ], ], 'contributors' => [ @@ -135,15 +133,6 @@ foreach (plugins()->getPluginsWithPodcastSettings() as $plugin) { 'class' => 'text-sm opacity-60', ]) ?> - is_op3_enabled): ?> - - 'text-xl text-white inline-flex items-center justify-center rounded', - ]) . 'OP3' . icon('external-link-fill', [ - 'class' => 'text-sm opacity-60', - ]) ?> - - diff --git a/themes/cp_admin/podcast/create.php b/themes/cp_admin/podcast/create.php index a5b90d88..84b10c38 100644 --- a/themes/cp_admin/podcast/create.php +++ b/themes/cp_admin/podcast/create.php @@ -58,29 +58,6 @@ ])) ?>" isRequired="true" /> - - - - - - - - 'text-sm', - ]) ?>op3.dev - - - @@ -212,21 +176,6 @@ - - - - diff --git a/themes/cp_admin/podcast/edit.php b/themes/cp_admin/podcast/edit.php index 9ea9ea13..65cc952d 100644 --- a/themes/cp_admin/podcast/edit.php +++ b/themes/cp_admin/podcast/edit.php @@ -83,29 +83,6 @@ isRequired="true" /> - - - - - - - 'text-sm', - ]) ?>op3.dev - - - @@ -248,23 +211,6 @@ title="" subtitle="" > - - - - extend('_layout') ?> - -section('title') ?> - -endSection() ?> - -section('pageTitle') ?> - -endSection() ?> - -section('content') ?> - -
- - - - - -
- -
-
- - -
-
- - -
-
-
- - -
-
-
- - - -
-endSection() ?> diff --git a/themes/cp_app/episode/_layout-preview.php b/themes/cp_app/episode/_layout-preview.php index c0c0e44a..efbbe471 100644 --- a/themes/cp_app/episode/_layout-preview.php +++ b/themes/cp_app/episode/_layout-preview.php @@ -148,9 +148,9 @@

description_markdown, "\n") > 6 || strlen($episode->description) > 500): ?> - getDescriptionHtml('-+Website+-') ?> + description_html ?> -
getDescriptionHtml('-+Website+-') ?>
+
description_html ?>
include('episode/_partials/navigation') ?> diff --git a/themes/cp_app/episode/_layout.php b/themes/cp_app/episode/_layout.php index 359c3304..a74df63b 100644 --- a/themes/cp_app/episode/_layout.php +++ b/themes/cp_app/episode/_layout.php @@ -141,9 +141,9 @@

description_markdown, "\n") > 6 || strlen($episode->description) > 500): ?> - getDescriptionHtml('-+Website+-') ?> + description_html ?> -
getDescriptionHtml('-+Website+-') ?>
+
description_html ?>
include('episode/_partials/navigation') ?>