diff --git a/app/Controllers/Admin/Episode.php b/app/Controllers/Admin/Episode.php index f88c49b277802b6e7ab2ca4de3a4c9bbe307686a..d85c58db18c731d4329e437d222ff0e3b264a8e5 100644 --- a/app/Controllers/Admin/Episode.php +++ b/app/Controllers/Admin/Episode.php @@ -117,6 +117,7 @@ class Episode extends BaseController ->with('errors', $this->validator->getErrors()); } + $publicationDate = $this->request->getPost('publication_date'); $newEpisode = new \App\Entities\Episode([ 'podcast_id' => $this->podcast->id, 'title' => $this->request->getPost('title'), @@ -141,11 +142,13 @@ class Episode extends BaseController 'is_blocked' => $this->request->getPost('block') == 'yes', 'created_by' => user(), 'updated_by' => user(), - 'published_at' => Time::createFromFormat( - 'Y-m-d H:i', - $this->request->getPost('publication_date'), - $this->request->getPost('client_timezone') - )->setTimezone('UTC'), + 'published_at' => $publicationDate + ? Time::createFromFormat( + 'Y-m-d H:i', + $publicationDate, + $this->request->getPost('client_timezone') + )->setTimezone('UTC') + : null, ]); $episodeModel = new EpisodeModel(); @@ -231,11 +234,16 @@ class Episode extends BaseController : null; $this->episode->type = $this->request->getPost('type'); $this->episode->is_blocked = $this->request->getPost('block') == 'yes'; - $this->episode->published_at = Time::createFromFormat( - 'Y-m-d H:i', - $this->request->getPost('publication_date'), - $this->request->getPost('client_timezone') - )->setTimezone('UTC'); + + $publicationDate = $this->request->getPost('publication_date'); + $this->episode->published_at = $publicationDate + ? Time::createFromFormat( + 'Y-m-d H:i', + $publicationDate, + $this->request->getPost('client_timezone') + )->setTimezone('UTC') + : null; + $this->episode->updated_by = user(); $enclosure = $this->request->getFile('enclosure'); diff --git a/app/Entities/Episode.php b/app/Entities/Episode.php index 5d240f9c2d08a52ad19b172c22fb12ddb69dc447..65c14e0f17446ace71e3e4b1649e1ae478c0ed5c 100644 --- a/app/Entities/Episode.php +++ b/app/Entities/Episode.php @@ -89,9 +89,9 @@ class Episode extends Entity protected $description; /** - * @var boolean + * @var string */ - protected $is_published; + protected $publication_status; protected $dates = [ 'published_at', @@ -462,16 +462,21 @@ class Episode extends Entity return $this; } - public function getIsPublished() + public function getPublicationStatus() { - if ($this->is_published) { - return $this->is_published; + if ($this->publication_status) { + return $this->publication_status; } - helper('date'); + if (!$this->published_at) { + return 'not_published'; + } - $this->is_published = $this->published_at->isBefore(Time::now()); + helper('date'); + if ($this->published_at->isBefore(Time::now())) { + return 'published'; + } - return $this->is_published; + return 'scheduled'; } } diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php index ae528dc4a5f92676f052505f5a31061d2f880d28..df2c00b8f98af81041f7c383c8f6fe1b589ac20e 100644 --- a/app/Helpers/components_helper.php +++ b/app/Helpers/components_helper.php @@ -271,27 +271,30 @@ if (!function_exists('publication_pill')) { */ function publication_pill( $publicationDate, - $isPublished, + $publicationStatus, $customClass = '' ): string { - $class = $isPublished - ? 'text-green-500 border-green-500' - : 'text-orange-600 border-orange-600'; - - $label = lang( - $isPublished ? 'Episode.published' : 'Episode.scheduled', - [ - '<time - pubdate - datetime="' . + $class = + $publicationStatus === 'published' + ? 'text-green-500 border-green-500' + : 'text-orange-600 border-orange-600'; + + $transParam = []; + if ($publicationDate) { + $transParam = [ + '<time pubdate datetime="' . $publicationDate->format(DateTime::ATOM) . - '" - title="' . + '" title="' . $publicationDate . '">' . lang('Common.mediumDate', [$publicationDate]) . '</time>', - ] + ]; + } + + $label = lang( + 'Episode.publication_status.' . $publicationStatus, + $transParam ); return '<span class="px-1 border ' . diff --git a/app/Language/en/Episode.php b/app/Language/en/Episode.php index 94d175c9ab255208dc4f9fb12c3275bc3ede86c0..e9886c15b584f4ba5b9307c34cb9ccc7a8e02d91 100644 --- a/app/Language/en/Episode.php +++ b/app/Language/en/Episode.php @@ -23,8 +23,11 @@ return [ 'delete' => 'Delete', 'go_to_page' => 'Go to page', 'create' => 'Add an episode', - 'published' => 'Published on {0}', - 'scheduled' => 'Scheduled for {0}', + 'publication_status' => [ + 'published' => 'Published on {0}', + 'scheduled' => 'Scheduled for {0}', + 'not_published' => 'Not published', + ], 'form' => [ 'enclosure' => 'Audio file', 'enclosure_hint' => 'Choose an .mp3 or .m4a audio file.', @@ -58,6 +61,7 @@ return [ 'publication_section_title' => 'Publication info', 'publication_section_subtitle' => '', 'publication_date' => 'Publication date', + 'publication_date_clear' => 'Clear publication date', 'publication_date_hint' => 'You can schedule the episode release by setting a future publication date. This field must be formatted as YYYY-MM-DD HH:mm', 'parental_advisory' => [ diff --git a/app/Language/fr/Episode.php b/app/Language/fr/Episode.php index 36d1daac64962d5756b243ead42d58a7ddfbb762..03c57020922efb9b2e14dd59671da9fe71ed5b81 100644 --- a/app/Language/fr/Episode.php +++ b/app/Language/fr/Episode.php @@ -23,8 +23,11 @@ return [ 'delete' => 'Supprimer', 'go_to_page' => 'Voir', 'create' => 'Ajouter un épisode', - 'published' => 'Publié le {0}', - 'scheduled' => 'Planifié pour le {0}', + 'publication_status' => [ + 'published' => 'Publié le {0}', + 'scheduled' => 'Planifié pour le {0}', + 'not_published' => 'Non publié', + ], 'form' => [ 'enclosure' => 'Fichier audio', 'enclosure_hint' => 'Sélectionnez un fichier audio .mp3 ou .m4a.', @@ -58,6 +61,7 @@ return [ 'publication_section_title' => 'Information de publication', 'publication_section_subtitle' => '', 'publication_date' => 'Date de publication', + 'publication_date_clear' => 'Effacer la date de publication', 'publication_date_hint' => 'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit être au format YYYY-MM-DD HH:mm', 'parental_advisory' => [ diff --git a/app/Views/_assets/icons/close.svg b/app/Views/_assets/icons/close.svg new file mode 100644 index 0000000000000000000000000000000000000000..0ef4f305a61e22f136a5b7c02cf8f3955a6cc639 --- /dev/null +++ b/app/Views/_assets/icons/close.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"> + <g> + <path fill="none" d="M0 0h24v24H0z"/> + <path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/> + </g> +</svg> diff --git a/app/Views/_assets/modules/DateTimePicker.ts b/app/Views/_assets/modules/DateTimePicker.ts index 235cf69f789583598738e46f7d7d15db92ee1817..a3ab215ad0d119bb9a45e58901cdf33e46f6190f 100644 --- a/app/Views/_assets/modules/DateTimePicker.ts +++ b/app/Views/_assets/modules/DateTimePicker.ts @@ -11,30 +11,36 @@ const isBrowserLocale24h = () => .match(/AM/); const DateTimePicker = (): void => { - const dateTimeContainers: NodeListOf<HTMLInputElement> = document.querySelectorAll( - "input[data-picker='datetime']" + const dateTimeContainers: NodeListOf<HTMLDivElement> = document.querySelectorAll( + "div[data-picker='datetime']" ); for (let i = 0; i < dateTimeContainers.length; i++) { const dateTimeContainer = dateTimeContainers[i]; + const dateTimeInput: HTMLInputElement | null = dateTimeContainer.querySelector( + "input[data-input]" + ); - const flatpickrInstance = flatpickr(dateTimeContainer, { - enableTime: true, - time_24hr: isBrowserLocale24h(), - }); + if (dateTimeInput) { + const flatpickrInstance = flatpickr(dateTimeContainer, { + enableTime: true, + time_24hr: isBrowserLocale24h(), + wrap: true, + }); - // convert container UTC date value to user timezone - const dateTime = new Date(dateTimeContainer.value); - const dateUTC = Date.UTC( - dateTime.getFullYear(), - dateTime.getMonth(), - dateTime.getDate(), - dateTime.getHours(), - dateTime.getMinutes() - ); + // convert container UTC date value to user timezone + const dateTime = new Date(dateTimeInput.value); + const dateUTC = Date.UTC( + dateTime.getFullYear(), + dateTime.getMonth(), + dateTime.getDate(), + dateTime.getHours(), + dateTime.getMinutes() + ); - // set converted date as field value - flatpickrInstance.setDate(new Date(dateUTC)); + // set converted date as field value + flatpickrInstance.setDate(new Date(dateUTC)); + } } }; diff --git a/app/Views/_assets/modules/Time.ts b/app/Views/_assets/modules/Time.ts index 58ea0f2695f47c72b83f8292dfc6ba2a59545a0a..17dd8cf269569461fe65b651e65d64537e2f38ae 100644 --- a/app/Views/_assets/modules/Time.ts +++ b/app/Views/_assets/modules/Time.ts @@ -3,8 +3,6 @@ const Time = (): void => { "time" ); - console.log(timeElements); - for (let i = 0; i < timeElements.length; i++) { const timeElement = timeElements[i]; diff --git a/app/Views/admin/episode/create.php b/app/Views/admin/episode/create.php index d5e39664a978247d08c8f8b1525dbbb1b1dc7656..8553a0a982f694baaa73c088295c34d2e6f15c6d 100644 --- a/app/Views/admin/episode/create.php +++ b/app/Views/admin/episode/create.php @@ -199,13 +199,20 @@ [], lang('Episode.form.publication_date_hint') ) ?> -<?= form_input([ - 'id' => 'publication_date', - 'name' => 'publication_date', - 'class' => 'form-input mb-4', - 'value' => old('publication_date', date('Y-m-d H:i')), - 'data-picker' => 'datetime', -]) ?> +<div class="flex mb-4" data-picker="datetime"> + <?= form_input([ + 'id' => 'publication_date', + 'name' => 'publication_date', + 'class' => 'form-input rounded-r-none flex-1', + 'value' => old('publication_date', date('Y-m-d H:i')), + 'data-input' => '', + ]) ?> + <button + class="p-3 bg-green-100 border border-l-0 focus:outline-none rounded-r-md hover:bg-green-200 focus:shadow-outline" + type="button" + title="<?= lang('Episode.form.publication_date_clear') ?>" + data-clear=""><?= icon('close') ?></button> +</div> <?= form_fieldset('', ['class' => 'flex mb-6 gap-1']) ?> <legend> diff --git a/app/Views/admin/episode/edit.php b/app/Views/admin/episode/edit.php index 7390bdfc60f2ba0550b73a444bcb52689a00cade..af7018023f3153dd2a09afd62ae9d4a6f3acf390 100644 --- a/app/Views/admin/episode/edit.php +++ b/app/Views/admin/episode/edit.php @@ -202,18 +202,25 @@ [], lang('Episode.form.publication_date_hint') ) ?> -<?= form_input([ - 'id' => 'publication_date', - 'name' => 'publication_date', - 'class' => 'form-input mb-4', - 'value' => old( - 'publication_date', - $episode->published_at - ? $episode->published_at->format('Y-m-d H:i') - : '' - ), - 'data-picker' => 'datetime', -]) ?> +<div class="flex mb-4" data-picker="datetime"> + <?= form_input([ + 'id' => 'publication_date', + 'name' => 'publication_date', + 'class' => 'form-input rounded-r-none flex-1', + 'value' => old( + 'publication_date', + $episode->published_at + ? $episode->published_at->format('Y-m-d H:i') + : '' + ), + 'data-input' => '', + ]) ?> + <button + class="p-3 bg-green-100 border border-l-0 focus:outline-none rounded-r-md hover:bg-green-200 focus:shadow-outline" + type="button" + title="<?= lang('Episode.form.publication_date_clear') ?>" + data-clear=""><?= icon('close') ?></button> +</div> <?= form_fieldset('', ['class' => 'mb-6']) ?> <legend> diff --git a/app/Views/admin/episode/list.php b/app/Views/admin/episode/list.php index da3170e9f07998d46a931156ee3a3ecaf957482a..78f8f4270b32f4ad2dee6ddd1dd60b6d5007951c 100644 --- a/app/Views/admin/episode/list.php +++ b/app/Views/admin/episode/list.php @@ -65,9 +65,7 @@ 'soundbites-edit', $podcast->id, $episode->id - ) ?>"><?= lang( - 'Episode.soundbites' -) ?></a> + ) ?>"><?= lang('Episode.soundbites') ?></a> <a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to( 'episode', $podcast->name, @@ -84,7 +82,7 @@ <div class="mb-2 text-xs"> <?= publication_pill( $episode->published_at, - $episode->is_published + $episode->publication_status ) ?> <span class="mx-1">•</span> <time datetime="PT<?= $episode->enclosure_duration ?>S"> diff --git a/app/Views/admin/episode/view.php b/app/Views/admin/episode/view.php index 74744e47366bdfba9474d720d673343d58111f8b..b1c001a6fe95073b277f3d90a221858ecccc98f4 100644 --- a/app/Views/admin/episode/view.php +++ b/app/Views/admin/episode/view.php @@ -8,7 +8,7 @@ <?= $episode->title . publication_pill( $episode->published_at, - $episode->is_published, + $episode->publication_status, 'text-sm ml-2 align-middle' ) ?> <?= $this->endSection() ?> diff --git a/app/Views/admin/podcast/latest_episodes.php b/app/Views/admin/podcast/latest_episodes.php index 9613909276c048de0646eee1303434df757e54ce..db3049588538bf97abf18f9b1311cde879e6583a 100644 --- a/app/Views/admin/podcast/latest_episodes.php +++ b/app/Views/admin/podcast/latest_episodes.php @@ -33,17 +33,19 @@ 'font-bold text-gray-600', true ) ?> - <span class="mx-1">•</span> - <time - pubdate - datetime="<?= $episode->published_at->format( - DateTime::ATOM - ) ?>" - title="<?= $episode->published_at ?>"> - <?= lang('Common.mediumDate', [ - $episode->published_at, - ]) ?> - </time> + <?php if ($episode->published_at): ?> + <span class="mx-1">•</span> + <time + pubdate + datetime="<?= $episode->published_at->format( + DateTime::ATOM + ) ?>" + title="<?= $episode->published_at ?>"> + <?= lang('Common.mediumDate', [ + $episode->published_at, + ]) ?> + </time> + <?php endif; ?> </div> </div> <div class="relative" data-toggle="dropdown"> diff --git a/package-lock.json b/package-lock.json index 3c3e067d847b2ac4275d3fe3f9e5c7e00c18f23c..05428113faa1ccdbd8535480a25e7e4f048e0ffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3944,9 +3944,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001077", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz", - "integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==", + "version": "1.0.30001165", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz", + "integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==", "dev": true }, "cardinal": { @@ -16679,12 +16679,6 @@ "fill-range": "^7.0.1" } }, - "caniuse-lite": { - "version": "1.0.30001142", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001142.tgz", - "integrity": "sha512-pDPpn9ankEpBFZXyCv2I4lh1v/ju+bqb78QfKf+w9XgDAFWBwSYPswXqprRdrgQWK0wQnpIbfwRjNHO1HWqvoQ==", - "dev": true - }, "chalk": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",