From 4a8147bfbbd98d9badfc57a0f2a18bdd5812e802 Mon Sep 17 00:00:00 2001 From: Yassine Doghri <yassine@doghri.fr> 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 | 73 ++++--- 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 | 190 ++++++------------ 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 | 42 ++-- app/Resources/js/modules/EpisodesMap.ts | 2 +- app/Resources/styles/custom.css | 15 ++ app/Validation/FileRules.php | 14 +- 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, 506 insertions(+), 419 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 debc98f039..680a19507f 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; - /** - * Size of images linked in the rss feed (should be between 1400 and 3000) + * 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<string, int[]> */ - public int $feedSize = 1400; + public array $podcastCoverSizes = [ + 'tiny' => [40, 40], + 'thumbnail' => [150, 150], + 'medium' => [320, 320], + 'large' => [1024, 1024], + 'feed' => [1400, 1400], + 'id3' => [500, 500], + ]; /** - * Size for ID3 tag cover art (should be between 300 and 800) + * Podcast header cover image + * + * Uploaded podcast header covers are of 3:1 ratio + * + * Array values are as follows: 'name' => [width, height] + * + * @var array<string, int[]> */ - 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 array $podcastBannerSizes = [ + 'small' => [320, 128], + 'medium' => [960, 320], + 'large' => [1500, 500], + ]; - public string $largeSuffix = '_large'; + public string $podcastBannerDefaultPath = 'castopod-banner-default.jpg'; - public string $feedSuffix = '_feed'; + public string $podcastBannerDefaultMimeType = 'image/jpeg'; - public string $id3Suffix = '_id3'; + /** + * 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<string, int[]> + */ + 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 8c1b5418bf..816653dd15 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 0b439a61f6..3a59f91a27 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"></iframe>', '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 6c6df82d80..5ead95979b 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 9243e7f386..0663392a1b 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 310b1b70a5..aea2a023c3 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 9101ee772b..58e26df307 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 5f2689b88a..fdc16d04bb 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 799840c8ea..78740d0c66 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,152 +70,87 @@ class Image extends Entity $this->mimetype = $mimetype; } - public function getFile(): File + public function __get($property) { - if ($this->file === null) { - $this->file = new File($this->path); + // 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}(); } - 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'); + $fileSuffix = ''; + if ($lastUnderscorePosition = strrpos($property, '_')) { + $fileSuffix = '_' . substr($property, 0, $lastUnderscorePosition); + } - return media_base_url($this->thumbnail_path); - } + $path = ''; + if ($this->dirname !== '.') { + $path .= $this->dirname . '/'; + } + $path .= $this->filename . $fileSuffix . '.' . $this->extension; - public function getMediumPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->mediumSuffix . - '.' . - $this->extension; - } + if (str_ends_with($property, 'url')) { + helper('media'); - public function getMediumUrl(): string - { - helper('media'); + return media_base_url($path); + } - return media_base_url($this->medium_path); + if (str_ends_with($property, 'path')) { + return $path; + } } - public function getLargePath(): string + public function getMimetype(): string { - return $this->dirname . - '/' . - $this->filename . - $this->config->largeSuffix . - '.' . - $this->extension; + return $this->mimetype; } - public function getLargeUrl(): string + public function getFile(): File { - helper('media'); - - return media_base_url($this->large_path); - } + if ($this->file === null) { + $this->file = new File($this->path); + } - public function getFeedPath(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->feedSuffix . - '.' . - $this->extension; + return $this->file; } - public function getFeedUrl(): string + /** + * @param array<string, int[]> $sizes + */ + public function saveImage(array $sizes, string $dirname, string $filename): void { helper('media'); - return media_base_url($this->feed_path); - } + $this->dirname = $dirname; + $this->filename = $filename; - public function getId3Path(): string - { - return $this->dirname . - '/' . - $this->filename . - $this->config->id3Suffix . - '.' . - $this->extension; - } + save_media($this->file, $this->dirname, $this->filename); - public function getId3Url(): string - { - helper('media'); + $imageService = service('image'); - return media_base_url($this->id3_path); + foreach ($sizes as $name => $size) { + $pathProperty = $name . '_path'; + $imageService + ->withFile(media_path($this->path)) + ->resize($size[0], $size[1]) + ->save(media_path($this->{$pathProperty})); + } } - public function saveImage(string $dirname, string $filename): void + /** + * @param array<string, int[]> $sizes + */ + public function delete(array $sizes): void { helper('media'); - $this->dirname = $dirname; - $this->filename = $filename; - - save_media($this->file, $this->dirname, $this->filename); - - $imageService = Services::image(); - - $thumbnailSize = $this->config->thumbnailSize; - $mediumSize = $this->config->mediumSize; - $largeSize = $this->config->largeSize; - $feedSize = $this->config->feedSize; - $id3Size = $this->config->id3Size; - - $imageService - ->withFile(media_path($this->path)) - ->resize($thumbnailSize, $thumbnailSize) - ->save(media_path($this->thumbnail_path)); - - $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 aa9fbff4eb..f860ecec39 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 4861fa371a..35c35093a8 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 setCover(Image $cover): static + { + // Save image + $cover->saveImage(config('Images')->podcastCoverSizes, 'podcasts/' . $this->attributes['handle'], 'cover'); + + $this->attributes['cover_path'] = $cover->path; + $this->attributes['cover_mimetype'] = $cover->mimetype; + + return $this; + } + + public function getCover(): Image + { + 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 setImage(Image $image): static + public function setBanner(?Image $banner): static { + if ($banner === null) { + $this->attributes['banner_path'] = null; + $this->attributes['banner_mimetype'] = null; + + return $this; + } + // Save image - $image->saveImage('podcasts/' . $this->attributes['handle'], 'cover'); + $banner->saveImage( + config('Images') + ->podcastBannerSizes, + 'podcasts/' . $this->attributes['handle'], + 'banner' + ); - $this->attributes['image_mimetype'] = $image->mimetype; - $this->attributes['image_path'] = $image->path; + $this->attributes['banner_path'] = $banner->path; + $this->attributes['banner_mimetype'] = $banner->mimetype; return $this; } - public function getImage(): Image + public function getBanner(): Image { - return new Image(null, $this->image_path, $this->image_mimetype); + 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 e7232a1611..c975f54b8a 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 c23fab8521..adb7d3bd5d 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 b6c9f6e522..11bc23eb46 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 f3973d1c5b..c73886f32f 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 36fba45ca1..77cc85c2fe 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 5e18549101..a342a6f9dc 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); - - $podcastActor->avatar_image_url = $podcast->image->thumbnail_url; - $podcastActor->avatar_image_mimetype = $podcast->image_mimetype; + if ($podcast !== null) { + $podcastActor = (new ActorModel())->find($podcast->actor_id); - (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->hasChanged()) { - $actorModel->update($actor->id, $actor); + 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); + } } } diff --git a/app/Resources/js/modules/EpisodesMap.ts b/app/Resources/js/modules/EpisodesMap.ts index 41b794c5f3..0f3c33ce63 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( '<div class="flex min-w-max"><img src="' + - data[i].image_path + + data[i].cover_path + '" alt="' + data[i].episode_title + '" class="mr-2 rounded w-16 h-16" /><div class="flex flex-col"><h2 class="lg:text-base text-sm ! font-bold"><a href="' + diff --git a/app/Resources/styles/custom.css b/app/Resources/styles/custom.css index 8f5fabad55..815c0d6aac 100644 --- a/app/Resources/styles/custom.css +++ b/app/Resources/styles/custom.css @@ -38,4 +38,19 @@ hsla(0, 0%, 0%, 0.8) 100% ); } + + .backdrop-gradient-pine { + background-image: linear-gradient( + 180deg, + hsla(172, 100%, 17%, 0) 0%, + hsla(172, 100%, 17%, 0.034375) 16.36%, + hsla(172, 100%, 17%, 0.125) 33.34%, + hsla(172, 100%, 17%, 0.253125) 50.1%, + hsla(172, 100%, 17%, 0.4) 65.75%, + hsla(172, 100%, 17%, 0.546875) 79.43%, + hsla(172, 100%, 17%, 0.675) 90.28%, + hsla(172, 100%, 17%, 0.765625) 97.43%, + hsla(172, 100%, 17%, 0.8) 100% + ); + } } diff --git a/app/Validation/FileRules.php b/app/Validation/FileRules.php index 6b6ed3f31f..e56fd0923c 100644 --- a/app/Validation/FileRules.php +++ b/app/Validation/FileRules.php @@ -57,9 +57,9 @@ class FileRules extends ValidationFileRules //-------------------------------------------------------------------- /** - * Checks an uploaded file to verify that the image ratio is of 1:1 + * Checks an uploaded image to verify that the ratio corresponds to the params */ - public function is_image_squared(string $blank = null, string $params): bool + public function is_image_ratio(string $blank = null, string $params): bool { // Grab the file name off the top of the $params // after we split it. @@ -79,12 +79,14 @@ class FileRules extends ValidationFileRules return true; } + // Get Parameter sizes + $x = $params[0] ?? 1; + $y = $params[1] ?? 1; + // Get uploaded image size - $info = getimagesize($file->getTempName()); - $fileWidth = $info[0]; - $fileHeight = $info[1]; + [0 => $fileWidth, 1 => $fileHeight] = getimagesize($file->getTempName()); - if ($fileWidth !== $fileHeight) { + if (($x / $y) !== ($fileWidth / $fileHeight)) { return false; } } diff --git a/app/Views/Components/Forms/Input.php b/app/Views/Components/Forms/Input.php index 31489e1a90..bbddfd819a 100644 --- a/app/Views/Components/Forms/Input.php +++ b/app/Views/Components/Forms/Input.php @@ -10,7 +10,7 @@ class Input extends FormComponent public function render(): string { - $baseClass = 'bg-white border-black rounded-lg focus:border-black border-3 focus:ring-castopod focus-within:ring-castopod ' . $this->class; + $baseClass = 'w-full bg-white border-black rounded-lg focus:border-black border-3 focus:ring-castopod focus-within:ring-castopod ' . $this->class; $this->attributes['class'] = $baseClass; diff --git a/app/Views/Components/Forms/MarkdownEditor.php b/app/Views/Components/Forms/MarkdownEditor.php index 63f9276a6a..62b035a97f 100644 --- a/app/Views/Components/Forms/MarkdownEditor.php +++ b/app/Views/Components/Forms/MarkdownEditor.php @@ -13,7 +13,7 @@ class MarkdownEditor extends FormComponent $this->attributes['class'] = 'border-none focus:border-none focus:outline-none focus:ring-0 w-full h-full'; $this->attributes['rows'] = 6; - $textarea = form_textarea($this->attributes, old($this->name, html_entity_decode($this->value), null)); + $textarea = form_textarea($this->attributes, old($this->name, html_entity_decode($this->value), false)); $icons = [ 'heading' => icon('heading'), 'bold' => icon('bold'), diff --git a/app/Views/Components/Forms/Textarea.php b/app/Views/Components/Forms/Textarea.php index 744861fef2..c1d7e20fda 100644 --- a/app/Views/Components/Forms/Textarea.php +++ b/app/Views/Components/Forms/Textarea.php @@ -17,7 +17,7 @@ class Textarea extends FormComponent { unset($this->attributes['value']); - $this->attributes['class'] = 'focus:border-black focus:ring-castopod rounded-lg border-3 border-black ' . $this->class; + $this->attributes['class'] = 'w-full focus:border-black focus:ring-castopod rounded-lg border-3 border-black ' . $this->class; $textarea = form_textarea( $this->attributes, diff --git a/app/Views/Components/Forms/Toggler.php b/app/Views/Components/Forms/Toggler.php index 15d101900c..5bc0c583e0 100644 --- a/app/Views/Components/Forms/Toggler.php +++ b/app/Views/Components/Forms/Toggler.php @@ -36,7 +36,7 @@ class Toggler extends FormComponent $this->attributes['class'] = 'form-switch'; - $checkbox = form_checkbox($this->attributes, $this->value, old($this->name, $this->checked)); + $checkbox = form_checkbox($this->attributes, $this->value, old($this->name) === 'yes' ? true : $this->checked); $hint = $this->hint === '' ? '' : hint_tooltip($this->hint, 'ml-1'); return <<<HTML <label class="relative inline-flex items-center {$wrapperClass}"> diff --git a/modules/Admin/Config/Routes.php b/modules/Admin/Config/Routes.php index edda5e8152..210f9b3183 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 312953217f..cd33cc4b21 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 3d75828c52..7825e6f8d3 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 dfdfc57be3..d2cae11f73 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 f3d7a4ebd7..cddb3989d0 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 1308df71f5..0362c8233f 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 f3a6a91364..d105e76d0d 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 073af0210a..87552b95ee 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 334e535154..b9e122d2e4 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 d2aaa10126..2a40d5b7f8 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 ae627c4b21..eb04b85ff0 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 8788589f73..f856cf9073 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 b0cc9c80e9..ad3cf9713f 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 db2e1ab491..aa77d8777a 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 0d332120f9..268014e3cc 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 e68c87c7a2..08cb0a64ac 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 12b45319f9..ec1d4f130d 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 88e1055aef..253465986b 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\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" 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 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 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=<NpH&0WUXCHwH#VMur3+WcdG&!P+^<)iK0B$VwqSMK`M;HC>_1P|rX?qqI0P zFI~aY%U!`Mz|~!$%*;qrN1?DZF(<R6Qo&Zi(tzv#Z3brsW+o;O0s<BwV&!0EWnp3E zVrOUL;Njxo;pXDz=H(L-;N=tK<K`9+6A%;@5fv5X;TM+>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<OiazpEiA2^U0mJVJv_aFLqfyCBO;@cQ&Q8?GcvP^OG?Yi zD=Mp+TUy)NJ36~2O`bA!+VmMSXDwR1Wa+ZyD^{-Bw0X<cZQFP3+;!;ik)y|spE!By z(&Z~xuU)@!^VY*hkDok!_WZ@mS06up{_^$P_a8rhf&9hDzzp#g+-GS15(N5-iG_uk zg&pKCMy7HgW)@^&RWxK1atvfoEEHBUYUB`cnz(S|K~81kpbw%+MHjimR7@VKegt_9 z>@(s#)<l-i;2uNx>lOnKGb1qam<1W^8Gg!jX{=B9W23RCi(yFP#Ue=!TpC9u7BF>b zP>#2ilrr7eW~oz@Ew$}S|Bo;K85*T_yem$!V=n)#<UL93sQSLrN5LX0i{>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(js<m^}Ma_aX+T$uSwaqaeGBnsMe(uZVvAo?B`ZK|JJ+x z&sX`0D;LdFi#>6$-RHter|+D}hBABByS@m$`k&$P<$o;NLfcB)nXd4Bibx1E4cRUy X|A0$l(Snyi0+^mgF(?e||K9`v<AaRZ literal 0 HcmV?d00001 diff --git a/themes/cp_admin/_partials/_nav_header.php b/themes/cp_admin/_partials/_nav_header.php index fc726afd50..e9a6f9dd21 100644 --- a/themes/cp_admin/_partials/_nav_header.php +++ b/themes/cp_admin/_partials/_nav_header.php @@ -36,7 +36,7 @@ $interactButtons .= <<<CODE_SAMPLE <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> - <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->image->thumbnail_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> + <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> </button> CODE_SAMPLE; } diff --git a/themes/cp_admin/episode/_card.php b/themes/cp_admin/episode/_card.php index a77f478b00..0dcee50e98 100644 --- a/themes/cp_admin/episode/_card.php +++ b/themes/cp_admin/episode/_card.php @@ -2,7 +2,7 @@ <a href="<?= route_to('episode-view', $episode->podcast->id, $episode->id) ?>" class="flex flex-col justify-end w-full h-full text-white group"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img src="<?= $episode->image->medium_url ?>" alt="<?= $episode->title ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img src="<?= $episode->cover->medium_url ?>" alt="<?= $episode->title ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <?= publication_pill($episode->published_at, $episode->publication_status, 'absolute top-0 left-0 ml-2 mt-2 text-sm'); ?> <div class="absolute z-20 flex flex-col items-start px-4 py-2"> diff --git a/themes/cp_admin/episode/_sidebar.php b/themes/cp_admin/episode/_sidebar.php index 8dba09e1fa..ee91ce6a7a 100644 --- a/themes/cp_admin/episode/_sidebar.php +++ b/themes/cp_admin/episode/_sidebar.php @@ -10,7 +10,7 @@ $podcastNavigation = [ <a href="<?= route_to('podcast-view', $podcast->id) ?>" class="flex items-center px-4 py-2 border-b border-pine-900 focus:ring-inset focus:ring-castopod"> <?= icon('arrow-left', 'mr-2') ?> <img - src="<?= $podcast->image->thumbnail_url ?>" + src="<?= $podcast->cover->tiny_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-6 h-6 rounded" /> @@ -18,7 +18,7 @@ $podcastNavigation = [ </a> <div class="flex items-center px-4 py-2 border-b border-pine-900"> <img - src="<?= $episode->image->thumbnail_url ?>" + src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="object-cover w-16 h-16 rounded" /> diff --git a/themes/cp_admin/episode/create.php b/themes/cp_admin/episode/create.php index d97554583d..41ac3e152c 100644 --- a/themes/cp_admin/episode/create.php +++ b/themes/cp_admin/episode/create.php @@ -28,10 +28,10 @@ required="true" /> <Forms.Field - name="image" - label="<?= lang('Episode.form.image') ?>" - hint="<?= lang('Episode.form.image_hint') ?>" - helper="<?= lang('Common.forms.image_size_hint', ) ?>" + name="cover" + label="<?= lang('Episode.form.cover') ?>" + hint="<?= lang('Episode.form.cover_hint') ?>" + helper="<?= lang('Episode.form.cover_size_hint', ) ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/episode/edit.php b/themes/cp_admin/episode/edit.php index 1f4d87f626..2bd9e46dc9 100644 --- a/themes/cp_admin/episode/edit.php +++ b/themes/cp_admin/episode/edit.php @@ -31,10 +31,10 @@ accept=".mp3,.m4a" /> <Forms.Field - name="image" - label="<?= lang('Episode.form.image') ?>" - hint="<?= lang('Episode.form.image_hint') ?>" - helper="<?= lang('Common.forms.image_size_hint', ) ?>" + name="cover" + label="<?= lang('Episode.form.cover') ?>" + hint="<?= lang('Episode.form.cover_hint') ?>" + helper="<?= lang('Episode.form.cover_size_hint', ) ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php index 9f571ef53f..1e8d29df78 100644 --- a/themes/cp_admin/episode/list.php +++ b/themes/cp_admin/episode/list.php @@ -34,7 +34,7 @@ $episode->audio_file_duration, ) . '</time>' . - '<img loading="lazy" src="' . $episode->image->thumbnail_url . '" alt="' . $episode->title . '" class="object-cover w-20 h-20 rounded-lg" />' . + '<img loading="lazy" src="' . $episode->cover->thumbnail_url . '" alt="' . $episode->title . '" class="object-cover w-20 h-20 rounded-lg" />' . '</div>' . '<a class="overflow-x-hidden text-sm hover:underline" href="' . route_to( 'episode-view', diff --git a/themes/cp_admin/episode/persons.php b/themes/cp_admin/episode/persons.php index 93ed8e400c..214aeb6b5c 100644 --- a/themes/cp_admin/episode/persons.php +++ b/themes/cp_admin/episode/persons.php @@ -58,7 +58,7 @@ return '<div class="flex">' . '<a href="' . route_to('person-view', $person->id) . - "\"><img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . + "\"><img src=\"{$person->avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . '<div class="flex flex-col ml-3">' . $person->full_name . implode( diff --git a/themes/cp_admin/episode/publish.php b/themes/cp_admin/episode/publish.php index 11af5a63fd..10b6995062 100644 --- a/themes/cp_admin/episode/publish.php +++ b/themes/cp_admin/episode/publish.php @@ -40,7 +40,7 @@ <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" rows="2" /> </div> <div class="flex border-t border-b"> - <img src="<?= $episode->image + <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2"> diff --git a/themes/cp_admin/episode/publish_edit.php b/themes/cp_admin/episode/publish_edit.php index cd6de4b545..d5127e062b 100644 --- a/themes/cp_admin/episode/publish_edit.php +++ b/themes/cp_admin/episode/publish_edit.php @@ -42,7 +42,7 @@ <Forms.Textarea name="message" placeholder="<?= lang('Episode.publish_form.message_placeholder') ?>" autofocus="" value="<?= $post->message ?>" rows="2" /> </div> <div class="flex border-t border-b"> - <img src="<?= $episode->image + <img src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2"> diff --git a/themes/cp_admin/person/_card.php b/themes/cp_admin/person/_card.php index c504982924..8301cc3e2c 100644 --- a/themes/cp_admin/person/_card.php +++ b/themes/cp_admin/person/_card.php @@ -2,7 +2,7 @@ <a href="<?= route_to('person-view', $person->id) ?>" class="flex flex-col justify-end w-full h-full text-white group"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img alt="<?= $person->full_name ?>" src="<?= $person->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img alt="<?= $person->full_name ?>" src="<?= $person->avatar->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> </div> <div class="absolute z-20"> <h2 class="px-4 py-2 font-semibold leading-tight"><?= $person->full_name ?></h2> diff --git a/themes/cp_admin/person/create.php b/themes/cp_admin/person/create.php index 531603e515..a7e2ec0193 100644 --- a/themes/cp_admin/person/create.php +++ b/themes/cp_admin/person/create.php @@ -15,9 +15,9 @@ <?= csrf_field() ?> <Forms.Field - name="image" - label="<?= lang('Person.form.image') ?>" - helper="<?= lang('Person.form.image_size_hint') ?>" + name="avatar" + label="<?= lang('Person.form.avatar') ?>" + helper="<?= lang('Person.form.avatar_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/person/edit.php b/themes/cp_admin/person/edit.php index 7ce8966e79..803bd98439 100644 --- a/themes/cp_admin/person/edit.php +++ b/themes/cp_admin/person/edit.php @@ -15,9 +15,9 @@ <?= csrf_field() ?> <Forms.Field - name="image" - label="<?= lang('Person.form.image') ?>" - helper="<?= lang('Person.form.image_size_hint') ?>" + name="avatar" + label="<?= lang('Person.form.avatar') ?>" + helper="<?= lang('Person.form.avatar_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> diff --git a/themes/cp_admin/person/view.php b/themes/cp_admin/person/view.php index 395b6d4416..fa17865fe6 100644 --- a/themes/cp_admin/person/view.php +++ b/themes/cp_admin/person/view.php @@ -18,7 +18,7 @@ <div class="flex flex-wrap"> <div class="w-full max-w-sm mb-6 md:mr-4"> <img - src="<?= $person->image->medium_url ?>" + src="<?= $person->avatar->medium_url ?>" alt="$person->full_name" class="object-cover w-full rounded" /> diff --git a/themes/cp_admin/podcast/_card.php b/themes/cp_admin/podcast/_card.php index 8db6030858..ea4d9b8879 100644 --- a/themes/cp_admin/podcast/_card.php +++ b/themes/cp_admin/podcast/_card.php @@ -4,7 +4,7 @@ <div class="w-full h-full overflow-hidden"> <img alt="<?= $podcast->title ?>" - src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + src="<?= $podcast->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" /> </div> <div class="absolute z-20 px-4 pb-4 transition duration-75 ease-out translate-y-6 group-focus:translate-y-0 group-hover:translate-y-0"> <h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2> diff --git a/themes/cp_admin/podcast/_sidebar.php b/themes/cp_admin/podcast/_sidebar.php index 9c0a7c0615..21a54dbaf2 100644 --- a/themes/cp_admin/podcast/_sidebar.php +++ b/themes/cp_admin/podcast/_sidebar.php @@ -37,7 +37,7 @@ $podcastNavigation = [ <div class="flex items-center px-4 py-2 border-b border-pine-900"> <img - src="<?= $podcast->image->thumbnail_url ?>" + src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" class="object-cover w-16 h-16 rounded" /> diff --git a/themes/cp_admin/podcast/create.php b/themes/cp_admin/podcast/create.php index 0ef31938e2..3de000c407 100644 --- a/themes/cp_admin/podcast/create.php +++ b/themes/cp_admin/podcast/create.php @@ -14,22 +14,28 @@ <?= $this->section('content') ?> -<form action="<?= route_to('podcast-create') ?>" method="POST" enctype='multipart/form-data' class="flex flex-col"> +<form action="<?= route_to('podcast-create') ?>" method="POST" enctype='multipart/form-data' class="flex flex-col gap-y-6"> <?= csrf_field() ?> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.identity_section_title') ?>" subtitle="<?= lang('Podcast.form.identity_section_subtitle') ?>" > <Forms.Field - name="image" - label="<?= lang('Podcast.form.image') ?>" - helper="<?= lang('Common.forms.image_size_hint') ?>" + name="cover" + label="<?= lang('Podcast.form.cover') ?>" + helper="<?= lang('Podcast.form.cover_size_hint') ?>" type="file" required="true" accept=".jpg,.jpeg,.png" /> +<Forms.Field + name="banner" + label="<?= lang('Podcast.form.banner') ?>" + helper="<?= lang('Podcast.form.banner_size_hint') ?>" + type="file" + accept=".jpg,.jpeg,.png" /> + <Forms.Field name="title" label="<?= lang('Podcast.form.title') ?>" @@ -67,7 +73,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.classification_section_title') ?>" subtitle="<?= lang('Podcast.form.classification_section_subtitle') ?>" > @@ -114,7 +119,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.author_section_title') ?>" subtitle="<?= lang('Podcast.form.author_section_subtitle') ?>" > @@ -143,7 +147,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.location_section_title') ?>" subtitle="<?= lang('Podcast.form.location_section_subtitle') ?>" > @@ -155,7 +158,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.monetization_section_title') ?>" subtitle="<?= lang('Podcast.form.monetization_section_subtitle') ?>" > @@ -184,7 +186,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.advanced_section_title') ?>" subtitle="<?= lang('Podcast.form.advanced_section_subtitle') ?>" > @@ -197,7 +198,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.status_section_title') ?>" > <Forms.Toggler class="mb-2" name="lock" value="yes" checked="true" hint="<?= lang('Podcast.form.lock_hint') ?>"> <?= lang('Podcast.form.lock') ?> diff --git a/themes/cp_admin/podcast/edit.php b/themes/cp_admin/podcast/edit.php index 71245af11b..2f5874cc23 100644 --- a/themes/cp_admin/podcast/edit.php +++ b/themes/cp_admin/podcast/edit.php @@ -18,18 +18,41 @@ <?= $this->section('content') ?> -<form id="podcast-edit-form" action="<?= route_to('podcast-edit', $podcast->id) ?>" method="POST" enctype='multipart/form-data' class="flex flex-col"> +<form id="podcast-edit-form" action="<?= route_to('podcast-edit', $podcast->id) ?>" method="POST" enctype='multipart/form-data' class="flex flex-row-reverse flex-wrap items-start justify-end gap-4"> <?= csrf_field() ?> +<div class="sticky z-40 flex flex-col w-full max-w-xs overflow-hidden bg-white shadow-sm border-3 border-pine-100 top-24 rounded-xl"> + <?php if ($podcast->banner_path !== null): ?> + <a href="<?= route_to('podcast-banner-delete', $podcast->id) ?>" class="absolute p-1 text-white bg-red-600 border-2 border-black rounded-full hover:bg-red-800 focus:ring-castopod top-2 right-2" title="<?= lang('Podcast.form.banner_delete') ?>"><?= icon('delete-bin') ?></a> + <?php endif; ?> + <img src="<?= $podcast->banner->small_url ?>" alt="" class="object-cover w-full aspect-[3/1] bg-pine-800" /> + <div class="flex px-4 py-2"> + <img src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" + class="w-16 h-16 mr-4 -mt-8 rounded-full ring-2 ring-white" /> + <div class="flex flex-col"> + <p class="font-semibold leading-none"><?= $podcast->title ?></p> + <p class="text-sm text-gray-500">@<?= $podcast->handle ?></p> + </div> + </div> +</div> + +<div class="flex flex-col gap-y-6"> + <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.identity_section_title') ?>" subtitle="<?= lang('Podcast.form.identity_section_subtitle') ?>" > <Forms.Field - name="image" - label="<?= lang('Podcast.form.image') ?>" - helper="<?= lang('Common.forms.image_size_hint') ?>" + name="cover" + label="<?= lang('Podcast.form.cover') ?>" + helper="<?= lang('Podcast.form.cover_size_hint') ?>" + type="file" + accept=".jpg,.jpeg,.png" /> + +<Forms.Field + name="banner" + label="<?= lang('Podcast.form.banner') ?>" + helper="<?= lang('Podcast.form.banner_size_hint') ?>" type="file" accept=".jpg,.jpeg,.png" /> @@ -65,7 +88,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.classification_section_title') ?>" subtitle="<?= lang('Podcast.form.classification_section_subtitle') ?>" > @@ -114,7 +136,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.author_section_title') ?>" subtitle="<?= lang('Podcast.form.author_section_subtitle') ?>" > @@ -147,7 +168,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.location_section_title') ?>" subtitle="<?= lang('Podcast.form.location_section_subtitle') ?>" > @@ -160,7 +180,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.monetization_section_title') ?>" subtitle="<?= lang('Podcast.form.monetization_section_subtitle') ?>" > @@ -190,7 +209,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.advanced_section_title') ?>" subtitle="<?= lang('Podcast.form.advanced_section_subtitle') ?>" > @@ -204,7 +222,6 @@ </Forms.Section> <Forms.Section - class="mb-8" title="<?= lang('Podcast.form.status_section_title') ?>" > <Forms.Toggler class="mb-2" name="lock" value="yes" checked="<?= $podcast->is_locked ? 'true' : 'false' ?>" hint="<?= lang('Podcast.form.lock_hint') ?>"> <?= lang('Podcast.form.lock') ?> @@ -217,6 +234,9 @@ </Forms.Toggler> </Forms.Section> +<Button variant="primary" type="submit" class="self-end"><?= lang('Podcast.form.submit_edit') ?></Button> +</div> + </form> <?= $this->endSection() ?> diff --git a/themes/cp_admin/podcast/latest_episodes.php b/themes/cp_admin/podcast/latest_episodes.php index 756b0a7051..a90cefbecd 100644 --- a/themes/cp_admin/podcast/latest_episodes.php +++ b/themes/cp_admin/podcast/latest_episodes.php @@ -1,5 +1,5 @@ <section class="flex flex-col"> - <header class="flex justify-between py-2"> + <header class="flex justify-between"> <Heading tagName="h2"><?= lang('Podcast.latest_episodes') ?></Heading> <a href="<?= route_to( 'episode-list', @@ -10,7 +10,7 @@ </a> </header> <?php if ($episodes): ?> - <div class="grid px-4 py-2 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity"> + <div class="grid px-4 pt-2 pb-5 -mx-2 overflow-x-auto grid-cols-latestEpisodes gap-x-4 snap snap-x snap-proximity"> <?php foreach ($episodes as $episode): ?> <?= view('episode/_card', [ 'episode' => $episode, diff --git a/themes/cp_admin/podcast/persons.php b/themes/cp_admin/podcast/persons.php index c75a46b1cf..1255253764 100644 --- a/themes/cp_admin/podcast/persons.php +++ b/themes/cp_admin/podcast/persons.php @@ -55,7 +55,7 @@ return '<div class="flex">' . '<a href="' . route_to('person-view', $person->id) . - "\"><img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . + "\"><img src=\"{$person->avatar->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-16 h-16 rounded-full\" /></a>" . '<div class="flex flex-col ml-3">' . $person->full_name . implode( diff --git a/themes/cp_admin/settings/general.php b/themes/cp_admin/settings/general.php index f48bc58136..dc51fa4060 100644 --- a/themes/cp_admin/settings/general.php +++ b/themes/cp_admin/settings/general.php @@ -10,7 +10,7 @@ <?= $this->section('content') ?> -<form action="<?= route_to('settings-instance') ?>" method="POST" class="flex flex-col max-w-sm gap-y-4" enctype="multipart/form-data"> +<form action="<?= route_to('settings-instance') ?>" method="POST" class="flex flex-col gap-y-4" enctype="multipart/form-data"> <?= csrf_field() ?> <Forms.Section diff --git a/themes/cp_app/_admin_navbar.php b/themes/cp_app/_admin_navbar.php index 69a32a1f40..da61c28128 100644 --- a/themes/cp_app/_admin_navbar.php +++ b/themes/cp_app/_admin_navbar.php @@ -31,7 +31,7 @@ $interactButtons .= <<<CODE_SAMPLE <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="interact-as-actor-{$userPodcast->id}" name="actor_id" value="{$userPodcast->actor_id}"> - <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->image->thumbnail_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> + <span class="inline-flex items-center flex-1 text-sm"><img src="{$userPodcast->cover->tiny_url}" class="w-6 h-6 mr-2 rounded-full" />{$userPodcast->title}{$checkMark}</span> </button> CODE_SAMPLE; } diff --git a/themes/cp_app/_persons_modal.php b/themes/cp_app/_persons_modal.php index 6822ed0118..59384b4c82 100644 --- a/themes/cp_app/_persons_modal.php +++ b/themes/cp_app/_persons_modal.php @@ -17,7 +17,7 @@ <div class="flex flex-col items-start p-4 gap-y-4"> <?php foreach ($persons as $person): ?> <div class="flex gap-x-2"> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-10 h-10 rounded-full" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-10 h-10 rounded-full" /> <div class="flex flex-col"> <h4 class="text-sm font-semibold"> <?php if ($person->information_url): ?> diff --git a/themes/cp_app/embed.php b/themes/cp_app/embed.php index 612a5f1f03..f3208bd443 100644 --- a/themes/cp_app/embed.php +++ b/themes/cp_app/embed.php @@ -20,7 +20,7 @@ </head> <body class="flex" style="background: <?= $themeData['background'] ?>; color: <?= $themeData['text'] ?>;"> - <img src="<?= $episode->image->thumbnail_url ?>" alt="<?= $episode->title ?>" class="flex-shrink w-36 h-36" /> + <img src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="flex-shrink w-36 h-36" /> <div class="flex flex-col items-start flex-1 min-w-0 px-4 pt-4 h-36"> <a href="https://castopod.org/" class="absolute top-0 right-0 mt-1 mr-2 text-2xl text-pine-500 hover:opacity-75" title="<?= lang('Common.powered_by', [ 'castopod' => 'Castopod', diff --git a/themes/cp_app/episode/_layout.php b/themes/cp_app/episode/_layout.php index 813c198387..3fd93685ad 100644 --- a/themes/cp_app/episode/_layout.php +++ b/themes/cp_app/episode/_layout.php @@ -40,7 +40,7 @@ ]) ?>"> <?= icon('arrow-left', 'mr-2 text-lg') ?> <div class="inline-flex items-center gap-x-2"> - <img class="w-8 h-8 rounded-full" src="<?= $episode->podcast->image->thumbnail_url ?>" alt="<?= $episode->podcast->title ?>" /> + <img class="w-8 h-8 rounded-full" src="<?= $episode->podcast->cover->tiny_url ?>" alt="<?= $episode->podcast->title ?>" /> <div class="flex flex-col"> <span class="text-sm font-semibold leading-none"><?= $episode->podcast->title ?></span> <span class="text-xs"><?= lang('Podcast.followers', [ @@ -69,10 +69,10 @@ </div> </nav> <header class="relative z-50 flex flex-col col-start-2 px-8 pt-8 pb-4 overflow-hidden bg-pine-500 gap-y-4"> - <div class="absolute top-0 left-0 w-full h-full bg-center bg-no-repeat bg-cover blur-lg mix-blend-luminosity" style="background-image: url('<?= $episode->podcast->image->thumbnail_url ?>');"></div> + <div class="absolute top-0 left-0 w-full h-full bg-center bg-no-repeat bg-cover blur-lg mix-blend-soft-light" style="background-image: url('<?= $episode->podcast->banner->small_url ?>');"></div> <div class="absolute top-0 left-0 w-full h-full bg-gradient-to-t from-pine-800 to-transparent"></div> <div class="z-10 flex flex-col items-start gap-y-2 gap-x-4 sm:flex-row"> - <img src="<?= $episode->image->medium_url ?>" alt="<?= $episode->title ?>" loading="lazy" class="rounded-md h-36" /> + <img src="<?= $episode->cover->medium_url ?>" alt="<?= $episode->title ?>" loading="lazy" class="rounded-md h-36" /> <div class="flex flex-col items-start text-white"> <?= episode_numbering($episode->number, $episode->season_number, 'bg-pine-50 text-sm leading-none font-semibold text-gray-700 border !no-underline border-pine-100', true) ?> <h1 class="inline-flex items-baseline max-w-md mt-2 text-2xl font-bold leading-none sm:text-3xl font-display line-clamp-2"><?= $episode->title ?></h1> @@ -82,7 +82,7 @@ <div class="inline-flex flex-row-reverse"> <?php $i = 0; ?> <?php foreach ($episode->persons as $person): ?> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> <?php $i++; if ($i === 3) { break; }?> @@ -102,7 +102,7 @@ <div class="z-10 inline-flex items-center text-white gap-x-4"> <play-episode-button id="<?= $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/_partials/card.php b/themes/cp_app/episode/_partials/card.php index bb43155926..27b3181183 100644 --- a/themes/cp_app/episode/_partials/card.php +++ b/themes/cp_app/episode/_partials/card.php @@ -3,7 +3,7 @@ <time class="absolute px-1 text-xs font-semibold text-white rounded bottom-2 right-2 bg-black/50" datetime="PT<?= $episode->audio_file_duration ?>S"> <?= format_duration($episode->audio_file_duration) ?> </time> - <img loading="lazy" src="<?= $episode->image + <img loading="lazy" src="<?= $episode->cover ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="object-cover w-20 h-20 rounded-lg" /> </div> <div class="flex items-center flex-1 gap-x-4"> @@ -16,7 +16,7 @@ </div> <play-episode-button id="<?= $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/_partials/preview_card.php b/themes/cp_app/episode/_partials/preview_card.php index a9dfe3b9cf..04a91b5bcd 100644 --- a/themes/cp_app/episode/_partials/preview_card.php +++ b/themes/cp_app/episode/_partials/preview_card.php @@ -4,7 +4,7 @@ <?= format_duration($episode->audio_file_duration) ?> </time> <img - src="<?= $episode->image->thumbnail_url ?>" + src="<?= $episode->cover->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24"/> </div> <div class="flex flex-col flex-1 px-4 py-2"> @@ -17,7 +17,7 @@ <play-episode-button class="mr-4" id="<?= $index . '_' . $episode->id ?>" - imageSrc="<?= $episode->image->thumbnail_url ?>" + imageSrc="<?= $episode->cover->thumbnail_url ?>" title="<?= $episode->title ?>" podcast="<?= $episode->podcast->title ?>" src="<?= $episode->audio_file_web_url ?>" diff --git a/themes/cp_app/episode/activity.php b/themes/cp_app/episode/activity.php index 6c5ca3a125..97a23ac5b8 100644 --- a/themes/cp_app/episode/activity.php +++ b/themes/cp_app/episode/activity.php @@ -9,11 +9,10 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->image->large_url ?>" /> +<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> <meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> + ->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta property="og:description" content="$description" /> <meta property="article:published_time" content="<?= $episode->published_at ?>" /> <meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> @@ -23,7 +22,7 @@ <link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> <meta name="twitter:title" content="<?= $episode->title ?>" /> <meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->image->large_url ?>" /> +<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> <meta name="twitter:card" content="player" /> <meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> <meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> diff --git a/themes/cp_app/episode/comments.php b/themes/cp_app/episode/comments.php index 33e9c9b472..873e166874 100644 --- a/themes/cp_app/episode/comments.php +++ b/themes/cp_app/episode/comments.php @@ -11,11 +11,10 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $episode->image->large_url ?>" /> +<meta property="og:image" content="<?= $episode->cover->large_url ?>" /> <meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> + ->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta property="og:description" content="$description" /> <meta property="article:published_time" content="<?= $episode->published_at ?>" /> <meta property="article:modified_time" content="<?= $episode->updated_at ?>" /> @@ -25,7 +24,7 @@ <link rel="alternate" type="text/xml+oembed" href="<?= base_url(route_to('episode-oembed-xml', $podcast->handle, $episode->slug)) ?>" title="<?= $episode->title ?> oEmbed xml" /> <meta name="twitter:title" content="<?= $episode->title ?>" /> <meta name="twitter:description" content="<?= $episode->description ?>" /> -<meta name="twitter:image" content="<?= $episode->image->large_url ?>" /> +<meta name="twitter:image" content="<?= $episode->cover->large_url ?>" /> <meta name="twitter:card" content="player" /> <meta property="twitter:audio:partner" content="<?= $podcast->publisher ?>" /> <meta property="twitter:audio:artist_name" content="<?= $podcast->owner_name ?>" /> diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php index 0852e85c05..c4328a6733 100644 --- a/themes/cp_app/home.php +++ b/themes/cp_app/home.php @@ -52,7 +52,7 @@ <article class="text-white"> <div class="absolute bottom-0 left-0 z-10 w-full h-full backdrop-gradient"></div> <div class="w-full h-full overflow-hidden"> - <img alt="<?= $podcast->title ?>" src="<?= $podcast->image->medium_url ?>" class="object-cover w-full h-full transition duration-200 ease-in-out transform group-focus:scale-105 group-hover:scale-105" /> + <img alt="<?= $podcast->title ?>" src="<?= $podcast->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" /> </div> <div class="absolute bottom-0 left-0 z-20 px-4 pb-2"> <h2 class="font-bold leading-none truncate font-display"><?= $podcast->title ?></h2> diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php index 8a9c9eea16..3162a72747 100644 --- a/themes/cp_app/podcast/_layout.php +++ b/themes/cp_app/podcast/_layout.php @@ -34,9 +34,10 @@ </div> <?php endif; ?> - <header class="z-50 flex flex-col-reverse justify-between w-full col-start-2 bg-top bg-no-repeat bg-cover sm:flex-row sm:items-end bg-pine-800 aspect-[3/1]" style="background-image: url('<?= $podcast->actor->cover_image_url ?>');"> - <div class="flex items-center pl-4 -mb-6 md:pl-8 md:-mb-8 gap-x-4"> - <img src="<?= $podcast->image->thumbnail_url ?>" alt="<?= $podcast->title ?>" loading="lazy" class="h-24 rounded-full md:h-28 ring-4 ring-white" /> + <header class="relative z-50 flex flex-col-reverse justify-between w-full col-start-2 bg-top bg-no-repeat bg-cover sm:flex-row sm:items-end bg-pine-800 aspect-[3/1]" style="background-image: url('<?= $podcast->banner->medium_url ?>');"> + <div class="absolute bottom-0 left-0 w-full h-full backdrop-gradient-pine"></div> + <div class="z-10 flex items-center pl-4 -mb-6 md:pl-8 md:-mb-8 gap-x-4"> + <img src="<?= $podcast->cover->thumbnail_url ?>" alt="<?= $podcast->title ?>" loading="lazy" class="h-24 rounded-full md:h-28 ring-4 ring-white" /> <div class="relative flex flex-col text-white -top-2"> <h1 class="text-lg font-bold leading-none line-clamp-2 md:leading-none md:text-2xl font-display"><?= $podcast->title ?><span class="ml-1 font-sans text-base font-normal">@<?= $podcast->handle ?></span></h1> <span class="text-xs"><?= lang('Podcast.followers', [ @@ -44,7 +45,7 @@ ]) ?></span> </div> </div> - <div class="inline-flex items-center self-end mt-2 mb-2 mr-2 gap-x-2"> + <div class="z-10 inline-flex items-center self-end mt-2 mr-2 sm:mb-4 sm:mr-4 gap-x-2"> <?php if (in_array(true, array_column($podcast->fundingPlatforms, 'is_visible'), true)): ?> <IconButton glyph="heart" variant="accent" data-toggle="funding-links" data-toggle-class="hidden"><?= lang('Podcast.sponsor') . lang('Podcast.sponsor_title') ?></IconButton> <?php endif; ?> diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php index 1c80116ca4..5ffffba3e5 100644 --- a/themes/cp_app/podcast/about.php +++ b/themes/cp_app/podcast/about.php @@ -19,11 +19,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite') @@ -55,7 +53,7 @@ <div class="inline-flex flex-row-reverse"> <?php $i = 0; ?> <?php foreach ($podcast->persons as $person): ?> - <img src="<?= $person->image->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> + <img src="<?= $person->avatar->thumbnail_url ?>" alt="<?= $person->full_name ?>" class="object-cover w-8 h-8 -ml-5 border-2 rounded-full border-pine-100 last:ml-0" /> <?php $i++; if ($i === 3) { break; }?> diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php index 3743d8859b..c62f367452 100644 --- a/themes/cp_app/podcast/activity.php +++ b/themes/cp_app/podcast/activity.php @@ -17,11 +17,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite') diff --git a/themes/cp_app/podcast/episodes.php b/themes/cp_app/podcast/episodes.php index 8b6bae0fe4..1bd6924c95 100644 --- a/themes/cp_app/podcast/episodes.php +++ b/themes/cp_app/podcast/episodes.php @@ -17,11 +17,9 @@ <meta property="og:locale" content="<?= $podcast->language_code ?>" /> <meta property="og:site_name" content="<?= $podcast->title ?>" /> <meta property="og:url" content="<?= current_url() ?>" /> -<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> -<meta property="og:image:width" content="<?= config('Images') - ->largeSize ?>" /> -<meta property="og:image:height" content="<?= config('Images') - ->largeSize ?>" /> +<meta property="og:image" content="<?= $podcast->cover->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images')->podcastCoverSizes['large'][0] ?>" /> +<meta property="og:image:height" content="<?= config('Images')->podcastCoverSizes['large'][1] ?>" /> <meta name="twitter:card" content="summary_large_image" /> <?= service('vite') -- GitLab