From 4a8147bfbbd98d9badfc57a0f2a18bdd5812e802 Mon Sep 17 00:00:00 2001 From: Yassine Doghri Date: Mon, 1 Nov 2021 17:12:03 +0000 Subject: [PATCH] feat: add podcast banner field for each podcast + refactor images configuration - rename image fields on podcast, episode and persons for better clarity - set different sizes config for podcast cover, banner and persons avatars - add tiny size for covers - fix responsive on admin forms --- app/Config/Images.php | 77 ++++--- app/Controllers/CreditsController.php | 4 +- app/Controllers/EpisodeController.php | 12 +- app/Controllers/MapMarkerController.php | 2 +- .../2020-05-30-101500_add_podcasts.php | 16 +- .../2020-06-05-170000_add_episodes.php | 4 +- .../2020-12-25-120000_add_persons.php | 4 +- app/Entities/Episode.php | 37 ++-- app/Entities/Image.php | 204 ++++++------------ app/Entities/Person.php | 31 ++- app/Entities/Podcast.php | 74 +++++-- app/Helpers/id3_helper.php | 2 +- app/Helpers/rss_helper.php | 11 +- app/Libraries/PodcastEpisode.php | 4 +- app/Models/EpisodeModel.php | 4 +- app/Models/PersonModel.php | 4 +- app/Models/PodcastModel.php | 40 ++-- app/Resources/js/modules/EpisodesMap.ts | 2 +- app/Resources/styles/custom.css | 15 ++ app/Validation/FileRules.php | 16 +- app/Views/Components/Forms/Input.php | 2 +- app/Views/Components/Forms/MarkdownEditor.php | 2 +- app/Views/Components/Forms/Textarea.php | 2 +- app/Views/Components/Forms/Toggler.php | 2 +- modules/Admin/Config/Routes.php | 4 + .../Admin/Controllers/EpisodeController.php | 26 ++- .../Admin/Controllers/PersonController.php | 20 +- .../Admin/Controllers/PodcastController.php | 49 ++++- .../Controllers/PodcastImportController.php | 17 +- .../Admin/Controllers/SettingsController.php | 2 +- modules/Admin/Language/en/Common.php | 2 - modules/Admin/Language/en/Episode.php | 8 +- modules/Admin/Language/en/Person.php | 6 +- modules/Admin/Language/en/Podcast.php | 6 +- modules/Admin/Language/en/Validation.php | 4 +- modules/Admin/Language/fr/Common.php | 2 - modules/Admin/Language/fr/Episode.php | 6 +- modules/Admin/Language/fr/Person.php | 4 +- modules/Admin/Language/fr/Podcast.php | 6 +- modules/Admin/Language/fr/Settings.php | 2 +- modules/Admin/Language/fr/Validation.php | 4 +- phpstan.neon | 1 + ...efault.jpg => castopod-banner-default.jpg} | Bin .../media/castopod-banner-default_large.jpg | Bin 0 -> 15615 bytes .../media/castopod-banner-default_medium.jpg | Bin 0 -> 6919 bytes .../media/castopod-banner-default_small.jpg | Bin 0 -> 1748 bytes themes/cp_admin/_partials/_nav_header.php | 2 +- themes/cp_admin/episode/_card.php | 2 +- themes/cp_admin/episode/_sidebar.php | 4 +- themes/cp_admin/episode/create.php | 8 +- themes/cp_admin/episode/edit.php | 8 +- themes/cp_admin/episode/list.php | 2 +- themes/cp_admin/episode/persons.php | 2 +- themes/cp_admin/episode/publish.php | 2 +- themes/cp_admin/episode/publish_edit.php | 2 +- themes/cp_admin/person/_card.php | 2 +- themes/cp_admin/person/create.php | 6 +- themes/cp_admin/person/edit.php | 6 +- themes/cp_admin/person/view.php | 2 +- themes/cp_admin/podcast/_card.php | 2 +- themes/cp_admin/podcast/_sidebar.php | 2 +- themes/cp_admin/podcast/create.php | 22 +- themes/cp_admin/podcast/edit.php | 42 +++- themes/cp_admin/podcast/latest_episodes.php | 4 +- themes/cp_admin/podcast/persons.php | 2 +- themes/cp_admin/settings/general.php | 2 +- themes/cp_app/_admin_navbar.php | 2 +- themes/cp_app/_persons_modal.php | 2 +- themes/cp_app/embed.php | 2 +- themes/cp_app/episode/_layout.php | 10 +- themes/cp_app/episode/_partials/card.php | 4 +- .../cp_app/episode/_partials/preview_card.php | 4 +- themes/cp_app/episode/activity.php | 9 +- themes/cp_app/episode/comments.php | 9 +- themes/cp_app/home.php | 2 +- themes/cp_app/podcast/_layout.php | 9 +- themes/cp_app/podcast/about.php | 10 +- themes/cp_app/podcast/activity.php | 8 +- themes/cp_app/podcast/episodes.php | 8 +- 79 files changed, 515 insertions(+), 428 deletions(-) rename public/media/{castopod-cover-default.jpg => castopod-banner-default.jpg} (100%) create mode 100644 public/media/castopod-banner-default_large.jpg create mode 100644 public/media/castopod-banner-default_medium.jpg create mode 100644 public/media/castopod-banner-default_small.jpg diff --git a/app/Config/Images.php b/app/Config/Images.php index debc98f0..680a1950 100644 --- a/app/Config/Images.php +++ b/app/Config/Images.php @@ -32,42 +32,63 @@ class Images extends BaseConfig /* |-------------------------------------------------------------------------- - | Uploaded images resizing sizes (in px) + | Uploaded images sizes (in px) |-------------------------------------------------------------------------- | The sizes listed below determine the resizing of images when uploaded. - | All uploaded images are of 1:1 ratio (width and height are the same). */ - public int $thumbnailSize = 150; - - public int $mediumSize = 320; - - public int $largeSize = 1024; + /** + * Podcast cover image sizes + * + * Uploaded podcast covers are of 1:1 ratio (width and height are the same). + * + * Size of images linked in the rss feed (should be between 1400 and 3000). Size for ID3 tag cover art (should be + * between 300 and 800) + * + * Array values are as follows: 'name' => [width, height] + * + * @var array + */ + public array $podcastCoverSizes = [ + 'tiny' => [40, 40], + 'thumbnail' => [150, 150], + 'medium' => [320, 320], + 'large' => [1024, 1024], + 'feed' => [1400, 1400], + 'id3' => [500, 500], + ]; /** - * Size of images linked in the rss feed (should be between 1400 and 3000) + * Podcast header cover image + * + * Uploaded podcast header covers are of 3:1 ratio + * + * Array values are as follows: 'name' => [width, height] + * + * @var array */ - public int $feedSize = 1400; + public array $podcastBannerSizes = [ + 'small' => [320, 128], + 'medium' => [960, 320], + 'large' => [1500, 500], + ]; + + public string $podcastBannerDefaultPath = 'castopod-banner-default.jpg'; + + public string $podcastBannerDefaultMimeType = 'image/jpeg'; /** - * Size for ID3 tag cover art (should be between 300 and 800) + * Person image + * + * Uploaded person images are of 1:1 ratio (width and height are the same). + * + * Array values are as follows: 'name' => [width, height] + * + * @var array */ - public int $id3Size = 500; - - /* - |-------------------------------------------------------------------------- - | Uploaded images naming extensions - |-------------------------------------------------------------------------- - | The properties listed below set the name extensions for the resized images - */ - - public string $thumbnailSuffix = '_thumbnail'; - - public string $mediumSuffix = '_medium'; - - public string $largeSuffix = '_large'; - - public string $feedSuffix = '_feed'; - - public string $id3Suffix = '_id3'; + public array $personAvatarSizes = [ + 'tiny' => [40, 40], + 'thumbnail' => [150, 150], + 'medium' => [320, 320], + ]; } diff --git a/app/Controllers/CreditsController.php b/app/Controllers/CreditsController.php index 8c1b5418..816653dd 100644 --- a/app/Controllers/CreditsController.php +++ b/app/Controllers/CreditsController.php @@ -48,7 +48,7 @@ class CreditsController extends BaseController $personId => [ 'full_name' => $credit->person->full_name, 'thumbnail_url' => - $credit->person->image->thumbnail_url, + $credit->person->avatar->thumbnail_url, 'information_url' => $credit->person->information_url, 'roles' => [ @@ -87,7 +87,7 @@ class CreditsController extends BaseController $credits[$personGroup]['persons'][$personId] = [ 'full_name' => $credit->person->full_name, 'thumbnail_url' => - $credit->person->image->thumbnail_url, + $credit->person->avatar->thumbnail_url, 'information_url' => $credit->person->information_url, 'roles' => [ $personRole => [ diff --git a/app/Controllers/EpisodeController.php b/app/Controllers/EpisodeController.php index 0b439a61..3a59f91a 100644 --- a/app/Controllers/EpisodeController.php +++ b/app/Controllers/EpisodeController.php @@ -200,11 +200,11 @@ class EpisodeController extends BaseController '" width="100%" height="144" frameborder="0" scrolling="no">', 'width' => 600, 'height' => 144, - 'thumbnail_url' => $this->episode->image->large_url, + 'thumbnail_url' => $this->episode->cover->large_url, 'thumbnail_width' => config('Images') - ->largeSize, + ->podcastCoverSizes['large'][0], 'thumbnail_height' => config('Images') - ->largeSize, + ->podcastCoverSizes['large'][1], ]); } @@ -219,9 +219,9 @@ class EpisodeController extends BaseController $oembed->addChild('provider_url', $this->podcast->link); $oembed->addChild('author_name', $this->podcast->title); $oembed->addChild('author_url', $this->podcast->link); - $oembed->addChild('thumbnail', $this->episode->image->large_url); - $oembed->addChild('thumbnail_width', config('Images')->largeSize); - $oembed->addChild('thumbnail_height', config('Images')->largeSize); + $oembed->addChild('thumbnail', $this->episode->cover->large_url); + $oembed->addChild('thumbnail_width', config('Images')->podcastCoverSizes['large'][0]); + $oembed->addChild('thumbnail_height', config('Images')->podcastCoverSizes['large'][1]); $oembed->addChild( 'html', htmlentities( diff --git a/app/Controllers/MapMarkerController.php b/app/Controllers/MapMarkerController.php index 6c6df82d..5ead9597 100644 --- a/app/Controllers/MapMarkerController.php +++ b/app/Controllers/MapMarkerController.php @@ -46,7 +46,7 @@ class MapMarkerController extends BaseController 'location_url' => $episode->location->url, 'episode_link' => $episode->link, 'podcast_link' => $episode->podcast->link, - 'image_path' => $episode->image->thumbnail_url, + 'cover_path' => $episode->cover->thumbnail_url, 'podcast_title' => $episode->podcast->title, 'episode_title' => $episode->title, ]; diff --git a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php index 9243e7f3..0663392a 100644 --- a/app/Database/Migrations/2020-05-30-101500_add_podcasts.php +++ b/app/Database/Migrations/2020-05-30-101500_add_podcasts.php @@ -46,16 +46,28 @@ class AddPodcasts extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_path' => [ + 'cover_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'cover_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, ], + 'banner_path' => [ + 'type' => 'VARCHAR', + 'constraint' => 255, + 'null' => true, + 'default' => null, + ], + 'banner_mimetype' => [ + 'type' => 'VARCHAR', + 'constraint' => 13, + 'null' => true, + 'default' => null, + ], 'language_code' => [ 'type' => 'VARCHAR', 'constraint' => 2, diff --git a/app/Database/Migrations/2020-06-05-170000_add_episodes.php b/app/Database/Migrations/2020-06-05-170000_add_episodes.php index 310b1b70..aea2a023 100644 --- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php +++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php @@ -70,14 +70,14 @@ class AddEpisodes extends Migration 'description_html' => [ 'type' => 'TEXT', ], - 'image_path' => [ + 'cover_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'cover_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, 'null' => true, diff --git a/app/Database/Migrations/2020-12-25-120000_add_persons.php b/app/Database/Migrations/2020-12-25-120000_add_persons.php index 9101ee77..58e26df3 100644 --- a/app/Database/Migrations/2020-12-25-120000_add_persons.php +++ b/app/Database/Migrations/2020-12-25-120000_add_persons.php @@ -42,14 +42,14 @@ class AddPersons extends Migration 'The url to a relevant resource of information about the person, such as a homepage or third-party profile platform.', 'null' => true, ], - 'image_path' => [ + 'avatar_path' => [ 'type' => 'VARCHAR', 'constraint' => 255, 'null' => true, ], // constraint is 13 because the longest safe mimetype for images is image/svg+xml, // see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types - 'image_mimetype' => [ + 'avatar_mimetype' => [ 'type' => 'VARCHAR', 'constraint' => 13, 'null' => true, diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 5f2689b8..fdc16d04 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -44,9 +44,9 @@ use RuntimeException; * @property string|null $description Holds text only description, striped of any markdown or html special characters * @property string $description_markdown * @property string $description_html - * @property Image $image - * @property string|null $image_path - * @property string|null $image_mimetype + * @property Image $cover + * @property string|null $cover_path + * @property string|null $cover_mimetype * @property File|null $transcript_file * @property string|null $transcript_file_url * @property string|null $transcript_file_path @@ -98,7 +98,7 @@ class Episode extends Entity protected string $embed_url; - protected Image $image; + protected Image $cover; protected ?string $description = null; @@ -153,8 +153,8 @@ class Episode extends Entity 'audio_file_header_size' => 'integer', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_path' => '?string', - 'image_mimetype' => '?string', + 'cover_path' => '?string', + 'cover_mimetype' => '?string', 'transcript_file_path' => '?string', 'transcript_file_remote_url' => '?string', 'chapters_file_path' => '?string', @@ -175,31 +175,36 @@ class Episode extends Entity ]; /** - * Saves an episode image + * Saves an episode cover */ - public function setImage(?Image $image = null): static + public function setCover(?Image $cover = null): static { - if ($image === null) { + if ($cover === null) { return $this; } // Save image - $image->saveImage('podcasts/' . $this->getPodcast()->handle, $this->attributes['slug']); + $cover->saveImage( + config('Images') + ->podcastCoverSizes, + 'podcasts/' . $this->getPodcast()->handle, + $this->attributes['slug'] + ); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['cover_mimetype'] = $cover->mimetype; + $this->attributes['cover_path'] = $cover->path; return $this; } - public function getImage(): Image + public function getCover(): Image { - if ($imagePath = $this->attributes['image_path']) { - return new Image(null, $imagePath, $this->attributes['image_mimetype']); + if ($coverPath = $this->attributes['cover_path']) { + return new Image(null, $coverPath, $this->attributes['cover_mimetype']); } return $this->getPodcast() - ->image; + ->cover; } /** diff --git a/app/Entities/Image.php b/app/Entities/Image.php index 799840c8..78740d0c 100644 --- a/app/Entities/Image.php +++ b/app/Entities/Image.php @@ -13,7 +13,6 @@ namespace App\Entities; use CodeIgniter\Entity\Entity; use CodeIgniter\Files\File; use Config\Images; -use Config\Services; use RuntimeException; /** @@ -24,16 +23,6 @@ use RuntimeException; * @property string $mimetype * @property string $path * @property string $url - * @property string $thumbnail_path - * @property string $thumbnail_url - * @property string $medium_path - * @property string $medium_url - * @property string $large_path - * @property string $large_url - * @property string $feed_path - * @property string $feed_url - * @property string $id3_path - * @property string $id3_url */ class Image extends Entity { @@ -47,14 +36,14 @@ class Image extends Entity protected string $extension; + protected string $mimetype; + public function __construct(?File $file, string $path = '', string $mimetype = '') { if ($file === null && $path === '') { throw new RuntimeException('File or path must be set to create an Image.'); } - $this->config = config('Images'); - $dirname = ''; $filename = ''; $extension = ''; @@ -81,6 +70,45 @@ class Image extends Entity $this->mimetype = $mimetype; } + public function __get($property) + { + // Convert to CamelCase for the method + $method = 'get' . str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $property))); + + // if a get* method exists for this property, + // call that method to get this value. + // @phpstan-ignore-next-line + if (method_exists($this, $method)) { + return $this->{$method}(); + } + + $fileSuffix = ''; + if ($lastUnderscorePosition = strrpos($property, '_')) { + $fileSuffix = '_' . substr($property, 0, $lastUnderscorePosition); + } + + $path = ''; + if ($this->dirname !== '.') { + $path .= $this->dirname . '/'; + } + $path .= $this->filename . $fileSuffix . '.' . $this->extension; + + if (str_ends_with($property, 'url')) { + helper('media'); + + return media_base_url($path); + } + + if (str_ends_with($property, 'path')) { + return $path; + } + } + + public function getMimetype(): string + { + return $this->mimetype; + } + public function getFile(): File { if ($this->file === null) { @@ -90,104 +118,10 @@ class Image extends Entity return $this->file; } - public function getPath(): string - { - return $this->dirname . '/' . $this->filename . '.' . $this->extension; - } - - public function getUrl(): string - { - helper('media'); - - return media_base_url($this->path); - } - - public function getThumbnailPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->thumbnailSuffix . - '.' . - $this->extension; - } - - public function getThumbnailUrl(): string - { - helper('media'); - - return media_base_url($this->thumbnail_path); - } - - public function getMediumPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->mediumSuffix . - '.' . - $this->extension; - } - - public function getMediumUrl(): string - { - helper('media'); - - return media_base_url($this->medium_path); - } - - public function getLargePath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->largeSuffix . - '.' . - $this->extension; - } - - public function getLargeUrl(): string - { - helper('media'); - - return media_base_url($this->large_path); - } - - public function getFeedPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->feedSuffix . - '.' . - $this->extension; - } - - public function getFeedUrl(): string - { - helper('media'); - - return media_base_url($this->feed_path); - } - - public function getId3Path(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->id3Suffix . - '.' . - $this->extension; - } - - public function getId3Url(): string - { - helper('media'); - - return media_base_url($this->id3_path); - } - - public function saveImage(string $dirname, string $filename): void + /** + * @param array $sizes + */ + public function saveImage(array $sizes, string $dirname, string $filename): void { helper('media'); @@ -196,37 +130,27 @@ class Image extends Entity save_media($this->file, $this->dirname, $this->filename); - $imageService = Services::image(); + $imageService = service('image'); - $thumbnailSize = $this->config->thumbnailSize; - $mediumSize = $this->config->mediumSize; - $largeSize = $this->config->largeSize; - $feedSize = $this->config->feedSize; - $id3Size = $this->config->id3Size; + foreach ($sizes as $name => $size) { + $pathProperty = $name . '_path'; + $imageService + ->withFile(media_path($this->path)) + ->resize($size[0], $size[1]) + ->save(media_path($this->{$pathProperty})); + } + } - $imageService - ->withFile(media_path($this->path)) - ->resize($thumbnailSize, $thumbnailSize) - ->save(media_path($this->thumbnail_path)); + /** + * @param array $sizes + */ + public function delete(array $sizes): void + { + helper('media'); - $imageService - ->withFile(media_path($this->path)) - ->resize($mediumSize, $mediumSize) - ->save(media_path($this->medium_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($largeSize, $largeSize) - ->save(media_path($this->large_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($feedSize, $feedSize) - ->save(media_path($this->feed_path)); - - $imageService - ->withFile(media_path($this->path)) - ->resize($id3Size, $id3Size) - ->save(media_path($this->id3_path)); + foreach (array_keys($sizes) as $name) { + $pathProperty = $name . '_path'; + unlink(media_path($this->{$pathProperty})); + } } } diff --git a/app/Entities/Person.php b/app/Entities/Person.php index aa9fbff4..f860ecec 100644 --- a/app/Entities/Person.php +++ b/app/Entities/Person.php @@ -19,16 +19,16 @@ use RuntimeException; * @property string $full_name * @property string $unique_name * @property string|null $information_url - * @property Image $image - * @property string $image_path - * @property string $image_mimetype + * @property Image $avatar + * @property string $avatar_path + * @property string $avatar_mimetype * @property int $created_by * @property int $updated_by * @property object[]|null $roles */ class Person extends Entity { - protected Image $image; + protected Image $avatar; protected ?int $podcast_id = null; @@ -47,8 +47,8 @@ class Person extends Entity 'full_name' => 'string', 'unique_name' => 'string', 'information_url' => '?string', - 'image_path' => '?string', - 'image_mimetype' => '?string', + 'avatar_path' => '?string', + 'avatar_mimetype' => '?string', 'podcast_id' => '?integer', 'episode_id' => '?integer', 'created_by' => 'integer', @@ -56,32 +56,31 @@ class Person extends Entity ]; /** - * Saves a picture in `public/media/persons/` + * Saves the person avatar in `public/media/persons/` */ - public function setImage(?Image $image = null): static + public function setAvatar(?Image $avatar = null): static { - if ($image === null) { + if ($avatar === null) { return $this; } helper('media'); - // Save image - $image->saveImage('persons', $this->attributes['unique_name']); + $avatar->saveImage(config('Images')->personAvatarSizes, 'persons', $this->attributes['unique_name']); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['avatar_mimetype'] = $avatar->mimetype; + $this->attributes['avatar_path'] = $avatar->path; return $this; } - public function getImage(): Image + public function getAvatar(): Image { - if ($this->attributes['image_path'] === null) { + if ($this->attributes['avatar_path'] === null) { return new Image(null, '/castopod-avatar-default.jpg', 'image/jpeg'); } - return new Image(null, $this->attributes['image_path'], $this->attributes['image_mimetype']); + return new Image(null, $this->attributes['avatar_path'], $this->attributes['avatar_mimetype']); } /** diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index 4861fa37..35c35093 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -34,9 +34,12 @@ use RuntimeException; * @property string|null $description Holds text only description, striped of any markdown or html special characters * @property string $description_markdown * @property string $description_html - * @property Image $image - * @property string $image_path - * @property string $image_mimetype + * @property Image $cover + * @property string $cover_path + * @property string $cover_mimetype + * @property Image|null $banner + * @property string|null $banner_path + * @property string|null $banner_mimetype * @property string $language_code * @property int $category_id * @property Category|null $category @@ -84,7 +87,9 @@ class Podcast extends Entity protected ?Actor $actor = null; - protected Image $image; + protected Image $cover; + + protected ?Image $banner; protected ?string $description = null; @@ -145,8 +150,10 @@ class Podcast extends Entity 'title' => 'string', 'description_markdown' => 'string', 'description_html' => 'string', - 'image_path' => 'string', - 'image_mimetype' => 'string', + 'cover_path' => 'string', + 'cover_mimetype' => 'string', + 'banner_path' => '?string', + 'banner_mimetype' => '?string', 'language_code' => 'string', 'category_id' => 'integer', 'parental_advisory' => '?string', @@ -189,22 +196,63 @@ class Podcast extends Entity } /** - * Saves a cover image to the corresponding podcast folder in `public/media/podcast_name/` + * Saves a podcast cover to the corresponding podcast folder in `public/media/podcast_name/` */ - public function setImage(Image $image): static + public function setCover(Image $cover): static { // Save image - $image->saveImage('podcasts/' . $this->attributes['handle'], 'cover'); + $cover->saveImage(config('Images')->podcastCoverSizes, 'podcasts/' . $this->attributes['handle'], 'cover'); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['cover_path'] = $cover->path; + $this->attributes['cover_mimetype'] = $cover->mimetype; return $this; } - public function getImage(): Image + public function getCover(): Image { - return new Image(null, $this->image_path, $this->image_mimetype); + return new Image(null, $this->cover_path, $this->cover_mimetype); + } + + /** + * Saves a podcast cover to the corresponding podcast folder in `public/media/podcast_name/` + */ + public function setBanner(?Image $banner): static + { + if ($banner === null) { + $this->attributes['banner_path'] = null; + $this->attributes['banner_mimetype'] = null; + + return $this; + } + + // Save image + $banner->saveImage( + config('Images') + ->podcastBannerSizes, + 'podcasts/' . $this->attributes['handle'], + 'banner' + ); + + $this->attributes['banner_path'] = $banner->path; + $this->attributes['banner_mimetype'] = $banner->mimetype; + + return $this; + } + + public function getBanner(): Image + { + if ($this->attributes['banner_path'] === null) { + return new Image( + null, + config('Images') + ->podcastBannerDefaultPath, + config('Images') + ->podcastBannerDefaultMimeType + ); + } + + return new Image(null, $this->banner_path, $this->banner_mimetype); } public function getLink(): string diff --git a/app/Helpers/id3_helper.php b/app/Helpers/id3_helper.php index e7232a16..c975f54b 100644 --- a/app/Helpers/id3_helper.php +++ b/app/Helpers/id3_helper.php @@ -51,7 +51,7 @@ if (! function_exists('write_audio_file_tags')) { $tagwriter->tagformats = ['id3v2.4']; $tagwriter->tag_encoding = $TextEncoding; - $cover = new File(media_path($episode->image->id3_path)); + $cover = new File(media_path($episode->cover->id3_path)); $APICdata = file_get_contents($cover->getRealPath()); diff --git a/app/Helpers/rss_helper.php b/app/Helpers/rss_helper.php index c23fab85..adb7d3bd 100644 --- a/app/Helpers/rss_helper.php +++ b/app/Helpers/rss_helper.php @@ -56,8 +56,7 @@ if (! function_exists('get_rss_feed')) { $itunesImage = $channel->addChild('image', null, $itunesNamespace); - // FIXME: This should be downsized to 1400x1400 - $itunesImage->addAttribute('href', $podcast->image->url); + $itunesImage->addAttribute('href', $podcast->cover->feed_url); $channel->addChild('language', $podcast->language_code); if ($podcast->location !== null) { @@ -134,7 +133,7 @@ if (! function_exists('get_rss_feed')) { $podcastNamespace, ); - $personElement->addAttribute('img', $person->image->medium_url); + $personElement->addAttribute('img', $person->avatar->medium_url); if ($person->information_url !== null) { $personElement->addAttribute('href', $person->information_url); @@ -190,7 +189,7 @@ if (! function_exists('get_rss_feed')) { } $image = $channel->addChild('image'); - $image->addChild('url', $podcast->image->feed_url); + $image->addChild('url', $podcast->cover->feed_url); $image->addChild('title', $podcast->title, null, false); $image->addChild('link', $podcast->link); @@ -234,7 +233,7 @@ if (! function_exists('get_rss_feed')) { $item->addChild('duration', (string) $episode->audio_file_duration, $itunesNamespace); $item->addChild('link', $episode->link); $episodeItunesImage = $item->addChild('image', null, $itunesNamespace); - $episodeItunesImage->addAttribute('href', $episode->image->feed_url); + $episodeItunesImage->addAttribute('href', $episode->cover->feed_url); $episode->parental_advisory && $item->addChild( @@ -300,7 +299,7 @@ if (! function_exists('get_rss_feed')) { htmlspecialchars(lang("PersonsTaxonomy.persons.{$role->group}.label", [], 'en')), ); - $personElement->addAttribute('img', $person->image->medium_url); + $personElement->addAttribute('img', $person->avatar->medium_url); if ($person->information_url !== null) { $personElement->addAttribute('href', $person->information_url); diff --git a/app/Libraries/PodcastEpisode.php b/app/Libraries/PodcastEpisode.php index b6c9f6e5..11bc23eb 100644 --- a/app/Libraries/PodcastEpisode.php +++ b/app/Libraries/PodcastEpisode.php @@ -52,8 +52,8 @@ class PodcastEpisode extends ObjectType $this->image = [ 'type' => 'Image', - 'mediaType' => $episode->image_mimetype, - 'url' => $episode->image->url, + 'mediaType' => $episode->cover_mimetype, + 'url' => $episode->cover->feed_url, ]; // add audio file diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index f3973d1c..c73886f3 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -75,8 +75,8 @@ class EpisodeModel extends Model 'audio_file_header_size', 'description_markdown', 'description_html', - 'image_path', - 'image_mimetype', + 'cover_path', + 'cover_mimetype', 'transcript_file_path', 'transcript_file_remote_url', 'chapters_file_path', diff --git a/app/Models/PersonModel.php b/app/Models/PersonModel.php index 36fba45c..77cc85c2 100644 --- a/app/Models/PersonModel.php +++ b/app/Models/PersonModel.php @@ -35,8 +35,8 @@ class PersonModel extends Model 'full_name', 'unique_name', 'information_url', - 'image_path', - 'image_mimetype', + 'avatar_path', + 'avatar_mimetype', 'created_by', 'updated_by', ]; diff --git a/app/Models/PodcastModel.php b/app/Models/PodcastModel.php index 5e185491..a342a6f9 100644 --- a/app/Models/PodcastModel.php +++ b/app/Models/PodcastModel.php @@ -10,7 +10,6 @@ declare(strict_types=1); namespace App\Models; -use App\Entities\Actor; use App\Entities\Podcast; use CodeIgniter\Database\Query; use CodeIgniter\HTTP\URI; @@ -41,8 +40,10 @@ class PodcastModel extends Model 'description_html', 'episode_description_footer_markdown', 'episode_description_footer_html', - 'image_path', - 'image_mimetype', + 'cover_path', + 'cover_mimetype', + 'banner_path', + 'banner_mimetype', 'language_code', 'category_id', 'parental_advisory', @@ -91,7 +92,7 @@ class PodcastModel extends Model 'handle' => 'required|regex_match[/^[a-zA-Z0-9\_]{1,32}$/]|is_unique[podcasts.handle,id,{id}]', 'description_markdown' => 'required', - 'image_path' => 'required', + 'cover_path' => 'required', 'language_code' => 'required', 'category_id' => 'required', 'owner_email' => 'required|valid_email', @@ -454,13 +455,16 @@ class PodcastModel extends Model { $podcast = (new self())->getPodcastById(is_array($data['id']) ? $data['id'][0] : $data['id']); - $podcastActor = (new ActorModel())->find($podcast->actor_id); + if ($podcast !== null) { + $podcastActor = (new ActorModel())->find($podcast->actor_id); - $podcastActor->avatar_image_url = $podcast->image->thumbnail_url; - $podcastActor->avatar_image_mimetype = $podcast->image_mimetype; - - (new ActorModel())->update($podcast->actor_id, $podcastActor); + if ($podcastActor) { + $podcastActor->avatar_image_url = $podcast->cover->thumbnail_url; + $podcastActor->avatar_image_mimetype = $podcast->cover_mimetype; + (new ActorModel())->update($podcast->actor_id, $podcastActor); + } + } return $data; } @@ -477,14 +481,18 @@ class PodcastModel extends Model $actorModel = new ActorModel(); $actor = $actorModel->getActorById($podcast->actor_id); - // update values - $actor->display_name = $podcast->title; - $actor->summary = $podcast->description_html; - $actor->avatar_image_url = $podcast->image->thumbnail_url; - $actor->avatar_image_mimetype = $podcast->image_mimetype; + if ($actor !== null) { + // update values + $actor->display_name = $podcast->title; + $actor->summary = $podcast->description_html; + $actor->avatar_image_url = $podcast->cover->thumbnail_url; + $actor->avatar_image_mimetype = $podcast->cover->mimetype; + $actor->cover_image_url = $podcast->banner->large_url; + $actor->cover_image_mimetype = $podcast->banner->mimetype; - if ($actor->hasChanged()) { - $actorModel->update($actor->id, $actor); + if ($actor->hasChanged()) { + $actorModel->update($actor->id, $actor); + } } } diff --git a/app/Resources/js/modules/EpisodesMap.ts b/app/Resources/js/modules/EpisodesMap.ts index 41b794c5..0f3c33ce 100644 --- a/app/Resources/js/modules/EpisodesMap.ts +++ b/app/Resources/js/modules/EpisodesMap.ts @@ -47,7 +47,7 @@ const drawEpisodesMap = async (mapDivId: string, dataUrl: string) => { data[i].longitude, ]).bindPopup( '
' +
           data[i].episode_title +
           '

diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php index edda5e81..210f9b31 100644 --- a/modules/Admin/Config/Routes.php +++ b/modules/Admin/Config/Routes.php @@ -98,6 +98,10 @@ $routes->group( $routes->post('edit', 'PodcastController::attemptEdit/$1', [ 'filter' => 'permission:podcast-edit', ]); + $routes->get('edit/delete-banner', 'PodcastController::deleteBanner/$1', [ + 'as' => 'podcast-banner-delete', + 'filter' => 'permission:podcast-edit', + ]); $routes->get('delete', 'PodcastController::delete/$1', [ 'as' => 'podcast-delete', 'filter' => 'permission:podcasts-delete', diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php index 31295321..cd33cc4b 100644 --- a/modules/Admin/Controllers/EpisodeController.php +++ b/modules/Admin/Controllers/EpisodeController.php @@ -112,8 +112,8 @@ class EpisodeController extends BaseController { $rules = [ 'audio_file' => 'uploaded[audio_file]|ext_in[audio_file,mp3,m4a]', - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', 'transcript_file' => 'ext_in[transcript,txt,html,srt,json]|permit_empty', 'chapters_file' => 'ext_in[chapters,json]|permit_empty', @@ -126,12 +126,6 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } - $image = null; - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $image = new Image($imageFile); - } - $newEpisode = new Episode([ 'podcast_id' => $this->podcast->id, 'title' => $this->request->getPost('title'), @@ -139,7 +133,6 @@ class EpisodeController extends BaseController 'guid' => null, 'audio_file' => $this->request->getFile('audio_file'), 'description_markdown' => $this->request->getPost('description'), - 'image' => $image, 'location' => $this->request->getPost('location_name') === '' ? null : new Location($this->request->getPost( 'location_name' )), @@ -163,6 +156,11 @@ class EpisodeController extends BaseController 'published_at' => null, ]); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $newEpisode->cover = new Image($coverFile); + } + $transcriptChoice = $this->request->getPost('transcript-choice'); if ( $transcriptChoice === 'upload-file' @@ -238,8 +236,8 @@ class EpisodeController extends BaseController $rules = [ 'audio_file' => 'uploaded[audio_file]|ext_in[audio_file,mp3,m4a]|permit_empty', - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', 'transcript_file' => 'ext_in[transcript_file,txt,html,srt,json]|permit_empty', 'chapters_file' => 'ext_in[chapters_file,json]|permit_empty', @@ -279,9 +277,9 @@ class EpisodeController extends BaseController $this->episode->audio_file = $audioFile; } - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $this->episode->image = new Image($imageFile); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $this->episode->cover = new Image($coverFile); } $transcriptChoice = $this->request->getPost('transcript-choice'); diff --git a/modules/Admin/Controllers/PersonController.php b/modules/Admin/Controllers/PersonController.php index 3d75828c..7825e6f8 100644 --- a/modules/Admin/Controllers/PersonController.php +++ b/modules/Admin/Controllers/PersonController.php @@ -66,8 +66,8 @@ class PersonController extends BaseController public function attemptCreate(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]', + 'avatar' => + 'is_image[avatar]|ext_in[avatar,jpg,jpeg,png]|min_dims[avatar,400,400]|is_image_ratio[avatar,1,1]', ]; if (! $this->validate($rules)) { @@ -85,9 +85,9 @@ class PersonController extends BaseController 'updated_by' => user_id(), ]); - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $person->image = new Image($imageFile); + $avatarFile = $this->request->getFile('avatar'); + if ($avatarFile !== null && $avatarFile->isValid()) { + $person->avatar = new Image($avatarFile); } $personModel = new PersonModel(); @@ -119,8 +119,8 @@ class PersonController extends BaseController public function attemptEdit(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,jpeg,png]|min_dims[image,400,400]|is_image_squared[image]', + 'avatar' => + 'is_image[avatar]|ext_in[avatar,jpg,jpeg,png]|min_dims[avatar,400,400]|is_image_ratio[avatar,1,1]', ]; if (! $this->validate($rules)) { @@ -134,9 +134,9 @@ class PersonController extends BaseController $this->person->unique_name = $this->request->getPost('unique_name'); $this->person->information_url = $this->request->getPost('information_url'); - $imageFile = $this->request->getFile('image'); - if ($imageFile !== null && $imageFile->isValid()) { - $this->person->image = new Image($imageFile); + $avatarFile = $this->request->getFile('avatar'); + if ($avatarFile !== null && $avatarFile->isValid()) { + $this->person->avatar = new Image($avatarFile); } $this->person->updated_by = user_id(); diff --git a/modules/Admin/Controllers/PodcastController.php b/modules/Admin/Controllers/PodcastController.php index dfdfc57b..d2cae11f 100644 --- a/modules/Admin/Controllers/PodcastController.php +++ b/modules/Admin/Controllers/PodcastController.php @@ -171,8 +171,9 @@ class PodcastController extends BaseController public function attemptCreate(): RedirectResponse { $rules = [ - 'image' => - 'uploaded[image]|is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'uploaded[cover]|is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', + 'banner' => 'is_image[banner]|ext_in[banner,jpg,png]|min_dims[banner,1500,500]|is_image_ratio[banner,3,1]', ]; if (! $this->validate($rules)) { @@ -195,7 +196,7 @@ class PodcastController extends BaseController 'title' => $this->request->getPost('title'), 'handle' => $this->request->getPost('handle'), 'description_markdown' => $this->request->getPost('description'), - 'image' => new Image($this->request->getFile('image')), + 'cover' => new Image($this->request->getFile('cover')), 'language_code' => $this->request->getPost('language'), 'category_id' => $this->request->getPost('category'), 'parental_advisory' => @@ -224,6 +225,11 @@ class PodcastController extends BaseController 'updated_by' => user_id(), ]); + $bannerFile = $this->request->getFile('banner'); + if ($bannerFile !== null && $bannerFile->isValid()) { + $podcast->banner = new Image($bannerFile); + } + $podcastModel = new PodcastModel(); $db = db_connect(); @@ -279,8 +285,9 @@ class PodcastController extends BaseController public function attemptEdit(): RedirectResponse { $rules = [ - 'image' => - 'is_image[image]|ext_in[image,jpg,png]|min_dims[image,1400,1400]|is_image_squared[image]', + 'cover' => + 'is_image[cover]|ext_in[cover,jpg,png]|min_dims[cover,1400,1400]|is_image_ratio[cover,1,1]', + 'banner' => 'is_image[banner]|ext_in[banner,jpg,png]|min_dims[banner,1500,500]|is_image_ratio[banner,3,1]', ]; if (! $this->validate($rules)) { @@ -302,9 +309,13 @@ class PodcastController extends BaseController $this->podcast->title = $this->request->getPost('title'); $this->podcast->description_markdown = $this->request->getPost('description'); - $image = $this->request->getFile('image'); - if ($image !== null && $image->isValid()) { - $this->podcast->image = new Image($image); + $coverFile = $this->request->getFile('cover'); + if ($coverFile !== null && $coverFile->isValid()) { + $this->podcast->cover = new Image($coverFile); + } + $bannerFile = $this->request->getFile('banner'); + if ($bannerFile !== null && $bannerFile->isValid()) { + $this->podcast->banner = new Image($bannerFile); } $this->podcast->language_code = $this->request->getPost('language'); $this->podcast->category_id = $this->request->getPost('category'); @@ -356,6 +367,28 @@ class PodcastController extends BaseController return redirect()->route('podcast-view', [$this->podcast->id]); } + public function deleteBanner(): RedirectResponse + { + if ($this->podcast->banner === null) { + return redirect()->back(); + } + + $this->podcast->banner->delete(config('Images')->podcastBannerSizes); + + $this->podcast->banner_path = null; + $this->podcast->banner_mimetype = null; + + $podcastModel = new PodcastModel(); + if (! $podcastModel->update($this->podcast->id, $this->podcast)) { + return redirect() + ->back() + ->withInput() + ->with('errors', $podcastModel->errors()); + } + + return redirect()->back(); + } + public function latestEpisodes(int $limit, int $podcastId): string { $episodes = (new EpisodeModel()) diff --git a/modules/Admin/Controllers/PodcastImportController.php b/modules/Admin/Controllers/PodcastImportController.php index f3d7a4eb..cddb3989 100644 --- a/modules/Admin/Controllers/PodcastImportController.php +++ b/modules/Admin/Controllers/PodcastImportController.php @@ -115,9 +115,9 @@ class PodcastImportController extends BaseController property_exists($nsItunes, 'image') && $nsItunes->image !== null && $nsItunes->image->attributes()['href'] !== null ) { - $imageFile = download_file((string) $nsItunes->image->attributes()['href']); + $coverFile = download_file((string) $nsItunes->image->attributes()['href']); } else { - $imageFile = download_file((string) $feed->channel[0]->image->url); + $coverFile = download_file((string) $feed->channel[0]->image->url); } $location = null; @@ -140,7 +140,8 @@ class PodcastImportController extends BaseController 'title' => (string) $feed->channel[0]->title, 'description_markdown' => $converter->convert($channelDescriptionHtml), 'description_html' => $channelDescriptionHtml, - 'image' => new Image($imageFile), + 'cover' => new Image($coverFile), + 'banner' => null, 'language_code' => $this->request->getPost('language'), 'category_id' => $this->request->getPost('category'), 'parental_advisory' => @@ -249,7 +250,7 @@ class PodcastImportController extends BaseController 'full_name' => $fullName, 'unique_name' => slugify($fullName), 'information_url' => $podcastPerson->attributes()['href'], - 'image' => new Image(download_file((string) $podcastPerson->attributes()['img'])), + 'avatar' => new Image(download_file((string) $podcastPerson->attributes()['img'])), 'created_by' => user_id(), 'updated_by' => user_id(), ]); @@ -326,9 +327,9 @@ class PodcastImportController extends BaseController property_exists($nsItunes, 'image') && $nsItunes->image !== null && $nsItunes->image->attributes()['href'] !== null ) { - $episodeImage = new Image(download_file((string) $nsItunes->image->attributes()['href'])); + $episodeCover = new Image(download_file((string) $nsItunes->image->attributes()['href'])); } else { - $episodeImage = null; + $episodeCover = null; } $location = null; @@ -351,7 +352,7 @@ class PodcastImportController extends BaseController ), 'description_markdown' => $converter->convert($itemDescriptionHtml), 'description_html' => $itemDescriptionHtml, - 'image' => $episodeImage, + 'cover' => $episodeCover, 'parental_advisory' => property_exists($nsItunes, 'explicit') && $nsItunes->explicit !== null ? (in_array((string) $nsItunes->explicit, ['yes', 'true'], true) @@ -402,7 +403,7 @@ class PodcastImportController extends BaseController 'full_name' => $fullName, 'unique_name' => slugify($fullName), 'information_url' => $episodePerson->attributes()['href'], - 'image' => new Image(download_file((string) $episodePerson->attributes()['img'])), + 'avatar' => new Image(download_file((string) $episodePerson->attributes()['img'])), 'created_by' => user_id(), 'updated_by' => user_id(), ]); diff --git a/modules/Admin/Controllers/SettingsController.php b/modules/Admin/Controllers/SettingsController.php index 1308df71..0362c823 100644 --- a/modules/Admin/Controllers/SettingsController.php +++ b/modules/Admin/Controllers/SettingsController.php @@ -25,7 +25,7 @@ class SettingsController extends BaseController { $rules = [ 'site_icon' => - 'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_squared[site_icon]|min_dims[image,512,512]|permit_empty', + 'is_image[site_icon]|ext_in[site_icon,png,jpeg]|is_image_ratio[site_icon,1,1]|min_dims[image,512,512]|permit_empty', ]; if (! $this->validate($rules)) { diff --git a/modules/Admin/Language/en/Common.php b/modules/Admin/Language/en/Common.php index f3a6a913..d105e76d 100644 --- a/modules/Admin/Language/en/Common.php +++ b/modules/Admin/Language/en/Common.php @@ -38,8 +38,6 @@ return [ 'noChoicesText' => 'No choices to choose from', 'maxItemText' => 'Cannot add more items', ], - 'image_size_hint' => - 'Image must be squared with at least 1400px wide and tall.', 'upload_file' => 'Upload a file', 'remote_url' => 'Remote URL', ], diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php index 073af021..87552b95 100644 --- a/modules/Admin/Language/en/Episode.php +++ b/modules/Admin/Language/en/Episode.php @@ -49,10 +49,10 @@ return [ 'audio_file' => 'Audio file', 'audio_file_hint' => 'Choose an .mp3 or .m4a audio file.', 'info_section_title' => 'Episode info', - 'info_section_subtitle' => '', - 'image' => 'Cover image', - 'image_hint' => - 'If you do not set an image, the podcast cover will be used instead.', + 'cover' => 'Episode cover', + 'cover_hint' => + 'If you do not set a cover, the podcast cover will be used instead.', + 'cover_size_hint' => 'Cover must be squared with at least 1400px wide and tall.', 'title' => 'Title', 'title_hint' => 'Should contain a clear and concise episode name. Do not specify the episode or season numbers here.', diff --git a/modules/Admin/Language/en/Person.php b/modules/Admin/Language/en/Person.php index 334e5351..b9e122d2 100644 --- a/modules/Admin/Language/en/Person.php +++ b/modules/Admin/Language/en/Person.php @@ -17,9 +17,9 @@ return [ 'edit' => 'Edit person', 'delete' => 'Delete person', 'form' => [ - 'image' => 'Picture', - 'image_size_hint' => - 'Image must be squared with at least 400px wide and tall.', + 'avatar' => 'Avatar', + 'avatar_size_hint' => + 'Avatar must be squared with at least 400px wide and tall.', 'full_name' => 'Full name', 'full_name_hint' => 'This is the full name or alias of the person.', 'unique_name' => 'Unique name', diff --git a/modules/Admin/Language/en/Podcast.php b/modules/Admin/Language/en/Podcast.php index d2aaa101..2a40d5b7 100644 --- a/modules/Admin/Language/en/Podcast.php +++ b/modules/Admin/Language/en/Podcast.php @@ -25,7 +25,11 @@ return [ 'form' => [ 'identity_section_title' => 'Podcast identity', 'identity_section_subtitle' => 'These fields allow you to get noticed.', - 'image' => 'Cover image', + 'cover' => 'Podcast cover', + 'cover_size_hint' => 'Cover must be squared with at least 1400px wide and tall.', + 'banner' => 'Podcast banner', + 'banner_size_hint' => 'Banner must have a 3:1 ratio with at least 1500px wide.', + 'banner_delete' => 'Delete podcast banner', 'title' => 'Title', 'handle' => 'Handle', 'handle_hint' => diff --git a/modules/Admin/Language/en/Validation.php b/modules/Admin/Language/en/Validation.php index ae627c4b..eb04b85f 100644 --- a/modules/Admin/Language/en/Validation.php +++ b/modules/Admin/Language/en/Validation.php @@ -11,8 +11,8 @@ declare(strict_types=1); return [ 'min_dims' => '{field} is either not an image, or it is not wide or tall enough.', - 'is_image_squared' => - '{field} is either not an image, or it is not squared (width and height differ).', + 'is_image_ratio' => + '{field} is either not an image or not of the right ratio.', 'validate_url' => 'The {field} field must be a valid URL (eg. https://example.com/).', ]; diff --git a/modules/Admin/Language/fr/Common.php b/modules/Admin/Language/fr/Common.php index 8788589f..f856cf90 100644 --- a/modules/Admin/Language/fr/Common.php +++ b/modules/Admin/Language/fr/Common.php @@ -38,8 +38,6 @@ return [ 'noChoicesText' => 'Aucune sélection possible', 'maxItemText' => 'Impossible de rajouter un élément', ], - 'image_size_hint' => - 'L’image doit être carrée, avec au minimum 1400px de long et de large.', 'upload_file' => 'Téléversez un fichier', 'remote_url' => 'URL distante', ], diff --git a/modules/Admin/Language/fr/Episode.php b/modules/Admin/Language/fr/Episode.php index b0cc9c80..ad3cf971 100644 --- a/modules/Admin/Language/fr/Episode.php +++ b/modules/Admin/Language/fr/Episode.php @@ -50,10 +50,10 @@ return [ 'audio_file' => 'Fichier audio', 'audio_file_hint' => 'Sélectionnez un fichier audio .mp3 ou .m4a.', 'info_section_title' => 'Informations épisode', - 'info_section_subtitle' => '', - 'image' => 'Image de couverture', - 'image_hint' => + 'cover' => 'Image de couverture', + 'cover_hint' => 'Si vous ne définissez pas d’image, celle du podcast sera utilisée à la place.', + 'cover_size_hint' => 'La couverture de l’épisode doit être carrée, avec au minimum 1400px de largeur et de hauteur.', 'title' => 'Titre', 'title_hint' => 'Doit contenir un titre d’épisode clair et concis. Ne précisez ici aucun numéro de saison ou d’épisode.', diff --git a/modules/Admin/Language/fr/Person.php b/modules/Admin/Language/fr/Person.php index db2e1ab4..aa77d877 100644 --- a/modules/Admin/Language/fr/Person.php +++ b/modules/Admin/Language/fr/Person.php @@ -17,8 +17,8 @@ return [ 'edit' => 'Modifier l’intervenant', 'delete' => 'Supprimer l’intervenant', 'form' => [ - 'image' => 'Photo', - 'image_size_hint' => + 'avatar' => 'Avatar', + 'avatar_size_hint' => 'L’image doit être carrée et avoir au moins 400px de largeur et de hauteur.', 'full_name' => 'Nom complet', 'full_name_hint' => 'Le nom complet ou le pseudonyme de l’intervenant', diff --git a/modules/Admin/Language/fr/Podcast.php b/modules/Admin/Language/fr/Podcast.php index 0d332120..268014e3 100644 --- a/modules/Admin/Language/fr/Podcast.php +++ b/modules/Admin/Language/fr/Podcast.php @@ -26,7 +26,11 @@ return [ 'identity_section_title' => 'Informations sur le Podcast', 'identity_section_subtitle' => 'Ces champs vous permettent de vous faire remarquer.', - 'image' => 'Image de couverture', + 'cover' => 'Couverture du podcast', + 'cover_size_hint' => 'La couverture du podcast doit être carrée, avec au minimum 1400px de largeur et de hauteur.', + 'banner' => 'Bannière du podcast', + 'banner_size_hint' => 'La bannière doit être au format 3/1, avec au minimum 1500px de largeur.', + 'banner_delete' => 'Supprimer la bannière du podcast', 'title' => 'Titre', 'handle' => 'Identifiant', 'handle_hint' => diff --git a/modules/Admin/Language/fr/Settings.php b/modules/Admin/Language/fr/Settings.php index e68c87c7..08cb0a64 100644 --- a/modules/Admin/Language/fr/Settings.php +++ b/modules/Admin/Language/fr/Settings.php @@ -16,7 +16,7 @@ return [ 'site_icon' => 'Favicon du site', 'site_icon_delete' => 'Supprimer la favicon du site', 'site_icon_hint' => 'Les favicons sont ce que vous voyez sur les onglets de votre navigateur, dans votre barre de favoris, et lorsque vous ajoutez un site web en raccourci sur des appareils mobiles.', - 'site_icon_helper' => 'La favicon doit être carrée et avoir au moins 512px de largeur et de hauteur.', + 'site_icon_helper' => 'La favicon doit être carrée, avec au minimum 512px de largeur et de hauteur.', 'site_name' => 'Titre du site', 'site_description' => 'Description du site', 'submit' => 'Save', diff --git a/modules/Admin/Language/fr/Validation.php b/modules/Admin/Language/fr/Validation.php index 12b45319..ec1d4f13 100644 --- a/modules/Admin/Language/fr/Validation.php +++ b/modules/Admin/Language/fr/Validation.php @@ -11,8 +11,8 @@ declare(strict_types=1); return [ 'min_dims' => '{field} n’est pas une image ou n’a pas la taille minimale requise.', - 'is_image_squared' => - '{field} n’est pas une image ou n’est pas carré (largeur et hauteur différentes).', + 'is_image_ratio' => + '{field} n’est pas une image ou n’est pas au bon format.', 'validate_url' => 'Le champs {field} doit être une adresse valide (par exemple https://exemple.com/).', ]; diff --git a/phpstan.neon b/phpstan.neon index 88e1055a..25346598 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -28,6 +28,7 @@ parameters: - '#Function \"preg_.*\(\)\" cannot be used/left in the code#' - '#Function "property_exists\(\)" cannot be used/left in the code#' - '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(\)\)\-\>isSuperTypeOf\(\)" for static reflection to work#' + - '#^Access to an undefined property App\\Entities\\Image#' - message: '#Function "function_exists\(\)" cannot be used/left in the code#' paths: diff --git a/public/media/castopod-cover-default.jpg b/public/media/castopod-banner-default.jpg similarity index 100% rename from public/media/castopod-cover-default.jpg rename to public/media/castopod-banner-default.jpg diff --git a/public/media/castopod-banner-default_large.jpg b/public/media/castopod-banner-default_large.jpg new file mode 100644 index 0000000000000000000000000000000000000000..938e3bef51fb1a7eed49f0559a0e4e289093ab3c GIT binary patch literal 15615 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 K|2YF#u=CHB7?CFc literal 0 HcmV?d00001 diff --git a/public/media/castopod-banner-default_medium.jpg b/public/media/castopod-banner-default_medium.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f951c7de456d82c5e95cd5dfe040b0f8151c32f2 GIT binary patch literal 6919 zcmeH~dpMNa9>?D?!!WMHkWe!v8tk$aa&3}uXrJVgGNhQoBxKwt<;X z0^a~~cGkAm00aU7jtUk#IDJs7~&q7SW!4tkQ2(>LP<5zL@-DS0kj%`z$wtJDzNFbEfD6dt~(A8U zGc7wOH}4k)^K#LZ;*zV^$}1{w-m0p;T~k-z(D28Z4bkG$iI zj*U;xeB#dX=H?d`m$@JS^xs(D$o{~E6mp5cU{Dx*nF}HkE;J|-Cb~gSOy0r)?ir-8 zaa)`?YInx3WwjFO+a0GAy+~~crA_*S8q>>YU&#I(*qQ%}>>JqcTs?pk6e8R_C=$Q{ z3+t)a(xZGcEQ1QH+Uhr0)xfF-RyDAyfxm+W;O1IkJLc?1+<4hUe`P?VzxUpuMtoeu zv>{nNw`-suM^jZ@DxP7{QC@V+{qiNXM~?(!{E{!8ka;GG1ZN*1{9$71uEpjL!C%%f z2{9pp1MNe}4v*2UlvKj<(D`KG1pR4Elmpcm`d+u}*_UBT%IEflgZ5NC8w}+*&p0Jz zG)8-0eguoD=rQqpD*+R$TH!dX($GJi8coHPnC_-x3q@9f#j&YyJTQ*ccQ_1B3YjLF z_$1!&tnO@^U~vvlcQgGF_b#YB>71-wYFBk7f`F%I{`}9?f&1=h)p*b%ha@|zV%MV8 z?i3&?w%k&ym(i+Yw|uWmh7MN`m-Re|MYC^@VB5p9I=ENct|qw@qnHB%84j(@qsvT# z35hFq1Azhk2UF)qL4Ye=1p@cAqs!Jm8tA6s^44ouoet!0B;!6q{Xt-oiW^jD1A$T1 zKoE#>m`&ozet1fMmL|`*S0zB2qXludKwz#B{aq;RV#8@T@3{RtoAVAHy&xc7kOl&~ zdSjM`(&z21)dd!85IEEJdnAJwt`T8P&Q$%%&h*#i@w`Z;R(4ar^y@nmr@@w-(`yaK z*`&8;5_RU|Jwk8zzA8|AfcyzzWXhzO7@Ec(RG}8veP|))5(jTyRb|i`?`CH`kt!k3 zBwU<2R8mXdlgrq%Ywq}FZw}pGD+`ND?nHEZ^jz9*Yslal2eYZNcpYdr0?UY95eCO& z*ZD$A3fnAL@eStj4{NlEc$98sl-;1K1K+3kW+9*rmq$ptrEUQMLVuGs>oN$KOhl`7^>vK~ zRAJkc@~F&YIV1El74~HeZHP3eFD#>44P+BN#g%fn~{Zv(-OAz5%A|t8Lb)ak0Coh{?)mS6*t0C3FnhWHN0gw9P zp5`Faq@m;hZ-ZL*r~`p!p9^a7auM5hDyKD5NBQA|pqRlY%qr`QdL5KQGvF5+Kgg@n zcN07-8$MNv5vw6uOMCd_T)3X(w4XumBio$g@AV(r>zye_pczG6yaobEC42%?vx4ES z+4o#NPd(sG>Oh(uXXkoflXF?u58vyP6-nYn^(%AC$XtVd54&{ybM>if32847EiPw2 zw_nMkk%t|$# zL-#MfiV*YTOxdMd)UFd}<=Dv?o*x_U{=R#eFOBR3agTOaQJrkz+yBFU& zMYK40{X zkrVU1J8`H|nC6i$DVNa+_N9X$aB<3q@m8^(Z#lgujF)!Bt(rJ8Hl`n%ec=n;@MP=`ELv=7FZz$Ev&q|H@6lhHgV;K&+FS! zLpedS22mWkoOJ$s zLu!F{$Y~Wyitb0@*<4`xiTTZ8ocm|q7pZjj?bxF?AXb58<|URDca3G#mmIZ#Uuv&# zf9PdOZ%JwD=nP&^0|A#$CQ;Ng;~%#$H>JCkFTTQ6FVb7mXDGfLm3qJHGVfN@g>9u2 Z=T(lC@dL5CkE{E*s)4_x24unRe*us(Ie-8F literal 0 HcmV?d00001 diff --git a/public/media/castopod-banner-default_small.jpg b/public/media/castopod-banner-default_small.jpg new file mode 100644 index 0000000000000000000000000000000000000000..dd98b87d3883e724477ef000e2c78774d21ae0f0 GIT binary patch literal 1748 zcmex=_1P|rX?qqI0P zFI~aY%U!`Mz|~!$%*;qrN1?DZF(6Oj-S5fuR$!pIEN!@|nR z%E~Fi%grl7GWdUhL6C!?fzg4PQHg;`kdaxC@&6G9c?JeXR-jiwzJ&rtCZHSH*f}`4 zxPc0`3NSD+GBY!=FoRqTR9y>{XJ8Rz6;d>GWD^cdWLGK_F>0K+kVDyN<3Z7&iyu^s zlZu)+xx~aJB&Af<)HO7@(s#)lOnKGb1qam<1W^8Gg!jX{=B9W23RCi(yFP#Ue=!TpC9u7BF>b zP>#2ilrr7eW~oz@Ew$}S|Bo;K85*T_yem$!V=n)#YBo`1xu z|3`V&`oGM1{~6w&u5YbUTU>A7vSqE+b?*)4sZ*wDwe&C;`B_h@y6%}-vt6ug^Nvrx zhgNd@byT+abxm)}q+RYI1tw4K#H_jYw_p4}LrZb!e})CQ_y7LP|FGh|$Ifk2g+6KO zg#HkUaXYp1&+6NG`eEAHGXpBuGEF+Mw@BRL*Ox!9XI;u)R}-1IBJ$O(`kQVC0=tZQ zWFGvvd(3#2Xxi1c3zj!57hAOC-M`1jx8C3}++_90<9_Bh+t+;CzSe{N#&u-v(w%49 ze*nX7g_RQ}0SFJRBWvf%9^9&|tk~xoQ*OCYp7Eue{Evy>xFz1M&%Ku{)ase6tispO z{O8r*<44wNy%QF>CVW!z$?Dwy3{syx(>%h9H-x^N)&AS<_P?tB6IXTx>^;g8r|3R4 zYcGG`wjYz1MjlaW_Mc~R(js6$-RHter|+D}hBABByS@m$`k&$P<$o;NLfcB)nXd4Bibx1E4cRUy X|A0$l(Snyi0+^mgF(?e||K9`v - {$userPodcast->title}{$checkMark} + {$userPodcast->title}{$checkMark} CODE_SAMPLE; } diff --git a/themes/cp_admin/episode/_card.php b/themes/cp_admin/episode/_card.php index a77f478b..0dcee50e 100644 --- a/themes/cp_admin/episode/_card.php +++ b/themes/cp_admin/episode/_card.php @@ -2,7 +2,7 @@
- <?= $episode->title ?> + <?= $episode->title ?>
published_at, $episode->publication_status, 'absolute top-0 left-0 ml-2 mt-2 text-sm'); ?>
diff --git a/themes/cp_admin/episode/_sidebar.php b/themes/cp_admin/episode/_sidebar.php index 8dba09e1..ee91ce6a 100644 --- a/themes/cp_admin/episode/_sidebar.php +++ b/themes/cp_admin/episode/_sidebar.php @@ -10,7 +10,7 @@ $podcastNavigation = [ <?= $podcast->title ?> @@ -18,7 +18,7 @@ $podcastNavigation = [
<?= $episode->title ?> diff --git a/themes/cp_admin/episode/create.php b/themes/cp_admin/episode/create.php index d9755458..41ac3e15 100644 --- a/themes/cp_admin/episode/create.php +++ b/themes/cp_admin/episode/create.php @@ -28,10 +28,10 @@ required="true" /> diff --git a/themes/cp_admin/episode/edit.php b/themes/cp_admin/episode/edit.php index 1f4d87f6..2bd9e46d 100644 --- a/themes/cp_admin/episode/edit.php +++ b/themes/cp_admin/episode/edit.php @@ -31,10 +31,10 @@ accept=".mp3,.m4a" /> diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php index 9f571ef5..1e8d29df 100644 --- a/themes/cp_admin/episode/list.php +++ b/themes/cp_admin/episode/list.php @@ -34,7 +34,7 @@ $episode->audio_file_duration, ) . '' . - '' . $episode->title . '' . + '' . $episode->title . '' . '
' . '' . 'image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . + "\">avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . '
' . $person->full_name . implode( diff --git a/themes/cp_admin/episode/publish.php b/themes/cp_admin/episode/publish.php index 11af5a63..10b69950 100644 --- a/themes/cp_admin/episode/publish.php +++ b/themes/cp_admin/episode/publish.php @@ -40,7 +40,7 @@
- cover ->thumbnail_url ?>" alt="title ?>" class="w-24 h-24" />
- cover ->thumbnail_url ?>" alt="title ?>" class="w-24 h-24" />
diff --git a/themes/cp_admin/person/_card.php b/themes/cp_admin/person/_card.php index c5049829..8301cc3e 100644 --- a/themes/cp_admin/person/_card.php +++ b/themes/cp_admin/person/_card.php @@ -2,7 +2,7 @@
- <?= $person->full_name ?> + <?= $person->full_name ?>

full_name ?>

diff --git a/themes/cp_admin/person/create.php b/themes/cp_admin/person/create.php index 531603e5..a7e2ec01 100644 --- a/themes/cp_admin/person/create.php +++ b/themes/cp_admin/person/create.php @@ -15,9 +15,9 @@ diff --git a/themes/cp_admin/person/edit.php b/themes/cp_admin/person/edit.php index 7ce8966e..803bd984 100644 --- a/themes/cp_admin/person/edit.php +++ b/themes/cp_admin/person/edit.php @@ -15,9 +15,9 @@ diff --git a/themes/cp_admin/person/view.php b/themes/cp_admin/person/view.php index 395b6d44..fa17865f 100644 --- a/themes/cp_admin/person/view.php +++ b/themes/cp_admin/person/view.php @@ -18,7 +18,7 @@
$person->full_name diff --git a/themes/cp_admin/podcast/_card.php b/themes/cp_admin/podcast/_card.php index 8db60308..ea4d9b88 100644 --- a/themes/cp_admin/podcast/_card.php +++ b/themes/cp_admin/podcast/_card.php @@ -4,7 +4,7 @@
<?= $podcast->title ?> + src="cover->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" />

title ?>

diff --git a/themes/cp_admin/podcast/_sidebar.php b/themes/cp_admin/podcast/_sidebar.php index 9c0a7c06..21a54dba 100644 --- a/themes/cp_admin/podcast/_sidebar.php +++ b/themes/cp_admin/podcast/_sidebar.php @@ -37,7 +37,7 @@ $podcastNavigation = [
<?= $podcast->title ?> diff --git a/themes/cp_admin/podcast/create.php b/themes/cp_admin/podcast/create.php index 0ef31938..3de000c4 100644 --- a/themes/cp_admin/podcast/create.php +++ b/themes/cp_admin/podcast/create.php @@ -14,22 +14,28 @@ section('content') ?> -
+ + + @@ -114,7 +119,6 @@ @@ -143,7 +147,6 @@ @@ -155,7 +158,6 @@ @@ -184,7 +186,6 @@ @@ -197,7 +198,6 @@ diff --git a/themes/cp_admin/podcast/edit.php b/themes/cp_admin/podcast/edit.php index 71245af1..2f5874cc 100644 --- a/themes/cp_admin/podcast/edit.php +++ b/themes/cp_admin/podcast/edit.php @@ -18,18 +18,41 @@ section('content') ?> - + +
+ banner_path !== null): ?> + + + +
+ <?= $podcast->title ?> +
+

title ?>

+

@handle ?>

+
+
+
+ +
+ + + @@ -65,7 +88,6 @@ @@ -114,7 +136,6 @@ @@ -147,7 +168,6 @@ @@ -160,7 +180,6 @@ @@ -190,7 +209,6 @@ @@ -204,7 +222,6 @@ @@ -217,6 +234,9 @@ + +
+
endSection() ?> diff --git a/themes/cp_admin/podcast/latest_episodes.php b/themes/cp_admin/podcast/latest_episodes.php index 756b0a70..a90cefbe 100644 --- a/themes/cp_admin/podcast/latest_episodes.php +++ b/themes/cp_admin/podcast/latest_episodes.php @@ -1,5 +1,5 @@
-
+
+
$episode, diff --git a/themes/cp_admin/podcast/persons.php b/themes/cp_admin/podcast/persons.php index c75a46b1..12552537 100644 --- a/themes/cp_admin/podcast/persons.php +++ b/themes/cp_admin/podcast/persons.php @@ -55,7 +55,7 @@ return '
' . 'image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . + "\">avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" />" . '
' . $person->full_name . implode( diff --git a/themes/cp_admin/settings/general.php b/themes/cp_admin/settings/general.php index f48bc581..dc51fa40 100644 --- a/themes/cp_admin/settings/general.php +++ b/themes/cp_admin/settings/general.php @@ -10,7 +10,7 @@ section('content') ?> -
+ - {$userPodcast->title}{$checkMark} + {$userPodcast->title}{$checkMark} CODE_SAMPLE; } diff --git a/themes/cp_app/_persons_modal.php b/themes/cp_app/_persons_modal.php index 6822ed01..59384b4c 100644 --- a/themes/cp_app/_persons_modal.php +++ b/themes/cp_app/_persons_modal.php @@ -17,7 +17,7 @@
- <?= $person->full_name ?> + <?= $person->full_name ?>

information_url): ?> diff --git a/themes/cp_app/embed.php b/themes/cp_app/embed.php index 612a5f1f..f3208bd4 100644 --- a/themes/cp_app/embed.php +++ b/themes/cp_app/embed.php @@ -20,7 +20,7 @@ - <?= $episode->title ?> + <?= $episode->title ?>
- <?= $episode->podcast->title ?> + <?= $episode->podcast->title ?>
podcast->title ?>
-
+
- <?= $episode->title ?> + <?= $episode->title ?>
number, $episode->season_number, 'bg-pine-50 text-sm leading-none font-semibold text-gray-700 border !no-underline border-pine-100', true) ?>

title ?>

@@ -82,7 +82,7 @@
persons as $person): ?> - <?= $person->full_name ?> + <?= $person->full_name ?> @@ -102,7 +102,7 @@
audio_file_duration) ?> - <?= $episode->title ?>
@@ -16,7 +16,7 @@
audio_file_duration) ?> <?= $episode->title ?>
@@ -17,7 +17,7 @@ - + - + ->podcastCoverSizes['large'][0] ?>" /> + @@ -23,7 +22,7 @@ - + diff --git a/themes/cp_app/episode/comments.php b/themes/cp_app/episode/comments.php index 33e9c9b4..873e1668 100644 --- a/themes/cp_app/episode/comments.php +++ b/themes/cp_app/episode/comments.php @@ -11,11 +11,10 @@ - + - + ->podcastCoverSizes['large'][0] ?>" /> + @@ -25,7 +24,7 @@ - + diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php index 0852e85c..c4328a67 100644 --- a/themes/cp_app/home.php +++ b/themes/cp_app/home.php @@ -52,7 +52,7 @@
- <?= $podcast->title ?> + <?= $podcast->title ?>

title ?>

diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php index 8a9c9eea..3162a727 100644 --- a/themes/cp_app/podcast/_layout.php +++ b/themes/cp_app/podcast/_layout.php @@ -34,9 +34,10 @@
-
-
- <?= $podcast->title ?> +
+
+
+ <?= $podcast->title ?>

title ?>@handle ?>

-
+
fundingPlatforms, 'is_visible'), true)): ?> diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php index 1c80116c..5ffffba3 100644 --- a/themes/cp_app/podcast/about.php +++ b/themes/cp_app/podcast/about.php @@ -19,11 +19,9 @@ - - - + + + persons as $person): ?> - <?= $person->full_name ?> + <?= $person->full_name ?> diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php index 3743d885..c62f3674 100644 --- a/themes/cp_app/podcast/activity.php +++ b/themes/cp_app/podcast/activity.php @@ -17,11 +17,9 @@ - - - + + + language_code ?>" /> - - - + + +