Commit 7fdea63d authored by Yassine Doghri's avatar Yassine Doghri
Browse files

fix(persons): set person picture as optional for better ux

- use default avatar image if person image is not set
- add thumbnail and medium default avatar
images
- set default avatar images directly in public/media folder
- remove public/media's root
folder from .gitignore
- remove unnecessary copy:images script and cpy-cli package

closes #125
parent 0dd3b7e0
......@@ -144,8 +144,6 @@ public/*
!public/robots.txt
# public media folder
public/media/*
!public/media/index.html
!public/media/podcasts
!public/media/persons
......
......@@ -21,11 +21,11 @@ class ActivityPub extends ActivityPubBase
* Default avatar and cover images
* --------------------------------------------------------------------
*/
public string $defaultAvatarImagePath = 'assets/images/castopod-avatar-default.jpg';
public string $defaultAvatarImagePath = 'media/castopod-avatar-default_thumbnail.jpg';
public string $defaultAvatarImageMimetype = 'image/jpeg';
public string $defaultCoverImagePath = 'assets/images/castopod-cover-default.jpg';
public string $defaultCoverImagePath = 'media/castopod-cover-default.jpg';
public string $defaultCoverImageMimetype = 'image/jpeg';
}
......@@ -81,11 +81,15 @@ class PersonController extends BaseController
'full_name' => $this->request->getPost('full_name'),
'unique_name' => $this->request->getPost('unique_name'),
'information_url' => $this->request->getPost('information_url'),
'image' => new Image($this->request->getFile('image')),
'created_by' => user_id(),
'updated_by' => user_id(),
]);
$imageFile = $this->request->getFile('image');
if ($imageFile !== null && $imageFile->isValid()) {
$person->image = new Image($imageFile);
}
$personModel = new PersonModel();
if (! $personModel->insert($person)) {
......@@ -129,6 +133,7 @@ class PersonController extends BaseController
$this->person->full_name = $this->request->getPost('full_name');
$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);
......
......@@ -45,12 +45,14 @@ class AddPersons extends Migration
'image_path' => [
'type' => 'VARCHAR',
'constraint' => 255,
'null' => true,
],
// constraint is 13 because the longest safe mimetype for images is image/svg+xml,
// see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#image_types
'image_mimetype' => [
'type' => 'VARCHAR',
'constraint' => 13,
'null' => true,
],
'created_by' => [
'type' => 'INT',
......
......@@ -47,8 +47,8 @@ class Person extends Entity
'full_name' => 'string',
'unique_name' => 'string',
'information_url' => '?string',
'image_path' => 'string',
'image_mimetype' => 'string',
'image_path' => '?string',
'image_mimetype' => '?string',
'podcast_id' => '?integer',
'episode_id' => '?integer',
'created_by' => 'integer',
......@@ -58,8 +58,12 @@ class Person extends Entity
/**
* Saves a picture in `public/media/persons/`
*/
public function setImage(Image $image): static
public function setImage(?Image $image = null): static
{
if ($image === null) {
return $this;
}
helper('media');
// Save image
......@@ -73,6 +77,10 @@ class Person extends Entity
public function getImage(): Image
{
if ($this->attributes['image_path'] === null) {
return new Image(null, '/castopod-avatar-default.jpg', 'image/jpeg');
}
return new Image(null, $this->attributes['image_path'], $this->attributes['image_mimetype']);
}
......
......@@ -134,7 +134,7 @@ if (! function_exists('get_rss_feed')) {
$podcastNamespace,
);
$personElement->addAttribute('img', $person->image->large_url);
$personElement->addAttribute('img', $person->image->medium_url);
if ($person->information_url !== null) {
$personElement->addAttribute('href', $person->information_url);
......@@ -298,7 +298,7 @@ if (! function_exists('get_rss_feed')) {
htmlspecialchars(lang("PersonsTaxonomy.persons.{$role->group}.label", [], 'en')),
);
$personElement->addAttribute('img', $person->image->large_url);
$personElement->addAttribute('img', $person->image->medium_url);
if ($person->information_url !== null) {
$personElement->addAttribute('href', $person->information_url);
......
......@@ -19,6 +19,9 @@ return [
'form' => [
'identity_section_title' => 'Identity',
'identity_section_subtitle' => 'Who is working on the podcast',
'image' => 'Picture',
'image_size_hint' =>
'Image 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',
......@@ -26,9 +29,6 @@ return [
'information_url' => 'Information URL',
'information_url_hint' =>
'Url to a relevant resource of information about the person, such as a homepage or third-party profile platform.',
'image' => 'Picture, avatar, image',
'image_size_hint' =>
'Image must be squared with at least 400px wide and tall.',
'submit_create' => 'Create person',
'submit_edit' => 'Save person',
],
......
......@@ -19,6 +19,9 @@ return [
'form' => [
'identity_section_title' => 'Identité',
'identity_section_subtitle' => 'Qui intervient sur le podcast',
'image' => 'Photo',
'image_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',
'unique_name' => 'Nom unique',
......@@ -26,9 +29,6 @@ return [
'information_url' => 'Adresse d’information',
'information_url_hint' =>
'URL pointant vers des informations relatives à l’intervenant, telle qu’une page personnelle ou une page de profil sur une plateforme tierce.',
'image' => 'Photo, avatar, image',
'image_size_hint' =>
'L’image doit être carrée et avoir au moins 400px de largeur et de hauteur.',
'submit_create' => 'Créer l’intervenant',
'submit_edit' => 'Enregistrer l’intervenant',
],
......
......@@ -63,7 +63,6 @@ class PersonModel extends Model
'full_name' => 'required',
'unique_name' =>
'required|regex_match[/^[a-z0-9\-]{1,191}$/]|is_unique[persons.unique_name,id,{id}]',
'image_path' => 'required',
'created_by' => 'required',
'updated_by' => 'required',
];
......
......@@ -22,6 +22,18 @@
lang('Person.form.identity_section_subtitle'),
) ?>
<?= form_label(lang('Person.form.image'), 'image') ?>
<?= form_input([
'id' => 'image',
'name' => 'image',
'class' => 'form-input',
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
]) ?>
<small class="mb-4 text-gray-600"><?= lang(
'Person.form.image_size_hint',
) ?></small>
<?= form_label(
lang('Person.form.full_name'),
'full_name',
......@@ -66,19 +78,6 @@
'value' => old('information_url'),
]) ?>
<?= form_label(lang('Person.form.image'), 'image') ?>
<?= form_input([
'id' => 'image',
'name' => 'image',
'class' => 'form-input',
'required' => 'required',
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
]) ?>
<small class="mb-4 text-gray-600"><?= lang(
'Person.form.image_size_hint',
) ?></small>
<?= form_section_close() ?>
<?= button(
......
......@@ -23,6 +23,18 @@
"<img src=\"{$person->image->thumbnail_url}\" alt=\"{$person->full_name}\" class=\"object-cover w-32 h-32 mt-3 rounded\" />",
) ?>
<?= form_label(lang('Person.form.image'), 'image') ?>
<?= form_input([
'id' => 'image',
'name' => 'image',
'class' => 'form-input',
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
]) ?>
<small class="mb-4 text-gray-600"><?= lang(
'Person.form.image_size_hint',
) ?></small>
<?= form_label(
lang('Person.form.full_name'),
'full_name',
......@@ -67,18 +79,6 @@
'value' => old('information_url', $person->information_url),
]) ?>
<?= form_label(lang('Person.form.image'), 'image') ?>
<?= form_input([
'id' => 'image',
'name' => 'image',
'class' => 'form-input',
'type' => 'file',
'accept' => '.jpg,.jpeg,.png',
]) ?>
<small class="mb-4 text-gray-600"><?= lang(
'Person.form.image_size_hint',
) ?></small>
<?= form_section_close() ?>
<?= button(
......
......@@ -211,13 +211,12 @@ You do not wish to use the VSCode devcontainer? No problem!
3. Generate static assets:
```bash
# build all assets at once
# build all static assets at once
npm run build:static
# generate/copy specific assets
# build specific assets
npm run build:icons
npm run build:svg
npm run copy:images
```
> **Note:**
......
This diff is collapsed.
......@@ -12,10 +12,9 @@
"dev": "vite",
"build": "tsc && vite build",
"serve": "vite preview",
"build:static": "npm run build:icons && npm run build:svg && npm run copy:images",
"build:static": "npm run build:icons && npm run build:svg",
"build:icons": "svgo -f app/Resources/icons -o public/assets/icons -r --config=./.svgo.icons.js",
"build:svg": "svgo -f app/Resources/images -o public/assets/images -r --config=./.svgo.js",
"copy:images": "cpy app/Resources/images/*.jpg public/assets/images",
"lint": "eslint --ext js,ts app/Resources",
"lint:fix": "eslint --ext js,ts app/Resources --fix",
"lint:css": "stylelint \"app/Resources/**/*.css\"",
......@@ -55,7 +54,6 @@
"@types/prosemirror-view": "^1.18.0",
"@typescript-eslint/eslint-plugin": "^4.28.5",
"@typescript-eslint/parser": "^4.28.5",
"cpy-cli": "^3.1.1",
"cross-env": "^7.0.3",
"cssnano": "^5.0.7",
"cz-conventional-changelog": "^3.3.0",
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment