mirror of
https://github.com/ad-aures/castopod.git
synced 2026-04-12 11:16:43 +02:00
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
This commit is contained in:
parent
5c56f3e6f0
commit
4a8147bfbb
79 changed files with 506 additions and 419 deletions
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
]);
|
||||
|
|
|
|||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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.',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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' =>
|
||||
|
|
|
|||
|
|
@ -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/).',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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.',
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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' =>
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -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/).',
|
||||
];
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue