mirror of
https://code.castopod.org/adaures/castopod
synced 2025-04-19 13:01:19 +00:00
feat(episode): add form to allow editing episode's publication date to a past date
This allows podcasters to reorganize their published episodes as they see fit closes #97
This commit is contained in:
parent
94c0b7c159
commit
d783d16eb7
4
app/Resources/icons/history.svg
Normal file
4
app/Resources/icons/history.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<path fill="none" d="M0 0H24V24H0z"/>
|
||||
<path d="M12 2c5.523 0 10 4.477 10 10s-4.477 10-10 10S2 17.523 2 12h2c0 4.418 3.582 8 8 8s8-3.582 8-8-3.582-8-8-8C9.536 4 7.332 5.114 5.865 6.865L8 9H2V3l2.447 2.446C6.28 3.336 8.984 2 12 2zm1 5v4.585l3.243 3.243-1.415 1.415L11 12.413V7h2z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 357 B |
@ -4,27 +4,28 @@ declare(strict_types=1);
|
||||
|
||||
namespace App\Views\Components;
|
||||
|
||||
use ViewComponents\Component;
|
||||
|
||||
class IconButton extends Component
|
||||
class IconButton extends Button
|
||||
{
|
||||
public string $glyph = '';
|
||||
|
||||
public function render(): string
|
||||
public function __construct(array $attributes)
|
||||
{
|
||||
$attributes = [
|
||||
$iconButtonAttributes = [
|
||||
'isSquared' => 'true',
|
||||
'title' => $this->slot,
|
||||
'title' => $attributes['slot'],
|
||||
'data-tooltip' => 'bottom',
|
||||
];
|
||||
|
||||
$attributes = array_merge($attributes, $this->attributes);
|
||||
$glyphSize = [
|
||||
'small' => 'text-sm',
|
||||
'base' => 'text-lg',
|
||||
'large' => 'text-2xl',
|
||||
];
|
||||
|
||||
$attributes['slot'] = icon($this->glyph);
|
||||
$allAttributes = array_merge($attributes, $iconButtonAttributes);
|
||||
|
||||
unset($attributes['glyph']);
|
||||
parent::__construct($allAttributes);
|
||||
|
||||
$iconButton = new Button($attributes);
|
||||
return $iconButton->render();
|
||||
$this->slot = icon($this->glyph, $glyphSize[$this->size]);
|
||||
}
|
||||
}
|
||||
|
@ -327,6 +327,23 @@ $routes->group(
|
||||
'permission:podcast-manage_publications',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'publish-date-edit',
|
||||
'EpisodeController::publishDateEdit/$1/$2',
|
||||
[
|
||||
'as' => 'episode-publish_date_edit',
|
||||
'filter' =>
|
||||
'permission:podcast-manage_publications',
|
||||
],
|
||||
);
|
||||
$routes->post(
|
||||
'publish-date-edit',
|
||||
'EpisodeController::attemptPublishDateEdit/$1/$2',
|
||||
[
|
||||
'filter' =>
|
||||
'permission:podcast-manage_publications',
|
||||
],
|
||||
);
|
||||
$routes->get(
|
||||
'unpublish',
|
||||
'EpisodeController::unpublish/$1/$2',
|
||||
|
@ -683,29 +683,104 @@ class EpisodeController extends BaseController
|
||||
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]);
|
||||
}
|
||||
|
||||
public function unpublish(): string | RedirectResponse
|
||||
public function publishDateEdit(): string|RedirectResponse
|
||||
{
|
||||
if ($this->episode->publication_status === 'published') {
|
||||
helper(['form']);
|
||||
// only accessible if episode is already published
|
||||
if ($this->episode->publication_status !== 'published') {
|
||||
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
|
||||
'error',
|
||||
lang('Episode.publish_date_edit_error')
|
||||
);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'episode' => $this->episode,
|
||||
];
|
||||
helper('form');
|
||||
|
||||
replace_breadcrumb_params([
|
||||
0 => $this->podcast->title,
|
||||
1 => $this->episode->title,
|
||||
]);
|
||||
return view('episode/unpublish', $data);
|
||||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'episode' => $this->episode,
|
||||
];
|
||||
|
||||
replace_breadcrumb_params([
|
||||
0 => $this->podcast->title,
|
||||
1 => $this->episode->title,
|
||||
]);
|
||||
|
||||
return view('episode/publish_date_edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set an episode's publication date to a past date
|
||||
*
|
||||
* Prevents setting a future date as it does not make sense to set a future published date to an already published
|
||||
* episode. This also prevents any side-effects from occurring.
|
||||
*/
|
||||
public function attemptPublishDateEdit(): RedirectResponse
|
||||
{
|
||||
$rules = [
|
||||
'new_publication_date' => 'valid_date[Y-m-d H:i]',
|
||||
];
|
||||
|
||||
if (! $this->validate($rules)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $this->validator->getErrors());
|
||||
}
|
||||
|
||||
$newPublicationDate = $this->request->getPost('new_publication_date');
|
||||
|
||||
$newPublicationDate = Time::createFromFormat(
|
||||
'Y-m-d H:i',
|
||||
$newPublicationDate,
|
||||
$this->request->getPost('client_timezone'),
|
||||
)->setTimezone(app_timezone());
|
||||
|
||||
if ($newPublicationDate->isAfter(Time::now())) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('error', lang('Episode.publish_date_edit_future_error'));
|
||||
}
|
||||
|
||||
$this->episode->published_at = $newPublicationDate;
|
||||
|
||||
$episodeModel = new EpisodeModel();
|
||||
if (! $episodeModel->update($this->episode->id, $this->episode)) {
|
||||
return redirect()
|
||||
->back()
|
||||
->withInput()
|
||||
->with('errors', $episodeModel->errors());
|
||||
}
|
||||
|
||||
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
|
||||
'error',
|
||||
lang('Episode.unpublish_error')
|
||||
'message',
|
||||
lang('Episode.publish_date_edit_success')
|
||||
);
|
||||
}
|
||||
|
||||
public function unpublish(): string | RedirectResponse
|
||||
{
|
||||
if ($this->episode->publication_status !== 'published') {
|
||||
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with(
|
||||
'error',
|
||||
lang('Episode.unpublish_error')
|
||||
);
|
||||
}
|
||||
|
||||
helper(['form']);
|
||||
|
||||
$data = [
|
||||
'podcast' => $this->podcast,
|
||||
'episode' => $this->episode,
|
||||
];
|
||||
|
||||
replace_breadcrumb_params([
|
||||
0 => $this->podcast->title,
|
||||
1 => $this->episode->title,
|
||||
]);
|
||||
return view('episode/unpublish', $data);
|
||||
}
|
||||
|
||||
public function attemptUnpublish(): RedirectResponse
|
||||
{
|
||||
$rules = [
|
||||
|
@ -25,6 +25,7 @@ return [
|
||||
'persons' => 'persons',
|
||||
'publish' => 'publish',
|
||||
'publish-edit' => 'edit publication',
|
||||
'publish-date-edit' => 'edit publication date',
|
||||
'unpublish' => 'unpublish',
|
||||
'delete' => 'delete',
|
||||
'fediverse' => 'fediverse',
|
||||
|
@ -24,10 +24,14 @@ return [
|
||||
'edit' => 'Edit',
|
||||
'publish' => 'Publish',
|
||||
'publish_edit' => 'Edit publication',
|
||||
'publish_date_edit' => 'Edit publication date',
|
||||
'unpublish' => 'Unpublish',
|
||||
'publish_error' => 'Episode is already published.',
|
||||
'publish_edit_error' => 'Episode is already published.',
|
||||
'publish_cancel_error' => 'Episode is already published.',
|
||||
'publish_date_edit_error' => 'Episode has not been published yet, you cannot edit its publication date.',
|
||||
'publish_date_edit_future_error' => 'Episode\'s publication date can only be set to a past date! If you would like to reschedule it, unpublish it first.',
|
||||
'publish_date_edit_success' => 'Episode\'s publication date has been updated successfully!',
|
||||
'unpublish_error' => 'Episode is not published.',
|
||||
'delete' => 'Delete',
|
||||
'go_to_page' => 'Go to page',
|
||||
@ -178,6 +182,11 @@ return [
|
||||
'message_warning_hint' => 'Having a message increases social engagement, resulting in a better visibility for your episode.',
|
||||
'message_warning_submit' => 'Publish anyways',
|
||||
],
|
||||
'publish_date_edit_form' => [
|
||||
'new_publication_date' => 'New publication date',
|
||||
'new_publication_date_hint' => 'Must be set to a past date.',
|
||||
'submit' => 'Edit publication date',
|
||||
],
|
||||
'unpublish_form' => [
|
||||
'disclaimer' =>
|
||||
"Unpublishing the episode will delete all the comments and posts associated with it and remove it from the podcast's RSS feed.",
|
||||
|
@ -1,3 +1,9 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
$isPodcastArea = isset($podcast) && ! isset($episode);
|
||||
$isEpisodeArea = isset($podcast) && isset($episode);
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="<?= service('request')
|
||||
->getLocale() ?>">
|
||||
@ -32,9 +38,9 @@
|
||||
<?= render_breadcrumb('text-xs items-center flex') ?>
|
||||
<div class="flex justify-between py-1">
|
||||
<div class="flex flex-wrap items-center">
|
||||
<?php if ((isset($episode) && $episode->is_premium) || (isset($podcast) && $podcast->is_premium)): ?>
|
||||
<?php if (($isEpisodeArea && $episode->is_premium) || ($isPodcastArea && $podcast->is_premium)): ?>
|
||||
<div class="inline-flex items-center">
|
||||
<IconButton uri="<?= route_to('subscription-list', $podcast->id) ?>" glyph="exchange-dollar" variant="secondary" class="p-0 mr-2 text-4xl border-0"><?= isset($episode) ? lang('PremiumPodcasts.episode_is_premium') : lang('PremiumPodcasts.podcast_is_premium') ?></IconButton>
|
||||
<IconButton uri="<?= route_to('subscription-list', $podcast->id) ?>" glyph="exchange-dollar" variant="secondary" size="large" class="p-0 mr-2 border-0"><?= ($isEpisodeArea && $episode->is_premium) ? lang('PremiumPodcasts.episode_is_premium') : lang('PremiumPodcasts.podcast_is_premium') ?></IconButton>
|
||||
<Heading tagName="h1" size="large" class="truncate"><?= $this->renderSection('pageTitle') ?></Heading>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
@ -42,11 +48,11 @@
|
||||
<?php endif; ?>
|
||||
<?= $this->renderSection('headerLeft') ?>
|
||||
</div>
|
||||
<div class="flex flex-shrink-0 gap-x-2"><?= $this->renderSection('headerRight') ?></div>
|
||||
<div class="flex items-center flex-shrink-0 gap-x-2"><?= $this->renderSection('headerRight') ?></div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<?php if (isset($podcast) && $podcast->publication_status !== 'published'): ?>
|
||||
<?php if ($isPodcastArea && $podcast->publication_status !== 'published'): ?>
|
||||
<?= publication_status_banner($podcast->published_at, $podcast->id, $podcast->publication_status) ?>
|
||||
<?php endif ?>
|
||||
<div class="px-2 py-8 mx-auto md:px-12">
|
||||
|
@ -1,8 +1,14 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
$isPodcastArea = isset($podcast) && ! isset($episode);
|
||||
$isEpisodeArea = isset($podcast) && isset($episode);
|
||||
?>
|
||||
|
||||
<div data-sidebar-toggler="backdrop" role="button" tabIndex="0" aria-label="<?= lang('Common.close') ?>" class="fixed z-50 hidden w-full h-full bg-gray-800/75 md:hidden"></div>
|
||||
<aside data-sidebar-toggler="sidebar" data-toggle-class="-translate-x-full" data-hide-class="-translate-x-full" class="h-full max-h-[calc(100vh-40px)] sticky z-50 flex flex-col row-start-2 col-start-1 text-white transition duration-200 ease-in-out transform -translate-x-full border-r top-10 border-navigation bg-navigation md:translate-x-0">
|
||||
<?php if (isset($podcast) && isset($episode)): ?>
|
||||
<?php if ($isEpisodeArea): ?>
|
||||
<?= $this->include('episode/_sidebar') ?>
|
||||
<?php elseif (isset($podcast)): ?>
|
||||
<?php elseif ($isPodcastArea): ?>
|
||||
<?= $this->include('podcast/_sidebar') ?>
|
||||
<?php else: ?>
|
||||
<?= $this->include('_sidebar') ?>
|
||||
|
38
themes/cp_admin/episode/publish_date_edit.php
Normal file
38
themes/cp_admin/episode/publish_date_edit.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?= $this->extend('_layout') ?>
|
||||
|
||||
<?= $this->section('title') ?>
|
||||
<?= lang('Episode.publish_date_edit') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('pageTitle') ?>
|
||||
<?= lang('Episode.publish_date_edit') ?>
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('content') ?>
|
||||
|
||||
<?= anchor(
|
||||
route_to('episode-view', $podcast->id, $episode->id),
|
||||
icon('arrow-left', 'mr-2 text-lg') . lang('Episode.publish_form.back_to_episode_dashboard'),
|
||||
[
|
||||
'class' => 'inline-flex items-center font-semibold mr-4 text-sm',
|
||||
],
|
||||
) ?>
|
||||
|
||||
<form action="<?= route_to('episode-publish_date_edit', $podcast->id, $episode->id) ?>" method="POST" class="flex flex-col items-start w-full max-w-lg mx-auto mt-4" data-submit="validate-message">
|
||||
<?= csrf_field() ?>
|
||||
<input type="hidden" name="client_timezone" value="UTC" />
|
||||
|
||||
<Forms.Field
|
||||
as="DatetimePicker"
|
||||
name="new_publication_date"
|
||||
label="<?= lang('Episode.publish_date_edit_form.new_publication_date') ?>"
|
||||
hint="<?= lang('Episode.publish_date_edit_form.new_publication_date_hint') ?>"
|
||||
value="<?= $episode->published_at ?>"
|
||||
required="true"
|
||||
/>
|
||||
|
||||
<Button variant="primary" type="submit" class="mt-4"><?= lang('Episode.publish_date_edit_form.submit') ?></Button>
|
||||
|
||||
</form>
|
||||
|
||||
<?= $this->endSection() ?>
|
@ -17,6 +17,14 @@
|
||||
<?= $this->endSection() ?>
|
||||
|
||||
<?= $this->section('headerRight') ?>
|
||||
<?php if ($episode->publication_status === 'published'): ?>
|
||||
<IconButton
|
||||
uri="<?= route_to('episode-publish_date_edit', $podcast->id, $episode->id) ?>"
|
||||
glyph="history"
|
||||
variant="secondary"
|
||||
glyphClass="text-xl"
|
||||
><?= lang('Episode.publish_date_edit') ?></IconButton>
|
||||
<?php endif; ?>
|
||||
<?= publication_button(
|
||||
$podcast->id,
|
||||
$episode->id,
|
||||
|
Loading…
x
Reference in New Issue
Block a user