mirror of
https://code.castopod.org/adaures/castopod
synced 2025-06-06 18:31:05 +00:00
feat(episodes): replace soft delete with permanent delete
+ add constraint to prevent deleting an episode when published
This commit is contained in:
parent
0345728739
commit
eb9ff522c2
@ -147,10 +147,6 @@ class AddEpisodes extends Migration
|
|||||||
'updated_at' => [
|
'updated_at' => [
|
||||||
'type' => 'DATETIME',
|
'type' => 'DATETIME',
|
||||||
],
|
],
|
||||||
'deleted_at' => [
|
|
||||||
'type' => 'DATETIME',
|
|
||||||
'null' => true,
|
|
||||||
],
|
|
||||||
]);
|
]);
|
||||||
$this->forge->addPrimaryKey('id');
|
$this->forge->addPrimaryKey('id');
|
||||||
$this->forge->addUniqueKey(['podcast_id', 'slug']);
|
$this->forge->addUniqueKey(['podcast_id', 'slug']);
|
||||||
|
@ -207,12 +207,6 @@ class AuthSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'delete',
|
'name' => 'delete',
|
||||||
'description' =>
|
|
||||||
'Delete an episode of a podcast without removing it from the database',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete_permanently',
|
|
||||||
'description' =>
|
'description' =>
|
||||||
'Delete all occurrences of an episode of a podcast from the database',
|
'Delete all occurrences of an episode of a podcast from the database',
|
||||||
'has_permission' => ['podcast_admin'],
|
'has_permission' => ['podcast_admin'],
|
||||||
|
@ -78,7 +78,6 @@ use RuntimeException;
|
|||||||
* @property Time|null $published_at;
|
* @property Time|null $published_at;
|
||||||
* @property Time $created_at;
|
* @property Time $created_at;
|
||||||
* @property Time $updated_at;
|
* @property Time $updated_at;
|
||||||
* @property Time|null $deleted_at;
|
|
||||||
*
|
*
|
||||||
* @property Person[] $persons;
|
* @property Person[] $persons;
|
||||||
* @property Soundbite[] $soundbites;
|
* @property Soundbite[] $soundbites;
|
||||||
|
@ -10,6 +10,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Entities\Media;
|
namespace App\Entities\Media;
|
||||||
|
|
||||||
|
use App\Models\MediaModel;
|
||||||
|
use CodeIgniter\Database\BaseResult;
|
||||||
use CodeIgniter\Entity\Entity;
|
use CodeIgniter\Entity\Entity;
|
||||||
use CodeIgniter\Files\File;
|
use CodeIgniter\Files\File;
|
||||||
|
|
||||||
@ -101,9 +103,15 @@ class BaseMedia extends Entity
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteFile(): void
|
public function deleteFile(): bool
|
||||||
{
|
{
|
||||||
helper('media');
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,11 +69,13 @@ class Image extends BaseMedia
|
|||||||
return $this;
|
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
|
public function saveSizes(): void
|
||||||
@ -89,12 +91,16 @@ class Image extends BaseMedia
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function deleteSizes(): void
|
private function deleteSizes(): bool
|
||||||
{
|
{
|
||||||
// delete all derived sizes
|
// delete all derived sizes
|
||||||
foreach (array_keys($this->sizes) as $name) {
|
foreach (array_keys($this->sizes) as $name) {
|
||||||
$pathProperty = $name . '_path';
|
$pathProperty = $name . '_path';
|
||||||
unlink(media_path($this->{$pathProperty}));
|
if (! unlink(media_path($this->{$pathProperty}))) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,12 +63,16 @@ class Transcript extends BaseMedia
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function deleteFile(): void
|
public function deleteFile(): bool
|
||||||
{
|
{
|
||||||
parent::deleteFile();
|
if (! parent::deleteFile()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->json_path) {
|
if ($this->json_path) {
|
||||||
unlink($this->json_path);
|
return unlink($this->json_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,6 +327,18 @@ class Podcast extends Entity
|
|||||||
return $this->episodes;
|
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
|
* Returns the podcast's persons
|
||||||
*
|
*
|
||||||
|
@ -94,11 +94,6 @@ class EpisodeModel extends Model
|
|||||||
*/
|
*/
|
||||||
protected $returnType = Episode::class;
|
protected $returnType = Episode::class;
|
||||||
|
|
||||||
/**
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
protected $useSoftDeletes = true;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
@ -249,6 +244,18 @@ class EpisodeModel extends Model
|
|||||||
return $found;
|
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
|
* 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
|
* false if there's no episode to publish
|
||||||
|
@ -38,6 +38,7 @@ class Button extends Component
|
|||||||
'danger' => 'text-white bg-red-600 hover:bg-red-700',
|
'danger' => 'text-white bg-red-600 hover:bg-red-700',
|
||||||
'warning' => 'text-black bg-yellow-500 hover:bg-yellow-600',
|
'warning' => 'text-black bg-yellow-500 hover:bg-yellow-600',
|
||||||
'info' => 'text-white bg-blue-500 hover:bg-blue-600',
|
'info' => 'text-white bg-blue-500 hover:bg-blue-600',
|
||||||
|
'disabled' => 'text-black bg-gray-300 cursor-not-allowed',
|
||||||
];
|
];
|
||||||
|
|
||||||
$sizeClass = [
|
$sizeClass = [
|
||||||
|
@ -131,6 +131,18 @@ class EpisodeController extends BaseController
|
|||||||
->with('errors', $this->validator->getErrors());
|
->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 = db_connect();
|
||||||
$db->transStart();
|
$db->transStart();
|
||||||
|
|
||||||
@ -681,6 +693,11 @@ class EpisodeController extends BaseController
|
|||||||
->with('errors', $episodeModel->errors());
|
->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();
|
$db->transComplete();
|
||||||
|
|
||||||
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
|
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
|
||||||
@ -715,43 +732,74 @@ class EpisodeController extends BaseController
|
|||||||
->with('errors', $this->validator->getErrors());
|
->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 = db_connect();
|
||||||
|
|
||||||
$db->transStart();
|
$db->transStart();
|
||||||
|
|
||||||
$allPostsLinkedToEpisode = (new PostModel())
|
|
||||||
->where([
|
|
||||||
'episode_id' => $this->episode->id,
|
|
||||||
])
|
|
||||||
->findAll();
|
|
||||||
foreach ($allPostsLinkedToEpisode as $post) {
|
|
||||||
(new PostModel())->removePost($post);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set podcast is_published_on_hubs to false to trigger websub push
|
|
||||||
(new PodcastModel())->update($this->episode->podcast->id, [
|
|
||||||
'is_published_on_hubs' => false,
|
|
||||||
]);
|
|
||||||
|
|
||||||
$episodeModel = new EpisodeModel();
|
$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;
|
|
||||||
|
|
||||||
if (! $episodeModel->update($this->episode->id, $this->episode)) {
|
if (! $episodeModel->delete($this->episode->id)) {
|
||||||
$db->transRollback();
|
$db->transRollback();
|
||||||
return redirect()
|
return redirect()
|
||||||
->back()
|
->back()
|
||||||
->withInput()
|
->withInput()
|
||||||
->with('errors', $episodeModel->errors());
|
->with('errors', $episodeModel->errors());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$episodeMediaList = [$this->episode->transcript, $this->episode->chapters, $audio];
|
||||||
|
|
||||||
|
//only delete episode cover if different from podcast's
|
||||||
|
if ($this->episode->cover_id !== null) {
|
||||||
|
$episodeMediaList[] = $this->episode->cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
$episodeModel->delete($this->episode->id);
|
//delete episode media records from database
|
||||||
|
foreach ($episodeMediaList as $episodeMedia) {
|
||||||
|
if ($episodeMedia !== null && ! $episodeMedia->delete()) {
|
||||||
|
$db->transRollback();
|
||||||
|
return redirect()
|
||||||
|
->back()
|
||||||
|
->withInput()
|
||||||
|
->with('error', lang('Episode.messages.deleteError', [
|
||||||
|
'type' => $episodeMedia->type,
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$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();
|
$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
|
public function embed(): string
|
||||||
|
@ -47,6 +47,24 @@ return [
|
|||||||
'createSuccess' => 'Episode has been successfully created!',
|
'createSuccess' => 'Episode has been successfully created!',
|
||||||
'editSuccess' => 'Episode has been successfully updated!',
|
'editSuccess' => 'Episode has been successfully updated!',
|
||||||
'publishCancelSuccess' => 'Episode publication successfully cancelled!',
|
'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' => [
|
'form' => [
|
||||||
'file_size_error' =>
|
'file_size_error' =>
|
||||||
@ -147,7 +165,7 @@ return [
|
|||||||
],
|
],
|
||||||
'delete_form' => [
|
'delete_form' => [
|
||||||
'disclaimer' =>
|
'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',
|
'understand' => 'I understand, I want to delete the episode',
|
||||||
'submit' => 'Delete',
|
'submit' => 'Delete',
|
||||||
],
|
],
|
||||||
|
@ -147,7 +147,7 @@ return [
|
|||||||
],
|
],
|
||||||
'delete_form' => [
|
'delete_form' => [
|
||||||
'disclaimer' =>
|
'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',
|
'understand' => 'Je comprends, je veux supprimer l’épisode',
|
||||||
'submit' => 'Supprimer',
|
'submit' => 'Supprimer',
|
||||||
],
|
],
|
||||||
|
@ -195,12 +195,6 @@ class AuthSeeder extends Seeder
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => 'delete',
|
'name' => 'delete',
|
||||||
'description' =>
|
|
||||||
'Delete an episode of a podcast without removing it from the database',
|
|
||||||
'has_permission' => ['podcast_admin'],
|
|
||||||
],
|
|
||||||
[
|
|
||||||
'name' => 'delete_permanently',
|
|
||||||
'description' =>
|
'description' =>
|
||||||
'Delete all occurrences of an episode of a podcast from the database',
|
'Delete all occurrences of an episode of a podcast from the database',
|
||||||
'has_permission' => ['podcast_admin'],
|
'has_permission' => ['podcast_admin'],
|
||||||
|
@ -28,8 +28,6 @@ class AddIsPublishedOnHubsToPodcasts extends Migration
|
|||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
$prefix = $this->db->getPrefix();
|
$this->forge->dropColumn('podcasts', 'is_published_on_hubs');
|
||||||
|
|
||||||
$this->forge->dropColumn($prefix . 'podcasts', 'is_published_on_hubs');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,8 +28,6 @@ class AddIsPublishedOnHubsToEpisodes extends Migration
|
|||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
{
|
{
|
||||||
$prefix = $this->db->getPrefix();
|
$this->forge->dropColumn('episodes', 'is_published_on_hubs');
|
||||||
|
|
||||||
$this->forge->dropColumn($prefix . 'episodes', 'is_published_on_hubs');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,3 +17,13 @@ if (session()->has('message')): ?>
|
|||||||
</ul>
|
</ul>
|
||||||
</Alert>
|
</Alert>
|
||||||
<?php endif; ?>
|
<?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; ?>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</a>
|
</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>
|
<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',
|
'type' => 'link',
|
||||||
'title' => lang('Episode.go_to_page'),
|
'title' => lang('Episode.go_to_page'),
|
||||||
@ -45,11 +45,24 @@
|
|||||||
[
|
[
|
||||||
'type' => 'separator',
|
'type' => 'separator',
|
||||||
],
|
],
|
||||||
[
|
];
|
||||||
|
if ($episode->published_at === null) {
|
||||||
|
$items[] = [
|
||||||
'type' => 'link',
|
'type' => 'link',
|
||||||
'title' => lang('Episode.delete'),
|
'title' => lang('Episode.delete'),
|
||||||
'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id),
|
'uri' => route_to('episode-delete', $episode->podcast->id, $episode->id),
|
||||||
'class' => 'font-semibold text-red-600',
|
'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>
|
</article>
|
@ -278,7 +278,11 @@
|
|||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<?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>
|
<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() ?>
|
<?= $this->endSection() ?>
|
||||||
|
@ -73,10 +73,7 @@
|
|||||||
[
|
[
|
||||||
'header' => lang('Episode.list.actions'),
|
'header' => lang('Episode.list.actions'),
|
||||||
'cell' => function ($episode, $podcast) {
|
'cell' => function ($episode, $podcast) {
|
||||||
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">' .
|
$items = [
|
||||||
icon('more') .
|
|
||||||
'</button>' .
|
|
||||||
'<DropdownMenu id="more-dropdown-' . $episode->id . '-menu" labelledby="more-dropdown-' . $episode->id . '" offsetY="-24" items="' . esc(json_encode([
|
|
||||||
[
|
[
|
||||||
'type' => 'link',
|
'type' => 'link',
|
||||||
'title' => lang('Episode.go_to_page'),
|
'title' => lang('Episode.go_to_page'),
|
||||||
@ -110,13 +107,29 @@
|
|||||||
[
|
[
|
||||||
'type' => 'separator',
|
'type' => 'separator',
|
||||||
],
|
],
|
||||||
[
|
];
|
||||||
|
if ($episode->published_at === null) {
|
||||||
|
$items[] = [
|
||||||
'type' => 'link',
|
'type' => 'link',
|
||||||
'title' => lang('Episode.delete'),
|
'title' => lang('Episode.delete'),
|
||||||
'uri' => route_to('episode-delete', $podcast->id, $episode->id),
|
'uri' => route_to('episode-delete', $podcast->id, $episode->id),
|
||||||
'class' => 'font-semibold text-red-600',
|
'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($items)) . '" />';
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
@ -33,7 +33,13 @@ $podcastNavigation = [
|
|||||||
'platforms-funding',
|
'platforms-funding',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
]; ?>
|
];
|
||||||
|
|
||||||
|
$counts = [
|
||||||
|
'episode-list' => $podcast->getEpisodesCount(),
|
||||||
|
];
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
<div class="flex items-center px-4 py-2 border-b border-navigation">
|
<div class="flex items-center px-4 py-2 border-b border-navigation">
|
||||||
<img
|
<img
|
||||||
@ -65,13 +71,19 @@ $podcastNavigation = [
|
|||||||
<ul class="flex flex-col">
|
<ul class="flex flex-col">
|
||||||
<?php foreach ($data['items'] as $item): ?>
|
<?php foreach ($data['items'] as $item): ?>
|
||||||
<?php $isActive = url_is(route_to($item, $podcast->id)); ?>
|
<?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">
|
<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
|
<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'
|
? 'font-semibold opacity-100 inline-flex items-center'
|
||||||
: 'opacity-75' ?>" href="<?= route_to(
|
: 'opacity-75' ?>" href="<?= route_to(
|
||||||
$item,
|
$item,
|
||||||
$podcast->id,
|
$podcast->id,
|
||||||
) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . lang('PodcastNavigation.' . $item) ?></a>
|
) ?>"><?= ($isActive ? icon('chevron-right', 'mr-2') : '') . $itemLabel ?></a>
|
||||||
</li>
|
</li>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</ul>
|
</ul>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user