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 522400a867ec81f29ca221925dab7035e92b3c39..5804c4603468b94a074c8c7f6e875682af964958 100644 --- a/app/Database/Migrations/2020-06-05-170000_add_episodes.php +++ b/app/Database/Migrations/2020-06-05-170000_add_episodes.php @@ -147,10 +147,6 @@ class AddEpisodes extends Migration 'updated_at' => [ 'type' => 'DATETIME', ], - 'deleted_at' => [ - 'type' => 'DATETIME', - 'null' => true, - ], ]); $this->forge->addPrimaryKey('id'); $this->forge->addUniqueKey(['podcast_id', 'slug']); diff --git a/app/Database/Seeds/AuthSeeder.php b/app/Database/Seeds/AuthSeeder.php index dff5c8eb87821b4c3c2dad133d8b9edf726bb5db..889ab902e8843b8908b4c31224a0865d32e3581e 100644 --- a/app/Database/Seeds/AuthSeeder.php +++ b/app/Database/Seeds/AuthSeeder.php @@ -207,12 +207,6 @@ class AuthSeeder extends Seeder ], [ 'name' => 'delete', - 'description' => - 'Delete an episode of a podcast without removing it from the database', - 'has_permission' => ['podcast_admin'], - ], - [ - 'name' => 'delete_permanently', 'description' => 'Delete all occurrences of an episode of a podcast from the database', 'has_permission' => ['podcast_admin'], diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index c706585c0b625a45edc9775c317b48b552f82b26..32223685175dba4c16bdfd167ebebd7495508659 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -78,7 +78,6 @@ use RuntimeException; * @property Time|null $published_at; * @property Time $created_at; * @property Time $updated_at; - * @property Time|null $deleted_at; * * @property Person[] $persons; * @property Soundbite[] $soundbites; diff --git a/app/Entities/Media/BaseMedia.php b/app/Entities/Media/BaseMedia.php index a45ede67af978e73c62a211013086556729bc084..2acf221d9fb9dc8ee14e770e8bf78138f046bd42 100644 --- a/app/Entities/Media/BaseMedia.php +++ b/app/Entities/Media/BaseMedia.php @@ -10,6 +10,8 @@ declare(strict_types=1); namespace App\Entities\Media; +use App\Models\MediaModel; +use CodeIgniter\Database\BaseResult; use CodeIgniter\Entity\Entity; use CodeIgniter\Files\File; @@ -101,9 +103,15 @@ class BaseMedia extends Entity return $this; } - public function deleteFile(): void + public function deleteFile(): bool { helper('media'); - unlink(media_path($this->file_path)); + return unlink(media_path($this->file_path)); + } + + public function delete(): bool|BaseResult + { + $mediaModel = new MediaModel(); + return $mediaModel->delete($this->id, true); } } diff --git a/app/Entities/Media/Image.php b/app/Entities/Media/Image.php index e57426c091d531fdfcceee06c15631c99cc9d81d..c1524806113e9942c12e3228cb0d2202006f2b34 100644 --- a/app/Entities/Media/Image.php +++ b/app/Entities/Media/Image.php @@ -69,11 +69,13 @@ class Image extends BaseMedia return $this; } - public function deleteFile(): void + public function deleteFile(): bool { - parent::deleteFile(); + if (parent::deleteFile()) { + return $this->deleteSizes(); + } - $this->deleteSizes(); + return false; } public function saveSizes(): void @@ -89,12 +91,16 @@ class Image extends BaseMedia } } - private function deleteSizes(): void + private function deleteSizes(): bool { // delete all derived sizes foreach (array_keys($this->sizes) as $name) { $pathProperty = $name . '_path'; - unlink(media_path($this->{$pathProperty})); + if (! unlink(media_path($this->{$pathProperty}))) { + return false; + } } + + return true; } } diff --git a/app/Entities/Media/Transcript.php b/app/Entities/Media/Transcript.php index a0dbebdf303fb69118e1f541368e75626fb0ec1d..988e24889710f4a2e765e82034ec492ed2a43c88 100644 --- a/app/Entities/Media/Transcript.php +++ b/app/Entities/Media/Transcript.php @@ -63,12 +63,16 @@ class Transcript extends BaseMedia return $this; } - public function deleteFile(): void + public function deleteFile(): bool { - parent::deleteFile(); + if (! parent::deleteFile()) { + return false; + } if ($this->json_path) { - unlink($this->json_path); + return unlink($this->json_path); } + + return true; } } diff --git a/app/Entities/Podcast.php b/app/Entities/Podcast.php index a54b7f62927f0f78abca4d0d42eaaa078444a825..b943ce8b4a2dd7e5ae1add7ba71b03764a328853 100644 --- a/app/Entities/Podcast.php +++ b/app/Entities/Podcast.php @@ -327,6 +327,18 @@ class Podcast extends Entity return $this->episodes; } + /** + * Returns the podcast's episodes count + */ + public function getEpisodesCount(): int|string + { + if ($this->id === null) { + throw new RuntimeException('Podcast must be created before getting number of episodes.'); + } + + return (new EpisodeModel())->getPodcastEpisodesCount($this->id); + } + /** * Returns the podcast's persons * diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 573b6e7694e9056587a6de00e2463a2ae580e77c..3b20c07ba9baffa5e2e3eb412c007fc0851905ba 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -94,11 +94,6 @@ class EpisodeModel extends Model */ protected $returnType = Episode::class; - /** - * @var bool - */ - protected $useSoftDeletes = true; - /** * @var bool */ @@ -249,6 +244,18 @@ class EpisodeModel extends Model return $found; } + /** + * Returns number of episodes of a podcast + */ + public function getPodcastEpisodesCount(int $podcastId): int|string + { + return $this + ->where([ + 'podcast_id' => $podcastId, + ]) + ->countAllResults(); + } + /** * Returns the timestamp difference in seconds between the next episode to publish and the current timestamp Returns * false if there's no episode to publish diff --git a/app/Views/Components/Button.php b/app/Views/Components/Button.php index 79c96ac5fdc7931b4f1bf08d7ad409f21dcc9a2b..2c2429b3f0bdb49772fe7c455214ca11718e1276 100644 --- a/app/Views/Components/Button.php +++ b/app/Views/Components/Button.php @@ -38,6 +38,7 @@ class Button extends Component 'danger' => 'text-white bg-red-600 hover:bg-red-700', 'warning' => 'text-black bg-yellow-500 hover:bg-yellow-600', 'info' => 'text-white bg-blue-500 hover:bg-blue-600', + 'disabled' => 'text-black bg-gray-300 cursor-not-allowed', ]; $sizeClass = [ diff --git a/modules/Admin/Controllers/EpisodeController.php b/modules/Admin/Controllers/EpisodeController.php index 6b1373cf17b3cf7b0fdef304fa944a5e05ad06f9..9521c8381d1da42cd41b61ff79ae5e2123a88b82 100644 --- a/modules/Admin/Controllers/EpisodeController.php +++ b/modules/Admin/Controllers/EpisodeController.php @@ -131,6 +131,18 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } + if ((new EpisodeModel()) + ->where([ + 'slug' => $this->request->getPost('slug'), + 'podcast_id' => $this->podcast->id, + ]) + ->first()) { + return redirect() + ->back() + ->withInput() + ->with('error', lang('Episode.messages.sameSlugError')); + } + $db = db_connect(); $db->transStart(); @@ -681,6 +693,11 @@ class EpisodeController extends BaseController ->with('errors', $episodeModel->errors()); } + // set podcast is_published_on_hubs to false to trigger websub push + (new PodcastModel())->update($this->episode->podcast->id, [ + 'is_published_on_hubs' => false, + ]); + $db->transComplete(); return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); @@ -715,43 +732,74 @@ class EpisodeController extends BaseController ->with('errors', $this->validator->getErrors()); } + if ($this->episode->published_at !== null) { + return redirect() + ->back() + ->withInput() + ->with('error', lang('Episode.messages.deletePublishedEpisodeError')); + } + + $audio = $this->episode->audio; + $db = db_connect(); $db->transStart(); - $allPostsLinkedToEpisode = (new PostModel()) - ->where([ - 'episode_id' => $this->episode->id, - ]) - ->findAll(); - foreach ($allPostsLinkedToEpisode as $post) { - (new PostModel())->removePost($post); + $episodeModel = new EpisodeModel(); + + if (! $episodeModel->delete($this->episode->id)) { + $db->transRollback(); + return redirect() + ->back() + ->withInput() + ->with('errors', $episodeModel->errors()); } - // set podcast is_published_on_hubs to false to trigger websub push - (new PodcastModel())->update($this->episode->podcast->id, [ - 'is_published_on_hubs' => false, - ]); + $episodeMediaList = [$this->episode->transcript, $this->episode->chapters, $audio]; - $episodeModel = new EpisodeModel(); - if ($this->episode->published_at !== null) { - // if episode is published, set episode published_at to null to unpublish before deletion - $this->episode->published_at = null; + //only delete episode cover if different from podcast's + if ($this->episode->cover_id !== null) { + $episodeMediaList[] = $this->episode->cover; + } - if (! $episodeModel->update($this->episode->id, $this->episode)) { + //delete episode media records from database + foreach ($episodeMediaList as $episodeMedia) { + if ($episodeMedia !== null && ! $episodeMedia->delete()) { $db->transRollback(); return redirect() ->back() ->withInput() - ->with('errors', $episodeModel->errors()); + ->with('error', lang('Episode.messages.deleteError', [ + 'type' => $episodeMedia->type, + ])); } } - $episodeModel->delete($this->episode->id); + $warnings = []; + + //remove episode media files from disk + foreach ($episodeMediaList as $episodeMedia) { + if ($episodeMedia !== null && ! $episodeMedia->deleteFile()) { + $warnings[] = lang('Episode.messages.deleteFileError', [ + 'type' => $episodeMedia->type, + 'file_path' => $episodeMedia->file_path, + ]); + } + } $db->transComplete(); - return redirect()->route('episode-list', [$this->podcast->id]); + if ($warnings !== []) { + return redirect() + ->route('episode-list', [$this->podcast->id]) + ->with('message', lang('Episode.messages.deleteSuccess')) + ->with('warnings', $warnings); + } + + return redirect()->route('episode-list', [$this->podcast->id])->with( + 'message', + lang('Episode.messages.deleteSuccess') + ); } public function embed(): string diff --git a/modules/Admin/Language/en/Episode.php b/modules/Admin/Language/en/Episode.php index 82ed40dc7546dc79a09da219c1c3ed6c2fb37125..718bca1f933b651f4e5c467cc5e7952f639be5a1 100644 --- a/modules/Admin/Language/en/Episode.php +++ b/modules/Admin/Language/en/Episode.php @@ -47,6 +47,24 @@ return [ 'createSuccess' => 'Episode has been successfully created!', 'editSuccess' => 'Episode has been successfully updated!', 'publishCancelSuccess' => 'Episode publication successfully cancelled!', + 'unpublishBeforeDeleteTip' => 'You must unpublish the episode before deleting it.', + 'deletePublishedEpisodeError' => 'Please unpublish the episode before deleting it.', + 'deleteSuccess' => 'Episode successfully deleted!', + 'deleteError' => 'Failed to delete episode {type, select, + transcript {transcript} + chapters {chapters} + image {cover} + audio {audio} + other {media} + }.', + 'deleteFileError' => 'Failed to delete {type, select, + transcript {transcript} + chapters {chapters} + image {cover} + audio {audio} + other {media} + } file {file_path}. You must manually remove it from your disk.', + 'sameSlugError' => 'An episode with the chosen slug already exists.', ], 'form' => [ 'file_size_error' => @@ -147,7 +165,7 @@ return [ ], 'delete_form' => [ 'disclaimer' => - "Deleting the episode will delete all the posts associated with it and remove it from the podcast's RSS feed.", + "Deleting the episode will delete all media files, comments, video clips and soundbites associated with it.", 'understand' => 'I understand, I want to delete the episode', 'submit' => 'Delete', ], diff --git a/modules/Admin/Language/fr/Episode.php b/modules/Admin/Language/fr/Episode.php index ee1ba07ad8137c4606f67b8dd57868593660537a..4b8d039fd91a263f35f77042cef234ae0e47d33a 100644 --- a/modules/Admin/Language/fr/Episode.php +++ b/modules/Admin/Language/fr/Episode.php @@ -147,7 +147,7 @@ return [ ], 'delete_form' => [ 'disclaimer' => - "Supprimer l’épisode supprimera toutes les publications qui lui sont associées et le retirera du flux RSS du podcast.", + "Supprimer l’épisode supprimera tous les fichiers multimédia, commentaires, extraits vidéos et extraits sonores qui lui sont associés.", 'understand' => 'Je comprends, je veux supprimer l’épisode', 'submit' => 'Supprimer', ], diff --git a/modules/Auth/Database/Seeds/AuthSeeder.php b/modules/Auth/Database/Seeds/AuthSeeder.php index 87e2bfcdae702c4ea3a73093b667cd790ea80a61..cb4c819aa15b19c56b9cd66e9357d43f64af0ccf 100644 --- a/modules/Auth/Database/Seeds/AuthSeeder.php +++ b/modules/Auth/Database/Seeds/AuthSeeder.php @@ -195,12 +195,6 @@ class AuthSeeder extends Seeder ], [ 'name' => 'delete', - 'description' => - 'Delete an episode of a podcast without removing it from the database', - 'has_permission' => ['podcast_admin'], - ], - [ - 'name' => 'delete_permanently', 'description' => 'Delete all occurrences of an episode of a podcast from the database', 'has_permission' => ['podcast_admin'], diff --git a/modules/WebSub/Database/Migrations/2022-03-07-180000_add_is_published_on_hubs_to_podcasts.php b/modules/WebSub/Database/Migrations/2022-03-07-180000_add_is_published_on_hubs_to_podcasts.php index 523ffdfdfd445852d7724f9949eee41688240d03..fb3d68ca7fa7efd9ab871c0b742276f43b9be4ee 100644 --- a/modules/WebSub/Database/Migrations/2022-03-07-180000_add_is_published_on_hubs_to_podcasts.php +++ b/modules/WebSub/Database/Migrations/2022-03-07-180000_add_is_published_on_hubs_to_podcasts.php @@ -28,8 +28,6 @@ class AddIsPublishedOnHubsToPodcasts extends Migration public function down(): void { - $prefix = $this->db->getPrefix(); - - $this->forge->dropColumn($prefix . 'podcasts', 'is_published_on_hubs'); + $this->forge->dropColumn('podcasts', 'is_published_on_hubs'); } } diff --git a/modules/WebSub/Database/Migrations/2022-03-07-181500_add_is_published_on_hubs_to_episodes.php b/modules/WebSub/Database/Migrations/2022-03-07-181500_add_is_published_on_hubs_to_episodes.php index 3bb719661ddfd77ac6858f7ae36d35b85f0f3818..dfe92a08bedd20958582dc75291a5d3c02d21906 100644 --- a/modules/WebSub/Database/Migrations/2022-03-07-181500_add_is_published_on_hubs_to_episodes.php +++ b/modules/WebSub/Database/Migrations/2022-03-07-181500_add_is_published_on_hubs_to_episodes.php @@ -28,8 +28,6 @@ class AddIsPublishedOnHubsToEpisodes extends Migration public function down(): void { - $prefix = $this->db->getPrefix(); - - $this->forge->dropColumn($prefix . 'episodes', 'is_published_on_hubs'); + $this->forge->dropColumn('episodes', 'is_published_on_hubs'); } } diff --git a/themes/cp_admin/_message_block.php b/themes/cp_admin/_message_block.php index 1504aa372c71b16280bc6967d34588142527f3e4..8e3ee6df5c3c6316b3e7ae86044c620b94f60bf7 100644 --- a/themes/cp_admin/_message_block.php +++ b/themes/cp_admin/_message_block.php @@ -17,3 +17,13 @@ if (session()->has('message')): ?> </ul> </Alert> <?php endif; ?> + +<?php if (session()->has('warnings')): ?> + <Alert variant="warning" class="mb-4"> + <ul> + <?php foreach (session('warnings') as $warning): ?> + <li><?= esc($warning) ?></li> + <?php endforeach; ?> + </ul> + </Alert> +<?php endif; ?> diff --git a/themes/cp_admin/episode/_card.php b/themes/cp_admin/episode/_card.php index 543d45e0f28568252df419c8e5f41ac79f84c030..0d675fe901902a54f2333d70a1f4b09194d28389 100644 --- a/themes/cp_admin/episode/_card.php +++ b/themes/cp_admin/episode/_card.php @@ -11,7 +11,7 @@ </div> </a> <button class="absolute top-0 right-0 z-10 p-2 mt-2 mr-2 text-white transition -translate-y-12 rounded-full opacity-0 focus:ring-accent focus:opacity-100 focus:-translate-y-0 group-hover:translate-y-0 bg-black/50 group-hover:opacity-100" id="more-dropdown-<?= $episode->id ?>" data-dropdown="button" data-dropdown-target="more-dropdown-<?= $episode->id ?>-menu" aria-haspopup="true" aria-expanded="false" title="<?= lang('Common.more') ?>"><?= icon('more') ?></button> - <DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode([ + <?php $items = [ [ 'type' => 'link', 'title' => lang('Episode.go_to_page'), @@ -45,11 +45,24 @@ [ 'type' => 'separator', ], - [ + ]; + if ($episode->published_at === null) { + $items[] = [ 'type' => 'link', 'title' => lang('Episode.delete'), 'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id), 'class' => 'font-semibold text-red-600', - ], - ])) ?>" /> + ]; + } else { + $label = lang('Episode.delete'); + $icon = icon('forbid', 'mr-2'); + $title = lang('Episode.messages.unpublishBeforeDeleteTip'); + $items[] = [ + 'type' => 'html', + 'content' => esc(<<<CODE_SAMPLE + <span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}{$label}</span> + CODE_SAMPLE), + ]; + } ?> + <DropdownMenu id="more-dropdown-<?= $episode->id ?>-menu" labelledby="more-dropdown-<?= $episode->id ?>" offsetY="-32" items="<?= esc(json_encode($items)) ?>" /> </article> \ No newline at end of file diff --git a/themes/cp_admin/episode/edit.php b/themes/cp_admin/episode/edit.php index 5611b60b0a3e4466406177556110c4c0fc7116c3..9191635d31632e6d149b941b90a8e7fd0366120e 100644 --- a/themes/cp_admin/episode/edit.php +++ b/themes/cp_admin/episode/edit.php @@ -278,7 +278,11 @@ </form> -<Button class="mt-8" variant="danger" uri="<?= route_to('episode-delete', $podcast->id, $episode->id) ?>" iconLeft="delete-bin"><?= lang('Episode.delete') ?></Button> +<?php if ($episode->published_at === null): ?> + <Button class="mt-8" variant="danger" uri="<?= route_to('episode-delete', $podcast->id, $episode->id) ?>" iconLeft="delete-bin"><?= lang('Episode.delete') ?></Button> +<?php else: ?> + <Button class="mt-8" variant="disabled" iconLeft="forbid" data-tooltip="right" title="<?= lang('Episode.messages.unpublishBeforeDeleteTip') ?>"><?= lang('Episode.delete') ?></Button> +<?php endif ?> <?= $this->endSection() ?> diff --git a/themes/cp_admin/episode/list.php b/themes/cp_admin/episode/list.php index b491ea9613df525a8eb5f5157d96b92d99e4e364..a7d2762bccfc047f0caae38f944ec710a0d87631 100644 --- a/themes/cp_admin/episode/list.php +++ b/themes/cp_admin/episode/list.php @@ -73,50 +73,63 @@ [ 'header' => lang('Episode.list.actions'), 'cell' => function ($episode, $podcast) { + $items = [ + [ + 'type' => 'link', + 'title' => lang('Episode.go_to_page'), + 'uri' => route_to('episode', esc($podcast->handle), esc($episode->slug)), + ], + [ + 'type' => 'link', + 'title' => lang('Episode.edit'), + 'uri' => route_to('episode-edit', $podcast->id, $episode->id), + ], + [ + 'type' => 'link', + 'title' => lang('Episode.embed.title'), + 'uri' => route_to('embed-add', $podcast->id, $episode->id), + ], + [ + 'type' => 'link', + 'title' => lang('Person.persons'), + 'uri' => route_to('episode-persons-manage', $podcast->id, $episode->id), + ], + [ + 'type' => 'link', + 'title' => lang('VideoClip.list.title'), + 'uri' => route_to('video-clips-list', $episode->podcast->id, $episode->id), + ], + [ + 'type' => 'link', + 'title' => lang('Soundbite.list.title'), + 'uri' => route_to('soundbites-list', $podcast->id, $episode->id), + ], + [ + 'type' => 'separator', + ], + ]; + if ($episode->published_at === null) { + $items[] = [ + 'type' => 'link', + 'title' => lang('Episode.delete'), + 'uri' => route_to('episode-delete', $podcast->id, $episode->id), + 'class' => 'font-semibold text-red-600', + ]; + } else { + $label = lang('Episode.delete'); + $icon = icon('forbid'); + $title = lang('Episode.messages.unpublishBeforeDeleteTip'); + $items[] = [ + 'type' => 'html', + 'content' => esc(<<<CODE_SAMPLE + <span class="inline-flex items-center px-4 py-1 font-semibold text-gray-400 cursor-not-allowed" data-tooltip="bottom" title="{$title}">{$icon}<span class="ml-2">{$label}</span></span> + CODE_SAMPLE), + ]; + } return '<button id="more-dropdown-' . $episode->id . '" type="button" class="inline-flex items-center p-1 rounded-full focus:ring-accent" data-dropdown="button" data-dropdown-target="more-dropdown-' . $episode->id . '-menu" aria-haspopup="true" aria-expanded="false">' . icon('more') . '</button>' . - '<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labelledby="more-dropdown-' . $episode->id . '" offsetY="-24" items="' . esc(json_encode([ - [ - 'type' => 'link', - 'title' => lang('Episode.go_to_page'), - 'uri' => route_to('episode', esc($podcast->handle), esc($episode->slug)), - ], - [ - 'type' => 'link', - 'title' => lang('Episode.edit'), - 'uri' => route_to('episode-edit', $podcast->id, $episode->id), - ], - [ - 'type' => 'link', - 'title' => lang('Episode.embed.title'), - 'uri' => route_to('embed-add', $podcast->id, $episode->id), - ], - [ - 'type' => 'link', - 'title' => lang('Person.persons'), - 'uri' => route_to('episode-persons-manage', $podcast->id, $episode->id), - ], - [ - 'type' => 'link', - 'title' => lang('VideoClip.list.title'), - 'uri' => route_to('video-clips-list', $episode->podcast->id, $episode->id), - ], - [ - 'type' => 'link', - 'title' => lang('Soundbite.list.title'), - 'uri' => route_to('soundbites-list', $podcast->id, $episode->id), - ], - [ - 'type' => 'separator', - ], - [ - 'type' => 'link', - 'title' => lang('Episode.delete'), - 'uri' => route_to('episode-delete', $podcast->id, $episode->id), - 'class' => 'font-semibold text-red-600', - ], - ])) . '" />'; + '<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labelledby="more-dropdown-' . $episode->id . '" offsetY="-24" items="' . esc(json_encode($items)) . '" />'; }, ], ], diff --git a/themes/cp_admin/podcast/_sidebar.php b/themes/cp_admin/podcast/_sidebar.php index dadafde8749b4b04e7343e43f802e3b140b49f1c..619e1f4cf7036b06f203c88a59f94d2ad725b217 100644 --- a/themes/cp_admin/podcast/_sidebar.php +++ b/themes/cp_admin/podcast/_sidebar.php @@ -33,7 +33,13 @@ $podcastNavigation = [ 'platforms-funding', ], ], -]; ?> +]; + +$counts = [ + 'episode-list' => $podcast->getEpisodesCount(), +]; + +?> <div class="flex items-center px-4 py-2 border-b border-navigation"> <img @@ -65,13 +71,19 @@ $podcastNavigation = [ <ul class="flex flex-col"> <?php foreach ($data['items'] as $item): ?> <?php $isActive = url_is(route_to($item, $podcast->id)); ?> + <?php + $itemLabel = lang('PodcastNavigation.' . $item); + if (array_key_exists($item, $counts)) { + $itemLabel .= ' (' . $counts[$item] . ')'; + } + ?> <li class="inline-flex"> <a class="w-full py-1 pl-14 pr-2 text-sm hover:opacity-100 focus:ring-inset focus:ring-accent <?= $isActive ? 'font-semibold opacity-100 inline-flex items-center' : 'opacity-75' ?>" href="<?= route_to( $item, $podcast->id, - ) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang('PodcastNavigation.' . $item) ?></a> + ) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . $itemLabel ?></a> </li> <?php endforeach; ?> </ul>