Compare commits

..

No commits in common. "master" and "issue-23" have entirely different histories.

218 changed files with 6787 additions and 9777 deletions

View file

@ -43,15 +43,5 @@ GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
# UWIL ADMIN SECURE
# - LIST SEPARATED WITH ',' (Required)
UWIL_ADMIN_SECURE_CIDR_LIST=""
# - LIST SEPARATED WITH ',' (Optional)
UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST=""
# SAML
SAML2_DEFAULT_TENANT_UUID=
SAML2_SP_CERT_x509=
SAML2_SP_CERT_PRIVATEKEY=
SAML2_CONTACT_TECHNICAL_EMAIL=
SAML2_CONTACT_SUPPORT_EMAIL=
# LDC Web Admin Secure
LDC_WEB_API_KEY="MY_KEY"

2
.gitignore vendored
View file

@ -6,8 +6,6 @@
/public/uploads
/public/cache
/public/sitemap.xml
/public/marker-icon.png
/public/marker-shadow.png
/storage/*.key
/vendor
.env

View file

@ -7,7 +7,7 @@ include:
file: 'security/security.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'build/docker-multistage.yml'
file: 'build/build.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'test/test.yml'

View file

@ -1,45 +1,5 @@
FROM composer:2.8 AS vendor
ARG GITLAB_USER
ARG GITLAB_PASSWORD
COPY database/ database/
COPY composer.json composer.json
COPY composer.lock composer.lock
RUN composer config gitlab-token.lab.s-web.fr ${GITLAB_USER} ${GITLAB_PASSWORD}
RUN composer install \
--ignore-platform-reqs \
--no-interaction \
--no-plugins \
--no-scripts \
--no-dev \
--prefer-dist
FROM node:20.18 AS frontend
ARG GITLAB_PASSWORD
RUN mkdir -p /app/public/build
COPY package*.json tsconfig.json vite.config.ts .eslintrc.json .stylelintrc.json /app/
COPY resources/ /app/resources/
WORKDIR /app
RUN npm config set @ldc-web:registry https://lab.s-web.fr/api/v4/packages/npm/
RUN npm config set -- '//lab.s-web.fr/api/v4/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
RUN npm ci && npm run prod
ARG PHP_8_5_TAG=latest
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/uwil-production-8.5:${PHP_8_5_TAG}
ARG USER=www-data
COPY --chown=${USER}:${USER} . /app/srcs
COPY --chown=${USER}:${USER} ./resources/favicon /app/srcs/public
COPY --from=vendor --chown=${USER}:${USER} /app/vendor/ /app/srcs/vendor/
COPY --from=frontend --chown=${USER}:${USER} /app/public/build/ /app/srcs/public/build/
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/ldcweb-production-8.2:latest
COPY --chown=www-data:www-data . /app/srcs
USER root
RUN chmod -R 755 /app/srcs/storage
USER ${USER}
USER www-data

View file

@ -15,6 +15,8 @@ php artisan schedule:run
```
ldcweb:admin-secure-sync
```
## TODO
Après le 30 juin 2024, retirer le code de la modal concernant le jeu (cf [ce commit](https://lab.s-web.fr/ldc-web/projets/lionor/-/commit/befd6848e0bb31bf9d79bac2a1f30d3015d8b14b))
### Refresh-Sitemap
#### Available Commands
```
sitemap:refresh --dry-run
```

View file

@ -15,10 +15,10 @@ class HtmlColor implements CastsAttributes
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return string
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
@ -32,10 +32,10 @@ class HtmlColor implements CastsAttributes
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return integer
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing

View file

@ -11,10 +11,10 @@ class RgbaColor extends HtmlColor
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return string
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing
@ -24,7 +24,6 @@ class RgbaColor extends HtmlColor
public function get($model, $key, $value, $attributes)
{
// phpcs:enable
/** @var \Illuminate\Support\Collection<array-key, float> */
$intValues = collect(\str_split($this->zeropad(\dechex(\intval($value)), 8), 2))->map(function ($hex) {
return intval(\hexdec($hex));
});
@ -36,10 +35,10 @@ class RgbaColor extends HtmlColor
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @param Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return integer
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Squiz.Commenting.FunctionComment.ScalarTypeHintMissing

View file

@ -0,0 +1,177 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class CreateAdmin extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend admin user';
/**
* The firstname of the new user.
*
* @var string
*/
protected $firstname;
/**
* The lastname of the new user.
*
* @var string
*/
protected $lastname;
/**
* The email of the new user.
*
* @var string
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* Execute the console command.
*
* @return integer
*/
public function handle(): int
{
$this->firstname = null;
$this->addFirstname();
$this->lastname = null;
$this->addLastname();
$this->email = null;
$this->addEmail();
$this->password = null;
$this->addPassword();
$user = new User([
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'email' => $this->email,
'password' => $this->password,
]);
$user->saveOrFail();
$this->info('User created 👌');
return 0;
}
/**
* Add a firstname to the user.
*
* @return void
*/
private function addFirstname(): void
{
while (is_null($this->firstname)) {
try {
$tmp = $this->ask('Type the wanted user firstname', 'Admin');
$validator = Validator::make(['firstname' => $tmp], [
'firstname' => 'required|min:3|max:255'
]);
$validator->validate();
$this->firstname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid firstname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a lastname to the user.
*
* @return void
*/
private function addLastname(): void
{
while (is_null($this->lastname)) {
try {
$tmp = $this->ask('Type the wanted user lastname', 'Saïtama');
$validator = Validator::make(['lastname' => $tmp], [
'lastname' => 'required|min:3|max:255'
]);
$validator->validate();
$this->lastname = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid lastname %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add an email to the user.
*
* @return void
*/
private function addEmail(): void
{
while (is_null($this->email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$this->email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a password to the user.
*
* @return void
*/
private function addPassword(): void
{
while (
($this->password = $this->secret(
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password'
)) !== $this->secret('Confirm the password')
) {
continue;
}; //end while
}
}

View file

@ -1,204 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Str;
class UserCreate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend admin user';
/**
* The first name of the new user.
*
* @var string|null
*/
protected $first_name;
/**
* The last name of the new user.
*
* @var string|null
*/
protected $last_name;
/**
* The email of the new user.
*
* @var string|null
*/
protected $email;
/**
* The role of the new user.
*
* @var string|null
*/
protected $role;
/**
* Execute the console command.
*
* @return void
*/
public function handle(): void
{
$this->first_name = null;
$this->addFirstName();
$this->last_name = null;
$this->addLastName();
$this->email = null;
$this->addEmail();
$this->role = null;
$this->addRole();
$user = new User([
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'role' => $this->role,
'published' => true,
]);
$user->saveOrFail();
$this->info('User created 👌');
}
/**
* Add a first name to the user.
*
* @return void
*/
private function addFirstName(): void
{
while (is_null($this->first_name)) {
try {
$tmp = $this->ask('Type the wanted user first name', 'Admin');
$validator = Validator::make(['first_name' => $tmp], [
'first_name' => 'required|min:3|max:255'
]);
$validator->validate();
$this->first_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid first name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a last name to the user.
*
* @return void
*/
private function addLastName(): void
{
while (is_null($this->last_name)) {
try {
$tmp = $this->ask('Type the wanted user last name', 'Saïtama');
$validator = Validator::make(['last_name' => $tmp], [
'last_name' => 'required|min:3|max:255'
]);
$validator->validate();
$this->last_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid last_name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add an email to the user.
*
* @return void
*/
private function addEmail(): void
{
while (is_null($this->email)) {
try {
$tmp = $this->ask('Type the wanted user email', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$this->email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
/**
* Add a role to the user.
*
* @return void
*/
private function addRole(): void
{
while (is_null($this->role)) {
try {
$tmp = $this->choice(
'Select his role',
[
Str::ucFirst(RoleEnum::conceptor->label()),
Str::ucFirst(RoleEnum::admin->label()),
Str::ucFirst(RoleEnum::visitor->label())
],
Str::ucFirst(RoleEnum::visitor->label()),
);
if ($tmp === Str::ucFirst(RoleEnum::conceptor->label())) {
$tmp = RoleEnum::conceptor;
}
if ($tmp === Str::ucFirst(RoleEnum::admin->label())) {
$tmp = RoleEnum::admin;
}
if ($tmp === Str::ucFirst(RoleEnum::visitor->label())) {
$tmp = RoleEnum::visitor;
}
$validator = Validator::make(['role' => $tmp], [
'role' => 'required'
]);
$validator->validate();
$this->role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please choose from the selection %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
}

View file

@ -1,48 +0,0 @@
<?php
namespace App\Enums\ActivityLogs;
use App\Traits\Enums\BaseEnum;
enum ActivityLogsEventEnum: int
{
use BaseEnum;
case created = 1;
case updated = 2;
case duplicated = 3;
case deleted = 4;
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::created->name => 'Création',
self::updated->name => 'Mise à jour',
self::duplicated->name => 'Duplication',
self::deleted->name => 'Suppression',
];
/**
* Custom added classes definition
*/
private const BOOTSTRAPCLASS = [
self::created->name => 'success',
self::updated->name => 'warning',
self::duplicated->name => 'secondary',
self::deleted->name => 'danger',
];
/**
* Get Class
*
* @return string
*/
public function bootstrapClass(): string
{
return self::BOOTSTRAPCLASS[$this->name];
}
}

View file

@ -17,7 +17,6 @@ enum CookingModeEnum: int
* Enum labels
*
* @return array
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::withoutCooking->name => 'Sans cuisson',

View file

@ -12,12 +12,6 @@ enum DifficultyEnum: int
case medium = 2;
case hard = 3;
/**
* Enum labels
*
* @return array
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::easy->name => 'Facile',
self::medium->name => 'Moyen',

View file

@ -15,12 +15,6 @@ enum NutriscoreEnum: int
case dLetter = 5;
case eLetter = 6;
/**
* Enum labels
*
* @return array
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::noLetter->name => 'N/A',
self::aLetter->name => 'A',
@ -30,11 +24,6 @@ enum NutriscoreEnum: int
self::eLetter->name => 'E'
];
/**
* Enum stickers
*
* @return array
*/
private const STICKERS = [
self::noLetter->name => '',
self::aLetter->name => 'resources/assets/images/nutriscore/logo-nutriscore-A.png',

View file

@ -1,16 +0,0 @@
<?php
namespace App\Enums\Pagination;
use App\Traits\Enums\BaseEnum;
enum ItemsPerPaginationEnum: int
{
use BaseEnum;
case five = 5;
case twelve = 12;
case twentyfive = 25;
case fifty = 50;
case onehundred = 100;
}

View file

@ -13,12 +13,6 @@ enum PreparationTimeEnum: int
case interval3 = 3;
case interval4 = 4;
/**
* Enum labels
*
* @return array
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::interval1->name => '0-30 min',
self::interval2->name => '30min - 2h',
@ -26,11 +20,6 @@ enum PreparationTimeEnum: int
self::interval4->name => '> 4h',
];
/**
* Enum times
*
* @return array
*/
private const TIMES = [
self::interval1->name => ['min' => 0, 'max' => 30],
self::interval2->name => ['min' => 31, 'max' => 120],
@ -51,7 +40,7 @@ enum PreparationTimeEnum: int
/**
* Get enum images
*
* @return array<string, array<string, int>>
* @return array<string>
*/
public static function toTimes(): array
{

View file

@ -20,12 +20,6 @@ enum SignsEnum: int
case uexpress = 10;
case netto = 11;
/**
* Enum labels
*
* @return array
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::leclerc->name => 'Leclerc',
self::auchan->name => 'Auchan',

View file

@ -1,31 +0,0 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum StaticPageType: int
{
use BaseEnum;
case legals = 1;
case confidentiality = 2;
case homepage = 3;
case our_products = 4;
case our_commitments = 5;
case banner_section = 6;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::legals->name => 'Mentions légales',
self::confidentiality->name => 'Confidentialité',
self::homepage->name => 'Accueil',
self::our_products->name => 'Nos produits',
self::our_commitments->name => 'Nos engagements',
self::banner_section->name => 'Section bannière',
];
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum;
enum RoleEnum: int
{
use BaseEnum;
case conceptor = 1;
case admin = 2;
case visitor = 99;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::conceptor->name => 'enums.user-role.conceptor',
self::admin->name => 'enums.user-role.admin',
self::visitor->name => 'enums.user-role.visitor',
];
}

View file

@ -1,71 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\ActivityLog;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
class ActivityLogController extends Controller
{
use ChangesModelOrder;
use UpdateModelPublished;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
$this->authorize('isConceptor');
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = ActivityLog::query()->with('user');
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'model_class',
);
}
$searchFields = \implode(', ', [
trans('validation.custom.target'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$activitylogModels = $this->paginate($query);
return view('bo.pages.activity_logs.index', compact('activitylogModels', 'search', 'searchFields'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\ActivityLog $activity_log
* @return \Illuminate\Contracts\View\View
*/
public function show(ActivityLog $activity_log): \Illuminate\Contracts\View\View
{
$this->authorize('isConceptor');
$targetModelClass = $activity_log->model_class;
$targetModel = $activity_log->model_class::where(
(new $targetModelClass())->getRouteKeyName(),
$activity_log->model_id
)->first();
return view('bo.pages.activity_logs.show', [
'activitylogModel' => $activity_log,
'targetModel' => $targetModel,
]);
}
}

View file

@ -16,9 +16,9 @@ class CertificationController extends Controller
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $certifications */
$certifications = Certification::query();
@ -43,9 +43,9 @@ class CertificationController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function create(): \Illuminate\Contracts\View\View
public function create()
{
return view('bo.pages.certifications.create');
}
@ -54,9 +54,9 @@ class CertificationController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Certifications\StoreCertificationRequest $request
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function store(StoreCertificationRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreCertificationRequest $request)
{
return DB::transaction(function () use ($request) {
$certification = new Certification();
@ -84,9 +84,9 @@ class CertificationController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Certification $certification
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Certification $certification): \Illuminate\Contracts\View\View
public function edit(Certification $certification)
{
return view('bo.pages.certifications.edit', [
'certificationModel' => $certification,
@ -98,12 +98,10 @@ class CertificationController extends Controller
*
* @param \App\Http\Requests\Bo\Certifications\UpdateCertificationRequest $request
* @param \App\Models\Certification $certification
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(
UpdateCertificationRequest $request,
Certification $certification
): \Illuminate\Http\RedirectResponse {
public function update(UpdateCertificationRequest $request, Certification $certification)
{
return DB::transaction(function () use ($request, $certification) {
$certification->fill($request->validated());
@ -128,9 +126,9 @@ class CertificationController extends Controller
* Remove the specified resource from storage.
*
* @param \App\Models\Certification $certification
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function destroy(Certification $certification): \Illuminate\Http\RedirectResponse
public function destroy(Certification $certification)
{
if ($certification->deleteOrFail()) {
return redirect()

View file

@ -3,6 +3,7 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\View;
class DashboardController extends Controller
{
@ -11,7 +12,7 @@ class DashboardController extends Controller
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
public function index(): View
{
return view('bo.pages.dashboard');
}

View file

@ -22,9 +22,9 @@ class ProductController extends Controller
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $products */
$products = Product::query();
@ -50,9 +50,9 @@ class ProductController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function create(): \Illuminate\Contracts\View\View
public function create()
{
$ranges = Range::all();
$certifications = Certification::all();
@ -65,9 +65,9 @@ class ProductController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Products\StoreProductRequest $request
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function store(StoreProductRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreProductRequest $request)
{
return DB::transaction(function () use ($request) {
$product = new Product();
@ -97,9 +97,9 @@ class ProductController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Product $product): \Illuminate\Contracts\View\View
public function edit(Product $product)
{
$ranges = Range::all();
$certifications = Certification::all();
@ -118,9 +118,9 @@ class ProductController extends Controller
*
* @param \App\Http\Requests\Bo\Products\UpdateProductRequest $request
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateProductRequest $request, Product $product): \Illuminate\Http\RedirectResponse
public function update(UpdateProductRequest $request, Product $product)
{
return DB::transaction(function () use ($request, $product) {
$product->fill($request->validated());
@ -148,9 +148,9 @@ class ProductController extends Controller
* Remove the specified resource from storage.
*
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function destroy(Product $product): \Illuminate\Http\RedirectResponse
public function destroy(Product $product)
{
if ($product->deleteOrFail()) {
return redirect()

View file

@ -6,21 +6,23 @@ use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
use App\Models\Range;
use App\Traits\Controllers\UpdateModelStatus;
use App\Traits\Controllers\ChangesModelOrder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class RangeController extends Controller
{
use ChangesModelOrder;
use UpdateModelStatus;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $ranges */
$ranges = Range::query();
@ -46,9 +48,9 @@ class RangeController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Range $range): \Illuminate\Contracts\View\View
public function edit(Range $range)
{
return view('bo.pages.ranges.edit', [
'rangeModel' => $range
@ -60,9 +62,9 @@ class RangeController extends Controller
*
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateRangeRequest $request, Range $range): \Illuminate\Http\RedirectResponse
public function update(UpdateRangeRequest $request, Range $range)
{
return DB::transaction(function () use ($request, $range) {
$range->fill($request->validated());

View file

@ -20,9 +20,9 @@ class RecipeController extends Controller
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $recipes */
$recipes = Recipe::query();
@ -48,9 +48,9 @@ class RecipeController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function create(): \Illuminate\Contracts\View\View
public function create()
{
$products = Product::all();
$recipes = Recipe::all();
@ -65,9 +65,9 @@ class RecipeController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Recipes\StoreRecipeRequest $request
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function store(StoreRecipeRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreRecipeRequest $request)
{
return DB::transaction(function () use ($request) {
$recipe = new Recipe();
@ -98,12 +98,12 @@ class RecipeController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Recipe $recipe
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Recipe $recipe): \Illuminate\Contracts\View\View
public function edit(Recipe $recipe)
{
$products = Product::all();
$recipes = Recipe::query()->where('id', '!=', $recipe->id)->get();
$recipes = Recipe::where('id', '!=', $recipe->id)->get();
return view('bo.pages.recipes.edit', [
'recipeModel' => $recipe,
@ -117,9 +117,9 @@ class RecipeController extends Controller
*
* @param \App\Http\Requests\Bo\Recipes\UpdateRecipeRequest $request
* @param \App\Models\Recipe $recipe
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateRecipeRequest $request, Recipe $recipe): \Illuminate\Http\RedirectResponse
public function update(UpdateRecipeRequest $request, Recipe $recipe)
{
return DB::transaction(function () use ($request, $recipe) {
$recipe->products()->sync($request->product_list);
@ -149,9 +149,9 @@ class RecipeController extends Controller
* Remove the specified resource from storage.
*
* @param \App\Models\Recipe $recipe
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function destroy(Recipe $recipe): \Illuminate\Http\RedirectResponse
public function destroy(Recipe $recipe)
{
DB::table('recipe_recipe')->where($recipe->recipes()->getRelatedPivotKeyName(), $recipe->id)->delete();
$recipe->products()->sync([]);

View file

@ -16,9 +16,9 @@ class ShopController extends Controller
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $shops */
$shops = Shop::query();
@ -46,9 +46,9 @@ class ShopController extends Controller
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function create(): \Illuminate\Contracts\View\View
public function create()
{
return view('bo.pages.shops.create');
}
@ -57,9 +57,9 @@ class ShopController extends Controller
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Shops\StoreShopRequest $request
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function store(StoreShopRequest $request): \Illuminate\Http\RedirectResponse
public function store(StoreShopRequest $request)
{
return DB::transaction(function () use ($request) {
$shop = new Shop();
@ -87,9 +87,9 @@ class ShopController extends Controller
* Show the form for editing the specified resource.
*
* @param \App\Models\Shop $shop
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Shop $shop): \Illuminate\Contracts\View\View
public function edit(Shop $shop)
{
return view('bo.pages.shops.edit', [
'shopModel' => $shop,
@ -101,9 +101,9 @@ class ShopController extends Controller
*
* @param \App\Http\Requests\Bo\Shops\UpdateShopRequest $request
* @param \App\Models\Shop $shop
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function update(UpdateShopRequest $request, Shop $shop): \Illuminate\Http\RedirectResponse
public function update(UpdateShopRequest $request, Shop $shop)
{
return DB::transaction(function () use ($request, $shop) {
$shop->fill($request->validated());
@ -129,9 +129,9 @@ class ShopController extends Controller
* Remove the specified resource from storage.
*
* @param \App\Models\Shop $shop
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function destroy(Shop $shop): \Illuminate\Http\RedirectResponse
public function destroy(Shop $shop)
{
if ($shop->deleteOrFail()) {
return redirect()

View file

@ -1,104 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest;
use App\Models\StaticPage;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class StaticPageController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(StaticPage::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = StaticPage::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'title',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.title'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$staticPageModelList = $this->paginate($query);
return view('bo.pages.static_pages.index', compact('staticPageModelList', 'search', 'searchFields'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Contracts\View\View
*/
public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View
{
return view('bo.pages.static_pages.edit', ['staticPageModel' => $static_page]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest $request
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateStaticPageRequest $request, StaticPage $static_page): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $static_page) {
$validated = $request->validated();
$data = $static_page->data;
foreach ($validated['data'] as $key => $input) {
if (isset($input['value']) && is_array($input['value'])) {
$data[$key]['value'] = $input['value'];
}
}
$static_page->data = $data;
$static_page->fill(array_replace_recursive(
$static_page->getFillableFields($static_page),
$validated
));
if ($static_page->saveOrFail()) {
return redirect()->route('bo.static_pages.edit', $static_page)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1))
]));
}
return redirect()->route('bo.static_pages.edit', $static_page)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(\trans_choice('models.classes.static_page', 1))
]));
});
}
}

View file

@ -2,16 +2,12 @@
namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Cache;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Support\Str;
class Controller extends BaseController
{
@ -33,7 +29,7 @@ class Controller extends BaseController
protected function searchQuery(
Builder $query,
string $search,
?callable $searchQuery = null,
callable $searchQuery = null,
string ...$fields
): Builder {
if (count($fields)) {
@ -111,7 +107,10 @@ class Controller extends BaseController
Session::remove("$rName.sort_way");
}
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
if (!in_array(request()->sort_col, $ignore)) {
if (
!isset($ignore) or
(isset($ignore) and !in_array(request()->sort_col, $ignore))
) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
}
} elseif (Schema::hasColumn($table, 'order')) {
@ -121,30 +120,4 @@ class Controller extends BaseController
}
return $query;
}
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(
Builder $query,
?ItemsPerPaginationEnum $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve;
$pagination = ToolboxHelper::getValidatedEnum(
Cache::get($cacheKey, $pagination->value()),
'pagination',
'\App\Enums\Pagination\ItemsPerPaginationEnum',
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);
}
$query = $query->paginate($pagination);
return $query;
}
}

View file

@ -1,22 +0,0 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\StaticPage;
class CommitmentController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function index()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::our_commitments)->first();
return view('fo.pages.engagments', compact('staticPageModel'));
}
}

View file

@ -2,11 +2,9 @@
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\Range;
use App\Models\Recipe;
use App\Models\StaticPage;
class HomeController extends Controller
{
@ -17,19 +15,16 @@ class HomeController extends Controller
*/
public function index()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::homepage)->first();
// Gammes.
$ranges = Range::query()->where('status', true)
$ranges = Range::where('status', true)
->get();
// 3 Recettes.
$publishedRecipes = Recipe::query()
->where('status', true)
$publishedRecipes = Recipe::where('status', true)
->inRandomOrder()
->take(3)
->get();
return view('fo.pages.home', compact('staticPageModel', 'ranges', 'publishedRecipes'));
return view('fo.pages.home', compact('ranges', 'publishedRecipes'));
}
}

View file

@ -2,11 +2,9 @@
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\Range;
use App\Models\StaticPage;
use Illuminate\Database\Eloquent\Builder;
class ProductsController extends Controller
@ -18,13 +16,11 @@ class ProductsController extends Controller
*/
public function index()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::our_products)->first();
// Gammes.
$ranges = Range::query()->where('status', true)
$ranges = Range::where('status', true)
->get();
return view('fo.pages.products.index', compact('staticPageModel', 'ranges'));
return view('fo.pages.products.index', compact('ranges'));
}
/**
@ -39,7 +35,7 @@ class ProductsController extends Controller
$q->where('id', $publishedRange->id);
})
->where('status', true)
->paginate(9);
->paginate(9);
return view('fo.pages.products.range-products', [
'products' => $products,
@ -55,7 +51,6 @@ class ProductsController extends Controller
*/
public function show(Product $publishedProduct)
{
$publishedProduct->load('range');
$certifications = $publishedProduct->certifications()->where('status', true)->get();
$associatedRecipes = $publishedProduct->recipes()->where('status', true)->take(3)->get();
$productModel = $publishedProduct;

View file

@ -2,64 +2,54 @@
namespace App\Http\Controllers\Fo;
use App\Enums\CookingModeEnum;
use App\Enums\DifficultyEnum;
use App\Enums\PreparationTimeEnum;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\RecipesIndexRequest;
use App\Models\Recipe;
use Illuminate\Support\Arr;
use App\Http\Resources\Fo\Recipes\RecipeResource;
use Illuminate\Support\Str;
use Illuminate\Http\Request;
class RecipesController extends Controller
{
/**
* Return a list of filtered recipes.
*
* @param \App\Http\Requests\Fo\RecipesIndexRequest $request
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(RecipesIndexRequest $request)
public function index(Request $request)
{
$query = Recipe::query();
// Get recipes.
$filters_activated = $request->filtered_infos;
/** @var \App\Enums\CookingModeEnum|null */
$cooking_mode_enum = !empty($request->get('cooking_mode_enum')) ?
CookingModeEnum::make($request->get('cooking_mode_enum')) :
null;
/** @var \App\Enums\PreparationTimeEnum|null */
$preparation_time_enum = !empty($request->get('preparation_time_enum')) ?
PreparationTimeEnum::make($request->get('preparation_time_enum')) :
null;
/** @var \App\Enums\DifficultyEnum|null */
$difficulty_enum = !empty($request->get('difficulty_enum')) ?
DifficultyEnum::make($request->get('difficulty_enum')) :
null;
if ($cooking_mode_enum) {
$query->where('cooking_mode', $cooking_mode_enum);
}
if ($preparation_time_enum) {
/** @var integer */
$min_time = Arr::get($preparation_time_enum->time(), 'min', 0);
/** @var integer */
$max_time = Arr::get($preparation_time_enum->time(), 'max', 9999);
$query->whereBetween('preparation_time', [$min_time, $max_time]);
}
if ($difficulty_enum) {
$query->where('difficulty', $difficulty_enum);
}
$publishedRecipes = $query
$publishedRecipes = Recipe::when(
isset($filters_activated),
function ($query) use ($filters_activated) {
if ($filters_activated[0] >= 0) {
$diff_oper = '=';
} else {
$diff_oper = '>';
}
if ($filters_activated[1] >= 0) {
$min_time = PreparationTimeEnum::from($filters_activated[1])->time()['min'];
$max_time = PreparationTimeEnum::from($filters_activated[1])->time()['max'];
} else {
$min_time = 0;
$max_time = 9999;
}
if ($filters_activated[2] >= 0) {
$cook_oper = '=';
} else {
$cook_oper = '>';
}
$query->where('difficulty', $diff_oper, $filters_activated[0])
->whereBetween('preparation_time', [$min_time, $max_time])
->where('cooking_mode', $cook_oper, $filters_activated[2]);
}
)
->where('status', true)
->orderBy('created_at')
->paginate(9);
return view('fo.pages.recipes.index', compact('publishedRecipes'));
return view('fo.pages.recipes.index', compact('publishedRecipes', 'filters_activated'));
}
/**
@ -74,8 +64,7 @@ class RecipesController extends Controller
$relatedRecipes = $publishedRecipe->recipes()->where('status', true)->get();
// 3 Recettes.
$publishedRecipes = Recipe::query()
->where('status', true)
$publishedRecipes = Recipe::where('status', true)
->where('id', '!=', $publishedRecipe->id)
->take(3)
->get();
@ -87,102 +76,4 @@ class RecipesController extends Controller
compact('recipeModel', 'products', 'relatedRecipes')
);
}
/**
* Print recipe informations
*
* @param \App\Models\Recipe $publishedRecipe
* @return \Symfony\Component\HttpFoundation\Response
*/
public function printRecipe(Recipe $publishedRecipe): \Symfony\Component\HttpFoundation\Response
{
$title = trans('Fiche commerciale du produit : :label - :appname', [
'label' => $publishedRecipe->name,
'appname' => \config('app.name', 'Lionor')
]);
$filename = trans('fiche-commerciale-:slug-:date', [
'slug' => Str::slug($publishedRecipe->name),
'date' => \now()->format('Y-m-d')
]);
/**
* ! Réponse mano car en fait \Mccarlosen\LaravelMpdf\LaravelMpdf
* ! ne retourne pas vraiemnt une http response, mais provoque une 500
* ! apres avoir header et echo dans le corps de la réponse php (Nasty trick)
*
* (From LeGauloisPro)
*/
return \response(
$this->generateRecipePdf($publishedRecipe, $title)->download($filename),
200,
[
'Content-Type' => 'application/pdf',
'Content-disposition' => "inline; filename=\"{$filename}\"",
'Cache-Control' => 'public, must-revalidate, max-age=0',
'Pragma' => 'public',
// * For glory
'X-Generator' => 'mPDF',
'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT',
'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT',
]
);
}
/**
* Generate recipe PDF
*
* @param \App\Models\Recipe $recipe
* @param string $title
* @return \Mccarlosen\LaravelMpdf\LaravelMpdf
*/
private function generateRecipePdf(Recipe $recipe, string $title): \Mccarlosen\LaravelMpdf\LaravelMpdf
{
$tabRecipe = \json_decode(\json_encode(new RecipeResource($recipe)));
$mpdf = app('laravel-mpdf');
/** @var \Mccarlosen\LaravelMpdf\LaravelMpdf */
$pdf = $mpdf->loadView(
'fo.documents.recipe',
// * Dirty convert to object.
[
'title' => $title,
'recipe' => $tabRecipe
],
[],
[
'mode' => 'utf-8',
'format' => 'A4',
'default_font_size' => '12',
'default_font' => 'sans-serif',
// Recto Verso.
'margin_left' => 10,
'margin_right' => 10,
'mirrorMargins' => true,
'margin_top' => 25,
'margin_bottom' => 20,
'margin_header' => 0,
'margin_footer' => 0,
'orientation' => 'P',
'title' => $title,
'author' => \config('app.name', 'Lionor'),
'watermark' => '',
'show_watermark' => false,
'watermark_font' => 'sans-serif',
'display_mode' => 'fullpage',
'watermark_text_alpha' => 0.1,
'custom_font_dir' => '',
'custom_font_data' => [],
'auto_language_detection' => false,
'temp_dir' => rtrim(
ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(),
DIRECTORY_SEPARATOR
),
'pdfa' => false,
'pdfaauto' => false,
]
);
return $pdf;
}
}

View file

@ -14,9 +14,9 @@ class ShopController extends Controller
/**
* Get all shops
*
* @return \Illuminate\Http\JsonResponse
* @return json
*/
public function index(): \Illuminate\Http\JsonResponse
public function index()
{
if (request()->wantsJson()) {
return response()
@ -28,10 +28,10 @@ class ShopController extends Controller
/**
* Get the 5 nearest shops with the given coords in the request.
*
* @param \App\Http\Requests\Fo\Shop\GetNearestShopRequest $request
* @return \Illuminate\Http\JsonResponse
* @param App\Http\Requests\Fo\Shop\GetNearestShopRequest $request
* @return json
*/
public function getNearest(GetNearestShopRequest $request): \Illuminate\Http\JsonResponse
public function getNearest(GetNearestShopRequest $request)
{
if (!$request->wantsJson()) {
return abort(404);

View file

@ -1,34 +0,0 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\StaticPage;
class StaticPageController extends Controller
{
/**
* Legal show
*
* @param \App\Models\StaticPage $legalPage
* @return \Illuminate\Contracts\View\View
*/
public function legals(StaticPage $legalPage)
{
$legalPage = StaticPage::query()->where('type', StaticPageType::legals)->first();
return view('fo.pages.legals', ['staticPageModel' => $legalPage]);
}
/**
* Policies show
*
* @param \App\Models\StaticPage $policiesPage
* @return \Illuminate\Contracts\View\View
*/
public function policies(StaticPage $policiesPage)
{
$policiesPage = StaticPage::query()->where('type', StaticPageType::confidentiality)->first();
return view('fo.pages.policies', ['staticPageModel' => $policiesPage]);
}
}

View file

@ -40,12 +40,6 @@ class Kernel extends HttpKernel
\App\Http\Middleware\SecureHeaders::class
],
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,

View file

@ -7,6 +7,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
@ -30,7 +31,7 @@ class SecureHeaders
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, Closure $next): \Symfony\Component\HttpFoundation\Response
public function handle(Request $request, Closure $next): Response
{
// phpcs:enable
View::share('nonce', $nonce = Vite::useCspNonce());
@ -50,20 +51,12 @@ class SecureHeaders
"default-src 'none';",
"manifest-src 'self';",
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' blob: " . implode(' ', [
'https://yl.s-web.fr',
'https://api-adresse.data.gouv.fr'
]) . ';',
"img-src 'self' data: blob: " . implode(' ', [
'https://tile.openstreetmap.org/'
]) . ';',
// ! Blob required for printjs
"connect-src 'self' blob:;",
"img-src * data: blob:;",
'frame-src ' . implode(' ', [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/',
'https://www.youtube.com/',
'blob:',
'https://www.google.com/maps/'
]) . ';',
"font-src 'self' data: https://fonts.gstatic.com;",
// ! Using CKEditor will must have unsafe-inline style
@ -74,7 +67,7 @@ class SecureHeaders
'https://fonts.googleapis.com',
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
'https://fonts.googleapis.com',
'https://fonts.googleapis.com'
]) . ';',
// ! If you dont use ckeditor use this
// "style-src 'self' 'nonce-$nonce';",

View file

@ -27,7 +27,7 @@ class StoreCertificationRequest extends FormRequest
protected function prepareForValidation()
{
$this->merge([
'status' => $this->boolean('status')
'status' => $this->status ? true : false
]);
$this->mergePicture('sticker');
}

View file

@ -24,7 +24,7 @@ class ChangeOrderRequest extends FormRequest
protected function prepareForValidation()
{
$this->merge([
'direction' => $this->direction === 'up' ? true : false
'direction' => $this->action === 'up' ? true : false
]);
}
@ -36,6 +36,7 @@ class ChangeOrderRequest extends FormRequest
public function rules()
{
return [
'action' => 'required|string|in:up,down',
'direction' => 'required|boolean',
];
}

View file

@ -7,7 +7,6 @@ use App\Enums\NutriscoreEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Support\Str;
class StoreProductRequest extends FormRequest
{
@ -31,15 +30,14 @@ class StoreProductRequest extends FormRequest
protected function prepareForValidation()
{
$this->merge([
'status' => $this->boolean('status'),
'nutritional_values' => $this->input('nutritional_values') ?? [],
'status' => $this->status ? true : false,
'nutritional_values' => $this->nutritional_values ?? [],
'certification_list' => \collect($this->certifications)->map(function ($value) {
return \is_array($value) ? ($value['value'] ?? null) : null;
})->filter()->all(),
'recipe_list' => \collect($this->recipes)->map(function ($value) {
return \is_array($value) ? ($value['value'] ?? null) : null;
})->filter()->all(),
'description' => (string)Str::of($this->input('description'))
]);
$this->mergePicture('picture');
$this->mergePicture('sticker');
@ -62,7 +60,7 @@ class StoreProductRequest extends FormRequest
'sticker_alt' => 'required|min:3|max:255',
'sticker_title' => 'required|min:3|max:255',
'description' => 'present|string',
'description' => 'required|string|min:15',
'preparation' => 'nullable|string|min:15',
'ingredients' => 'required|string|min:15',

View file

@ -28,8 +28,8 @@ class UpdateRangeRequest extends FormRequest
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug(strip_tags($this->input('name'))),
'status' => $this->boolean('status')
'slug' => Str::slug(strip_tags($this->name)),
'status' => $this->status ? true : false
]);
$this->mergePicture('picture');
$this->mergePicture('banner');

View file

@ -31,12 +31,12 @@ class StoreRecipeRequest extends FormRequest
protected function prepareForValidation()
{
$this->merge([
'slug' => Str::slug(strip_tags($this->input('title'))),
'status' => $this->boolean('status'),
'ingredients' => $this->input('ingredients') ?? [],
'products' => $this->input('products') ?? [],
'recipes' => $this->input('recipes') ?? [],
'cooking_time' => $this->input('cooking_time') ?? 0,
'slug' => Str::slug(strip_tags($this->title)),
'status' => $this->status ? true : false,
'ingredients' => $this->ingredients ?? [],
'products' => $this->products ?? [],
'recipes' => $this->recipes ?? [],
'cooking_time' => $this->cooking_time ?? 0,
// * For sync relations
'product_list' => \collect($this->products)->map(function ($value) {
return \is_array($value) ? ($value['value'] ?? null) : null;
@ -75,7 +75,7 @@ class StoreRecipeRequest extends FormRequest
'difficulty' => ['required', new Enum(DifficultyEnum::class)],
'cooking_mode' => [
'required_with:cooking_time',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->cooking_time == 0 and $value != 0) {
$fail(\trans(":attribute doit être à :enumValue quand temps de cuisson est à 0.", [
@ -97,7 +97,7 @@ class StoreRecipeRequest extends FormRequest
'preparation_time' => 'required|numeric|min:1|max:1440',
'status' => 'required|boolean',
'products' => [
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
'present', 'array', function ($attribute, array $value, $fail) {
if (count($value) < 1 || count($value) > 3) {
$fail(\trans('Vous devez associer 1 à 3 produits.'));
@ -108,7 +108,7 @@ class StoreRecipeRequest extends FormRequest
'products.*.value' => 'required|exists:products,id',
'products.*.label' => 'required|string',
'recipes' => [
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
'present', 'array', function ($attribute, array $value, $fail) {
if (count($value) > 3) {
$fail(\trans('Vous devez associer au maximum 3 recettes.'));

View file

@ -1,75 +0,0 @@
<?php
namespace App\Http\Requests\Bo\StaticPages;
use App\Enums\StaticPageType;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class UpdateStaticPageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
$this->merge([
'content' => $this->input('content') ? Str::of($this->input('content'))->value() : null
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
$rules = [
'seo_title' => 'nullable',
'seo_description' => 'nullable',
'title' => 'required|min:3|max:255',
'content' => 'nullable',
'data' => 'present|array',
'data.*' => 'required|array',
];
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = request()->route()->parameter('static_page');
if (!$staticPageModel->type->equals(StaticPageType::banner_section)) {
$rules = \array_merge(
$rules,
[
'seo_title' => 'required|min:45|max:70',
'seo_description' => 'required|min:50|max:160',
],
);
}
if (
!$staticPageModel->type->equals(StaticPageType::homepage) &&
!$staticPageModel->type->equals(StaticPageType::our_products) &&
!$staticPageModel->type->equals(StaticPageType::our_commitments) &&
!$staticPageModel->type->equals(StaticPageType::banner_section)
) {
$rules = \array_merge(
$rules,
['content' => ['present']],
);
}
return $rules;
}
}

View file

@ -109,7 +109,7 @@ abstract class UpdateMiniCrudRequest extends FormRequest
$rules = array_merge($rules, [
"data.$iptName.value" => [
function (string $attribute, $value, callable $fail) use ($sizes) {
ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
],
$this->compileValidate(

View file

@ -30,7 +30,7 @@ class ContactRequest extends FormRequest
return [
'civility' => [
'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (!in_array($value, CivilityEnum::toValues())) {
$fail(__('Veuillez choisir entre :values', [
@ -48,6 +48,7 @@ class ContactRequest extends FormRequest
'city' => 'required|string|max:255',
'subject' => 'required|string|max:255',
'message' => 'required|string|min:20|max:255',
'rgpd' => 'accepted'
];
}
@ -87,6 +88,7 @@ class ContactRequest extends FormRequest
'city' => trans('ville'),
'subject' => trans('objet e la demande'),
'message' => trans('message'),
'rgpd' => trans('condition d\'utilisation'),
];
}

View file

@ -1,65 +0,0 @@
<?php
namespace App\Http\Requests\Fo;
use App\Enums\CookingModeEnum;
use App\Enums\DifficultyEnum;
use App\Enums\PreparationTimeEnum;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class RecipesIndexRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return true;
}
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
$this->merge([
'cooking_mode_enum' => $this->makeEnum(CookingModeEnum::class, $this->get('cooking_mode_enum')),
'preparation_time_enum' => $this->makeEnum(PreparationTimeEnum::class, $this->get('preparation_time_enum')),
'difficulty_enum' => $this->makeEnum(DifficultyEnum::class, $this->get('difficulty_enum'))
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array|string>
*/
public function rules(): array
{
return [
'cooking_mode_enum' => ['nullable', Rule::enum(CookingModeEnum::class)],
'preparation_time_enum' => ['nullable', Rule::enum(PreparationTimeEnum::class)],
'difficulty_enum' => ['nullable', Rule::enum(DifficultyEnum::class)]
];
}
/**
* Make enum handle nullable case.
*
* @param string $enum_class
* @param string|null $query_index
* @return string|integer|null
*/
private function makeEnum(string $enum_class, ?string $query_index): string|int|null
{
if (!$query_index) {
return null;
}
return optional(call_user_func([$enum_class, 'make'], $query_index))->value();
}
}

View file

@ -1,81 +0,0 @@
<?php
namespace App\Http\Resources\Fo\Recipes;
use App\Models\Recipe;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class RecipeResource extends JsonResource
{
/** @var \App\Models\Recipe */
private $recipe;
/** @var array */
private static $skeleton = [
'id' => '',
'name' => '',
'slug' => '',
'picture' => '',
'description' => '',
'preparation' => '',
'ingredients' => [],
'portions' => '',
'difficulty' => [
'label' => 'none',
'value' => 0
],
'preparation_time' => '',
'cooking_time' => '',
];
/**
* RecipeResource
*
* @param \App\Models\Recipe $recipe
* @return void
*/
public function __construct(Recipe $recipe)
{
$this->recipe = $recipe;
}
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request|null $request
* @return array
*
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function toArray(?Request $request = null): array
{
// phpcs:enable
return array_intersect_key(
[
'id' => $this->recipe->id,
'name' => $this->recipe->name,
'slug' => $this->recipe->slug,
'picture' => $this->recipe->picture,
'description' => $this->recipe->description,
'preparation' => $this->recipe->preparation,
'ingredients' => $this->recipe->ingredients,
'portions' => $this->recipe->portions,
'difficulty' => $this->recipe->difficulty,
'preparation_time' => $this->recipe->preparation_time,
'cooking_time' => $this->recipe->cooking_time,
],
$this->getSkeleton()
);
}
/**
* Returns the resource skeletton
*
* @return array
*/
public static function getSkeleton(): array
{
return self::$skeleton;
}
}

View file

@ -145,7 +145,7 @@ class ExceptionsHelper
$traceStr .= self::handleArgs($stack);
}
$traceStr .= ")\n";
} else {
} elseif (isset($stack['function'])) {
$traceStr .= "{$stack['function']}(";
if (isset($stack['args'])) {
$traceStr .= self::handleArgs($stack);

View file

@ -91,7 +91,7 @@ class FileStorageHelper
Model $model,
$file,
bool $slugify = false,
?string $filename = null,
string $filename = null,
bool $private = false
): string {
// phpcs:enable
@ -118,7 +118,7 @@ class FileStorageHelper
);
return str_replace('/private/', '', $newPath);
} //end if
}//end if
if (\is_object($file) and $file instanceof \SplFileInfo) {
/** @var \SplFileInfo $file */
$filename = $filename ?: $file->getBaseName();
@ -152,7 +152,7 @@ class FileStorageHelper
public static function storeModelFilesFromHtml(
Model $model,
string $htmlString,
?string $oldHtmlString = null
string $oldHtmlString = null
): string {
\collect(self::onlyUploadedFilesWithFileManager(self::findFilesInHtml($htmlString)))
->each(function (string $path) use ($model, &$htmlString) {
@ -309,6 +309,7 @@ class FileStorageHelper
$private ? 'app/private/modelfiles' : 'app/public/modelfiles',
$tableName,
$filename,
$private
);
$filename = self::getFileUniqueName($folderPath, $filename);
return "{$folderPath}/{$filename}";
@ -403,7 +404,7 @@ class FileStorageHelper
$filename = Str::of($filename)->ltrim('.-');
// * Optional beautification
if ($beautify) {
$filename = self::beautifyFilename($filename);
$filename = static::beautifyFilename($filename);
}
// * Maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
/** @var string */

View file

@ -12,7 +12,6 @@ use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@ -170,10 +169,11 @@ class ToolboxHelper
$url = $match;
try {
$request = Request::create($url);
// Assert route matches existing routes.
$routes->match($request);
// ? Is url internal ?
if (\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST)) {
// If a route is match then it do not throws exception.
if (
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
$routes->match($request)
) {
// Removes the matched url.
throw ValidationException::withMessages([
$field => trans(
@ -199,14 +199,14 @@ class ToolboxHelper
* @param array|string $replace
* @param array|string $subject
* @param integer $count
* @return array|string|false
* @return array|string
*/
public static function mbReplace(
array|string $search,
array|string $replace,
array|string $subject,
int &$count = 0
): array|string|false {
): array|string {
if (!is_array($search) && is_array($replace)) {
return false;
}
@ -283,12 +283,9 @@ class ToolboxHelper
public static function getOriginal(Model $model, string $attribute): mixed
{
if (\in_array('Spatie\Translatable\HasTranslations', \class_uses_recursive($model))) {
// @phpstan-ignore-next-line
return $model->getTranslation(
$attribute,
// @phpstan-ignore-next-line
$model->getLocale(),
// @phpstan-ignore-next-line
$model->useFallbackLocale()
);
}
@ -312,7 +309,7 @@ class ToolboxHelper
): Builder {
$currentLocale = app()->currentLocale();
$locales = collect(config('app.locales'))
->mapWithKeys(fn (string $locale) => [$locale => $locale]);
->mapWithKeys(fn (string $locale) => [$locale => $locale]);
if (!$locales->has($defaultLocale)) {
// * Remove default locale.
$locales->pull($defaultLocale);
@ -358,55 +355,55 @@ class ToolboxHelper
/**
* Convert deeply an object to array
*
* @param mixed $input
* @param array|object $input
* @return array
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function objectToArray(mixed $input): array
public static function objectToArray($input): array
{
// Only process if it's an object or array being passed to the function.
if (!(is_object($input) or is_array($input))) {
if (is_object($input) or is_array($input)) {
$ret = (array)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::objectToArray($item) : $item;
}
return $ret;
} else {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
$ret = (array)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::objectToArray($item) : $item;
}
return $ret;
}
/**
* Convert deeply an array to object
*
* @param mixed $input
* @param array|object $input
* @return object
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function arrayToObject(mixed $input): object
public static function arrayToObject($input): object
{
// Only process if it's an object or array being passed to the function.
if (!(is_object($input) or is_array($input))) {
if (is_object($input) or is_array($input)) {
$ret = (object)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::arrayToObject($item) : $item;
}
return $ret;
} else {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
$ret = (object)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::arrayToObject($item) : $item;
}
return $ret;
}
/**
* Validate picture
*
* @param string $attribute
* @param string|null $value
* @param callable $fail
* @param callback $fail
* @param object $sizes
* @return void
*/
@ -429,7 +426,7 @@ class ToolboxHelper
'attribute' => $attribute
]));
}
if (!($dimensions = \getimagesize($fileFullPath))) {
if (!($dimensions = \getimagesize($fileFullPath)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
$fail(trans(':attribute impossible de valider les dimensions', [
'attribute' => $attribute
]));
@ -484,24 +481,4 @@ class ToolboxHelper
{
return !$useStorage ? File::mimeType($value) : Storage::mimeType($value);
}
/**
* Get validated pagination from request.
*
* @param integer $default
* @param string $field
* @param string $enumPath
* @return integer
*/
public static function getValidatedEnum(int $default, string $field, string $enumPath): int
{
try {
return \intval(Validator::make(
[$field => \request()->get($field)],
[$field => new Enum($enumPath)]
)->validated()[$field]);
} catch (ValidationException $e) {
return $default;
}
}
}

View file

@ -1,179 +0,0 @@
<?php
namespace App\Models;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Str;
/**
* @property string $model Target model.
* @property string $model_id Id of the target model.
* @property \App\Enums\ActivityLogs\ActivityLogsEventEnum $event Event of this activity (ActivityLogsEventEnum).
* @property array $data Changes.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
*
* @property-read \App\Models\User $user User BelongsTo relation.
*/
class ActivityLog extends Model
{
use HasFactory;
use Notifiable;
/**
* Indicates if the model should be timestamped.
*
* @var boolean
*/
public $timestamps = false;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'is_anonymous',
'model_class',
'model_id',
'event',
'data',
'created_at',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'is_anonymous' => 'bool',
'event' => ActivityLogsEventEnum::class,
'data' => 'json',
'created_at' => 'datetime',
];
// * METHODS
/**
* Add new activity to the activity logs list.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum
* @return void
*/
public static function addActivity(Model $model, ActivityLogsEventEnum $eventEnum): void
{
$isAnonymous = true;
/** @var \Illuminate\Database\Eloquent\Model|null */
$userModel = auth('backend')->user();
if (is_object($userModel)) {
$userModel = $userModel->getKeyForSelectQuery();
$isAnonymous = false;
}
$activity = new self();
$activity->user_id = $userModel;
$activity->is_console = app()->runningInConsole();
$activity->is_anonymous = $isAnonymous;
$activity->model_class = \get_class($model);
$activity->model_id = $model->getKey();
$activity->event = $eventEnum;
$activity->data = static::getChangedColumns($activity, $model, $eventEnum);
// @phpstan-ignore-next-line
$activity->created_at = now();
$activity->saveOrFail();
}
/**
* Get old, new columns that has changed.
*
* @param self $activity
* @param \Illuminate\Database\Eloquent\Model $model
* @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum
* @return array
*/
public static function getChangedColumns(self $activity, Model $model, ActivityLogsEventEnum $eventEnum): array
{
$modelClassName = $activity->model_class;
/** Get type of each fields of the target model */
/** @var \Illuminate\Database\Eloquent\Model|null $targetModel */
$targetModel = $activity->model_class::where(
(new $modelClassName())->getRouteKeyName(),
$activity->model_id
)->first();
if (\is_null($targetModel)) {
return [];
}
$targetModelTypes = collect($targetModel->toArray())
->map(fn ($value) => self::getValueType($value))
->toArray();
/** Return old, new and type of values changed */
return match ($eventEnum) {
ActivityLogsEventEnum::created => array_intersect_key($targetModelTypes, $model->toArray()),
ActivityLogsEventEnum::duplicated => array_intersect_key($targetModelTypes, $model->toArray()),
ActivityLogsEventEnum::updated => array_intersect_key($targetModelTypes, $model->getChanges()),
default => []
};
}
/**
* Get a value type as a string
* ! Use this for display purpose only
*
* @param mixed $value
* @return string
* @throws \RuntimeException If type is unhlanded.
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.MaxExceeded
*/
private static function getValueType(mixed $value): string
{
// phpcs:enable
$type = \gettype($value);
switch ($type) {
// phpcs:ignore PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
case 'string':
return match (true) {
Str::length(\strip_tags($value)) !== Str::length($value) => 'html',
Str::startsWith($value, 'storage/modelfiles') => 'file',
Str::isUuid($value) => 'uuid',
Str::isUlid($value) => 'ulid',
Str::isUrl($value) => 'url',
Str::isMatch('/^[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]$/', $value) => 'youtube_video_id',
\is_numeric($value) => 'numeric',
Str::isJson($value) and
(Str::startsWith($value, '[') or Str::startsWith($value, '{')) => 'json',
default => 'string'
};
case 'NULL':
case 'object':
case 'array':
case 'integer':
case 'boolean':
return $type;
case 'double':
return is_float($value) ? 'float' : 'double';
case 'resource (closed)':
case 'resource':
throw new \RuntimeException('Laravel model properties should not contain resources !');
case 'unknown type':
default:
throw new \RuntimeException('Unhandled Type ' . \gettype($value));
} //end switch
}
// * RELATIONS
/**
* User BelongsTo relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(User::class);
}
}

View file

@ -25,7 +25,7 @@ class Certification extends Model
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
* @var array
*/
protected $fillable = [
'name',
@ -38,7 +38,7 @@ class Certification extends Model
/**
* The attributes that should be cast.
*
* @var array<string, string>
* @var array
*/
protected $casts = [
'status' => 'bool'
@ -61,7 +61,7 @@ class Certification extends Model
$model->sticker = FileStorageHelper::storeFile($model, $model->sticker, true);
});
static::updated(function (self $model) {
FileStorageHelper::removeOldFile($model, 'sticker');
$model->sticker = FileStorageHelper::removeOldFile($model, 'sticker');
self::flashStatus($model, true);
});
static::deleting(function (self $model) {

View file

@ -10,11 +10,8 @@ use App\Traits\Models\HasFlashStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
class Product extends Model implements Sitemapable
class Product extends Model
{
use HasFactory;
use HasFlashStatus;
@ -22,7 +19,7 @@ class Product extends Model implements Sitemapable
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
* @var array
*/
protected $fillable = [
'name',
@ -50,7 +47,7 @@ class Product extends Model implements Sitemapable
/**
* The attributes that should be cast.
*
* @var array<string, string>
* @var array
*/
protected $casts = [
/** @var array<ingredient> */
@ -70,7 +67,7 @@ class Product extends Model implements Sitemapable
// * EVENT CLOSURES.
static::creating(function (self $model) {
// * ASSERTIONS
self::assertFieldIsUnique('name', $model->name);
static::assertFieldIsUnique('name', $model->name);
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
ToolboxHelper::assertNoInternalBoUrls('preparation', $model->preparation ?? '');
ToolboxHelper::assertNoInternalBoUrls('ingredients', $model->ingredients);
@ -85,7 +82,7 @@ class Product extends Model implements Sitemapable
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertFieldIsUnique('name', $model->name, $model->id);
static::assertFieldIsUnique('name', $model->name, $model->id);
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
ToolboxHelper::assertNoInternalBoUrls('preparation', $model->preparation ?? '');
ToolboxHelper::assertNoInternalBoUrls('ingredients', $model->ingredients);
@ -155,19 +152,6 @@ class Product extends Model implements Sitemapable
return $this->belongsToMany(Certification::class, 'product_certification');
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this->loadMissing('range');
$route = 'products.show';
$params = ['publishedProduct' => $model->slug];
return SitemapHelper::generateUrl($route, $params, $model);
}
/**
* BelongsTo certifications relation
*

View file

@ -9,9 +9,6 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
/**
* Gamme
@ -43,7 +40,7 @@ use Uwil\SitemapGenerator\Lib\SitemapHelper;
*
* @package App\Models
*/
class Range extends Model implements Sitemapable
class Range extends Model
{
use HasFactory;
use HasFlashStatus;
@ -51,7 +48,7 @@ class Range extends Model implements Sitemapable
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
* @var array
*/
protected $fillable = [
'name',
@ -83,8 +80,8 @@ class Range extends Model implements Sitemapable
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertFieldIsUnique('name', $model->name, $model->id);
self::assertAtLeastOneIsPublished($model);
static::assertFieldIsUnique('name', $model->name, $model->id);
static::assertAtLeastOneIsPublished($model);
// * TRANSFORMATIONS
$model->slug = Str::slug($model->name);
@ -93,9 +90,9 @@ class Range extends Model implements Sitemapable
$model->sticker = FileStorageHelper::storeFile($model, $model->sticker, true);
});
static::updated(function (self $model) {
FileStorageHelper::removeOldFile($model, 'picture');
FileStorageHelper::removeOldFile($model, 'banner');
FileStorageHelper::removeOldFile($model, 'sticker');
$model->picture = FileStorageHelper::removeOldFile($model, 'picture');
$model->banner = FileStorageHelper::removeOldFile($model, 'banner');
$model->banner = FileStorageHelper::removeOldFile($model, 'sticker');
static::flashStatus($model, true);
});
static::deleting(function () {
@ -137,19 +134,6 @@ class Range extends Model implements Sitemapable
}
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this->loadMissing('products');
$route = 'ranges.products';
$params = ['publishedRange' => $model->slug, 'publishedProduct' => $model];
return SitemapHelper::generateUrl($route, $params, $model);
}
// * RELATIONSHIPS
/**

View file

@ -10,11 +10,8 @@ use App\Traits\Models\HasFlashStatus;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
class Recipe extends Model implements Sitemapable
class Recipe extends Model
{
use HasFactory;
use HasFlashStatus;
@ -22,7 +19,7 @@ class Recipe extends Model implements Sitemapable
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
* @var array
*/
protected $fillable = [
'name',
@ -50,7 +47,7 @@ class Recipe extends Model implements Sitemapable
/**
* The attributes that should be cast.
*
* @var array<string, string>
* @var array
*/
protected $casts = [
'ingredients' => 'array',
@ -68,14 +65,14 @@ class Recipe extends Model implements Sitemapable
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
self::assertFieldIsUnique('name', $model->name);
static::assertFieldIsUnique('name', $model->name);
self::manageFields($model);
});
static::created(function (self $model) {
self::flashStatus($model);
});
static::updating(function (self $model) {
self::assertFieldIsUnique('name', $model->name, $model->id);
static::assertFieldIsUnique('name', $model->name, $model->id);
self::manageFields($model);
});
static::updated(function (self $model) {
@ -94,10 +91,10 @@ class Recipe extends Model implements Sitemapable
/**
* Manage fields on this model event.
*
* @param self $model
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
private static function manageFields(self $model)
private static function manageFields(Model $model)
{
// * ASSERTIONS
ToolboxHelper::assertNoInternalBoUrls('description', $model->description);
@ -164,19 +161,6 @@ class Recipe extends Model implements Sitemapable
return "{$nbHours}H {$nbMinutes}M";
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this->loadMissing('recipes');
$route = 'recipes.show';
$params = ['publishedRecipe' => $model->slug, 'publishedProduct' => $model];
return SitemapHelper::generateUrl($route, $params, $model);
}
// * RELATIONSHIPS
/**

View file

@ -14,7 +14,7 @@ class Shop extends Model
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
* @var array
*/
protected $fillable = [
'name',
@ -29,7 +29,7 @@ class Shop extends Model
/**
* The attributes that should be cast.
*
* @var array<string, string>
* @var array
*/
protected $casts = [
'signs' => SignsEnum::class,
@ -45,11 +45,11 @@ class Shop extends Model
// * EVENT CLOSURES.
static::creating(function (self $model) {
// * ASSERTIONS
self::assertFieldIsUnique('name', $model->name);
static::assertFieldIsUnique('name', $model->name);
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertFieldIsUnique('name', $model->name, $model->id);
static::assertFieldIsUnique('name', $model->name, $model->id);
});
}

View file

@ -1,118 +0,0 @@
<?php
namespace App\Models;
use App\Enums\StaticPageType;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Models\ActivityLog;
use App\Traits\Models\MiniCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
/**
* @property \App\Enums\StaticPageType $type Page type (StaticPageEnum).
* @property string $seo_title Seo title about 70 chars as recommended.
* @property string $seo_description Seo description about 160 chars as recommended.
* @property string $title Title for the page.
* @property string $slug Slug of the title for the page.
* @property string $content WYSIWYG text content using ckeditor.
* @property array<string, array<string, mixed>> $data Static page json data.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*/
class StaticPage extends Model
{
use ActivityLog;
use HasFactory;
use MiniCrudModel;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'seo_title',
'seo_description',
'title',
'content',
'data'
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'type' => StaticPageType::class,
'data' => 'object',
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::creating(function (self $model) {
// * ASSERTIONS
self::assertTitleIsUnique($model->title);
// * CHANGES
$model->slug = Str::slug($model->title);
});
static::updating(function (self $model) {
// * ASSERTIONS
self::assertTitleIsUnique($model->title, $model->id);
// * CHANGES
$model->slug = Str::slug($model->title);
});
}
// * ROUTABLE
/**
* Get the value of the model's route key.
*
* @return mixed
*/
public function getRouteKey()
{
/** @var \App\Enums\StaticPageType */
$enum = $this->getAttribute($this->getRouteKeyName());
return $enum->value();
}
/**
* Get the route key for the model.
*
* ! Type is the routable parameter
*
* @return string
*/
public function getRouteKeyName(): string
{
return 'type';
}
// * METHODS
/**
* Asserts using validation that the title is unique
*
* @param string|null $title
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If an actu title already exists.
*/
private static function assertTitleIsUnique(string|null $title, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
}
}

View file

@ -2,29 +2,14 @@
namespace App\Models;
use App\Enums\Users\RoleEnum;
use App\Traits\Models\ActivityLog;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @property string $first_name Firstname.
* @property string $last_name Lastname.
* @property string $email Email.
* @property string $remember_token Remember_token.
* @property \App\Enums\Users\RoleEnum $role Role.
* @property \Illuminate\Support\Carbon $last_connection Last_connection.
* @property \Illuminate\Support\Carbon $published Published status.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*
* @property-read \App\Models\ActivityLog $activityLogs Activities logs HasMany relationship.
*/
class User extends Authenticatable
{
use ActivityLog;
use HasFactory;
use Notifiable;
@ -34,11 +19,10 @@ class User extends Authenticatable
* @var array<int, string>
*/
protected $fillable = [
'first_name',
'last_name',
'firstname',
'lastname',
'email',
'role',
'published',
'password',
];
/**
@ -47,6 +31,7 @@ class User extends Authenticatable
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
@ -56,9 +41,8 @@ class User extends Authenticatable
* @var array<string, string>
*/
protected $casts = [
'role' => RoleEnum::class,
'last_connection' => 'date',
'published' => 'boolean',
'email_verified_at' => 'datetime',
'last_connection' => 'datetime',
];
/**
@ -66,28 +50,20 @@ class User extends Authenticatable
*
* @return void
*/
protected static function booted(): void
protected static function booted()
{
static::creating(function (self $model) {
// * Prevent user elevation privileges
throw_if(
auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(),
AuthorizationException::class
);
});
static::updating(function (self $model) {
if (optional(auth('backend')->user())->getKey() === $model->getKey()) {
\validator(
['published' => $model->published],
['published' => 'required|boolean|accepted'],
['published.accepted' => trans('crud.messages.user_unpublished')],
)->validate();
// * EVENT CLOSURES.
static::creating(function (self $user) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($user->password);
}
// * Prevent user elevation privileges
throw_if(
auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(),
AuthorizationException::class
);
$user->remember_token = Str::random(10);
});
static::updating(function (self $user) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($user->password);
}
$user->remember_token = Str::random(10);
});
}
}

View file

@ -1,54 +0,0 @@
<?php
namespace App\Observers;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use App\Models\ActivityLog;
use Illuminate\Database\Eloquent\Model;
class ModelObserver
{
/**
* Handle the model "created" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function created(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::created);
}
/**
* Handle the model "updated" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function updated(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::updated);
}
/**
* Handle the model "duplicated" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function duplicated(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::duplicated);
}
/**
* Handle the model "deleted" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function deleted(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::deleted);
}
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Policies;
use App\Models\StaticPage;
use App\Models\User;
class StaticPagePolicy
{
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $authUser
* @return boolean
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.Found
*/
public function viewAny(User $authUser): bool
{
// phpcs:enable
return true;
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\StaticPage $static_page
* @return boolean
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
*/
public function update(User $authUser, StaticPage $static_page): bool
{
// phpcs:enable
return true;
}
}

View file

@ -2,16 +2,14 @@
namespace App\Providers;
use App\Enums\StaticPageType;
use App\Models\Range;
use App\Models\StaticPage;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
@ -40,7 +38,7 @@ class AppServiceProvider extends ServiceProvider
return request()->getClientIp();
} else {
return request()->server('HTTP_X_FORWARDED_FOR') ??
request()->getClientIp();
request()->getClientIp();
}
});
@ -73,14 +71,9 @@ class AppServiceProvider extends ServiceProvider
// * GAMMES
if (Schema::hasTable('ranges')) {
// On partage les ranges (gammes produit).
$ranges = Range::query()->where('status', true)->get();
$ranges = Range::where('status', true)->get();
View::share('PublishedRangesGlobal', $ranges);
}
if (Schema::hasTable('static_pages')) {
$bannerSection = StaticPage::query()->where('type', StaticPageType::banner_section->value())->get();
View::share('bannerSection', $bannerSection);
}
}
}
}

View file

@ -5,13 +5,6 @@ namespace App\Providers;
use Illuminate\Auth\Events\Registered;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Event;
use App\Models\User;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
use Slides\Saml2\Events\SignedIn;
use Slides\Saml2\Events\SignedOut;
class EventServiceProvider extends ServiceProvider
{
@ -26,6 +19,15 @@ class EventServiceProvider extends ServiceProvider
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot(): void
{
}
/**
* Determine if events and listeners should be automatically discovered.
*
@ -35,49 +37,4 @@ class EventServiceProvider extends ServiceProvider
{
return false;
}
/**
* Register any events for your application.
*
* @return void
*/
public function boot()
{
Event::listen('Slides\Saml2\Events\SignedIn', function (SignedIn $event) {
$messageId = $event->getAuth()->getLastMessageId();
$fromCache = Cache::get(config('cache.prefix') . "_saml_message_id_" . $messageId);
if ($fromCache) {
logger()->error(
'SAML MESSAGE ID HANDLER',
['error' => 'Message ID : ' . $messageId . " replayed !"]
);
abort(403);
} else {
Cache::put(config('cache.prefix') . "_saml_message_id_" . $messageId, $messageId, 28800);
// Cache for 8h.
}
$user = $event->getSaml2User();
$email = $user->getNameId();
$lUser = User::where('email', '=', $email)->first();
if (!$lUser) {
abort(403);
}
Auth::guard('backend')->login($lUser);
session(['sessionIndex' => $user->getSessionIndex()]);
$lUser->last_connection = now();
$lUser->saveQuietly();
});
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
Event::listen('Slides\Saml2\Events\SignedOut', function (SignedOut $event) {
Auth::guard('backend')->logout();
Session::save();
});
}
}

View file

@ -32,7 +32,7 @@ class TranslationServiceProvider extends ServiceProvider
/**
* Gather provided langs checking from files
*
* @return \Illuminate\Support\Collection<int, string>
* @return \Illuminate\Support\Collection<string, string>
*/
private function collectLocalesStrings(): Collection
{

View file

@ -3,56 +3,59 @@
namespace App\Traits\Controllers;
use App\Http\Requests\Bo\ChangeOrderRequest;
use App\Traits\Models\IsCachedAsCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
trait ChangesModelOrder
{
/**
* Change order.
* Change order
*
* ! requires x64 server + order column as unique integer
*
* @param \App\Http\Requests\Bo\ChangeOrderRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Http\RedirectResponse
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au model utilisé.
*/
public function changeOrder(ChangeOrderRequest $request, Model $model): \Illuminate\Http\RedirectResponse
public function changeOrder(ChangeOrderRequest $request, Model $model): RedirectResponse
{
$params = collect(request()->route()->parameterNames);
$className = Str::ucfirst(Str::lower($params->get($params->count() - 2)));
$className = "\\App\\Models\\$className";
if (!class_exists($className)) {
throw new \RuntimeException("The $className class doesn't exist.");
}
/** @var \Illuminate\Database\Eloquent\Model|null */
$model = $className::query()->where($model->getRouteKeyName(), $model->getRouteKey())->first();
if (!$model) {
return back()->with('error', trans('crud.messages.order_not_changed'));
}
return DB::transaction(function () use ($request, $className, $model) {
return DB::transaction(function () use ($request, $model) {
/** @var \Illuminate\Database\Eloquent\Model|null */
$tmp = $className::query()->where('order', $request->direction ? '<' : '>', $model->getOriginal('order'))
$tmp = (\get_class($model))::where('order', $request->direction ? '<' : '>', $model->order)
->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first();
if (!$tmp) {
return back()->with('error', trans('crud.messages.order_not_changed'));
return back()->with('bo.error', trans('L\'ordre n\'a pas pu être modifié'));
}
$oldOrder = $model->order;
$newOrder = $tmp->order;
$newOrder = $tmp->getOriginal('order');
// Didn't use ->update() because it didn't work with Models which use MiniCrudModel.
$tmp->fill(['order' => $model->getOriginal('order')]);
$tmp->save();
$model->fill(['order' => $newOrder]);
$model->save();
// * Silent update timestamps.
$tmp->timestamps = false;
$model->timestamps = false;
Session::flash('success', trans('crud.messages.order_changed'));
$saveOk = false;
// ! Fake value to prevent unique SQL key to trigger.
$tmp->order = 4294967295;
$saveOk = $tmp->saveQuietly();
$model->order = $newOrder;
$saveOk = $model->saveQuietly() & $saveOk;
$tmp->order = $oldOrder;
$saveOk = $tmp->saveQuietly() & $saveOk;
if (\in_array(IsCachedAsCollection::class, class_uses_recursive(\get_class($model)))) {
$model::removeCached();
}
if (!$saveOk) {
throw ValidationException::withMessages([
'error' => \trans('L\'ordre n\'a pas pu être modifié')
]);
}
Session::flash('bo.success', trans('L\'ordre a été modifié'));
return back();
});
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Traits\Controllers;
use Illuminate\Database\Eloquent\Model;
/**
* This trait works with Route::bind in App service provider
* to resolve the model and the DB row
* It does not work with binder Service
*/
trait UpdateModelPublished
{
/**
* Change model published.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Http\RedirectResponse
*/
public function changePublished(Model $model)
{
$model->update(['published' => !$model->getOriginal('published')]);
return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved')));
}
}

View file

@ -12,14 +12,19 @@ use Illuminate\Database\Eloquent\Model;
trait UpdateModelStatus
{
/**
* Change model published.
* Change model published
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Http\RedirectResponse
*/
public function changePublished(Model $model)
{
$model->update(['published' => !$model->getOriginal('published')]);
return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved')));
$model->status = !$model->status;
$model->saveOrFail();
if (!$model->saveOrFail()) {
return redirect()->back()->with('error', __('The status could not be changed'));
}
return redirect()->back()->with('success', __('The status has been changed'));
}
}

View file

@ -120,8 +120,8 @@ trait BaseEnum
/**
* Fetch enum using value
*
* @param self|integer|string $value
* @return self|null
* @param \App\Traits\Enums\BaseEnum|integer|string $value
* @return \App\Traits\Enums\BaseEnum|null
*/
public static function make(self|int|string $value): self|null
{

View file

@ -1,23 +0,0 @@
<?php
namespace App\Traits\Models;
use App\Observers\ModelObserver;
/**
* If there is a static function in a trait (named boot[TraitName]),
* it will be executed as the boot() function would on an Eloquent model.
* Futhermore, event in the Model observer doesn't erase actual events in the model,
* but adds them.
*/
trait ActivityLog
{
/**
*
* @return void
*/
public static function bootActivityLog(): void
{
static::observe(ModelObserver::class);
}
}

View file

@ -10,8 +10,7 @@ use Illuminate\Support\Facades\Cache;
/**
* ! Use this only on limited elements, (not on tags, or products, or posts for ex.)
*
* @property array<string,string|integer|float|boolean> $cacheConditions
* Eloquent condition to cache elements (to be used in where statement).
* @property private static array $cacheConditions Eloquent condition to cache elements (to be used in where statement).
*/
trait IsCachedAsCollection
{
@ -53,7 +52,7 @@ trait IsCachedAsCollection
{
$conditions = [];
if (property_exists(get_called_class(), 'cacheConditions')) {
$conditions = collect(get_called_class()::${'cacheConditions'})->all();
$conditions = collect(static::${'cacheConditions'})->all();
}
Cache::put(static::getCacheKeyName(), static::queryCached(static::query())
->where($conditions)->get()

View file

@ -11,20 +11,18 @@ trait MiniCrudModel
/**
* Fill the model with an array of attributes.
*
* @param array<string, mixed> $attributes
* @return $this
* @param array $attributes
* @return void
* @throws \Illuminate\Database\Eloquent\MassAssignmentException If fill prop is not allowed.
* @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows
*/
public function fill(array $attributes): static
public function fill(array $attributes)
{
// phpcs:enable
parent::fill($attributes);
if (isset($attributes['data'])) {
$this->setData($attributes['data']);
}
return $this;
}
/**
@ -64,6 +62,7 @@ trait MiniCrudModel
*/
public function minicrudUpdated()
{
/** @var array */
foreach ($this->data as $index => $input) {
switch ($input['inputType']) {
case InputType::text->value:
@ -178,21 +177,4 @@ trait MiniCrudModel
} //end foreach
return $data;
}
/**
* Get fillable properties and their values from the model.
*
* @param self $model
* @return array
*/
public function getFillableFields(self $model): array
{
$datas = [];
foreach ($model->toArray() as $property => $value) {
if (in_array($property, $model->getFillable())) {
$datas = array_merge($datas, [$property => $value]);
}
}
return $datas;
}
}

View file

@ -66,7 +66,7 @@ trait HasPicture
$name => [
!$nullable ? 'required' : 'nullable',
function (string $attribute, $value, callable $fail) use ($sizes) {
ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
]
];
@ -101,7 +101,7 @@ trait HasPicture
"{$name}Validate.*" => [
'required',
function (string $attribute, $value, callable $fail) use ($sizes) {
ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
return $this->validatePicture($attribute, $value, $fail, $sizes);
}
]
];

View file

@ -1,33 +0,0 @@
<?php
namespace App\View\Components;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class LocalBlockComponent extends Component
{
/** @var \App\Models\StaticPage $staticPageModel */
private $staticPageModel;
/**
* Create a new component instance.
*/
public function __construct()
{
$this->staticPageModel = StaticPage::query()->where('type', StaticPageType::our_commitments)->first();
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.local-block-component', [
'staticPageModel' => $this->staticPageModel
]);
}
}

View file

@ -3,34 +3,32 @@
"type": "project",
"description": "Site de la marque Lionor",
"require": {
"php": "^8.5",
"scaler-tech/laravel-saml2": "^2.5",
"php": "^8.2",
"axlon/laravel-postal-code-validation": "^3.3",
"carlos-meneses/laravel-mpdf": "^2.1",
"diglactic/laravel-breadcrumbs": "^9.0",
"diglactic/laravel-breadcrumbs": "^8.1.1",
"guzzlehttp/guzzle": "^7.2",
"kaishiyoku/laravel-color-validation": "^3.2",
"kwaadpepper/image-resizer": "^3.1",
"laravel/framework": "^11.0",
"kaishiyoku/laravel-color-validation": "^2.1.1",
"kwaadpepper/image-resizer": "^2.1",
"kwaadpepper/refresh-sitemap": "^2.0",
"laravel/framework": "^10.0",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"ldc/web-admin-secure-laravel": "^2.0.0",
"mews/purifier": "^3.4",
"propaganistas/laravel-phone": "^5.0.3",
"sentry/sentry-laravel": "*",
"uwil/laravel-admin-secure": "^6.0",
"uwil/k8s-laravel": "^1.4",
"uwil/sitemap-generator": "^3.0"
"uwil/k8s-laravel": "1.1.0"
},
"require-dev": {
"doctrine/dbal": "^3.3",
"fakerphp/faker": "^1.9.1",
"kitloong/laravel-migrations-generator": "^7.0",
"larastan/larastan": "^2.6",
"kitloong/laravel-migrations-generator": "^6.8",
"krlove/eloquent-model-generator": "dev-l10-compatibility",
"laravel-lang/lang": "10.0",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^8.1",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.0",
"slevomat/coding-standard": "^8.10",
"spatie/laravel-ignition": "^2.0",

6820
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -56,6 +56,8 @@ return [
'url' => env('APP_URL', 'http://localhost'),
'asset_url' => env('ASSET_URL'),
/*
|--------------------------------------------------------------------------
| Application Timezone

View file

@ -23,7 +23,7 @@ return [
|
*/
'view' => 'modules.breadcrumbs.breadcrumb-layout',
'view' => 'modules/breadcrumb/breadcrumb-layout',
/*
|--------------------------------------------------------------------------

106
config/refresh-sitemap.php Normal file
View file

@ -0,0 +1,106 @@
<?php
use Spatie\Sitemap\Tags\Url;
return [
/**
* A list of url to ignore
* These are hard coded
* Each url does not have to be exhaustive
*
* For example, if you want to ignore all routes
* of the backoffice group
* just add '/backoffice' into this table
*/
'ignoreRoutes' => [
'/bo',
],
/**
* This table is needed to resolve models matching
* routes
*
* it has to be filled like following
*
* 'products.meat-type' => [
* 'slugMeatType' => [MeatTypes::class, 'slug']
* ],
* 'kitchen-side.health-nutrition.balanced-menu.menu' => [
* 'type' => [BalancedMenuType::class, 'type']
* ]
*
* The index is the complete route name, the array content is an array
* within index is the argument name with its model class.
* This will allow the generator to generate a route for each of the models
* in this table
*/
'routesBinder' => [],
/**
* You can put route conditions here
* These will be used to decide whether the route should appear
* in the sitemap or not
*
* These are called in Eloquent where
*
* example
*
* 'queryConditions' => [
* 'status' => true,
* 'ocbStatus' => EleveurOcbStatus::yes()->value,
* 'star_moment' => true
* ]
*
*/
'queryConditions' => [
'status' => true,
],
/**
* The default route frequency to use with sitemap generator
*/
'routeDefaultFrequency' => Url::CHANGE_FREQUENCY_DAILY,
/**
* The default route priority to use with sitemap generator
*
* priority should be a number between 0 and 1
* https://www.sitemaps.org/protocol.html
*/
'routeDefaultPriority' => 0.5,
/**
* Frequencies for routes
* use the full or partial route name as index
* and a frequency within \Spatie\Sitemap\Tags\Url constants
*/
'routeFrequencies' => [],
/**
* Priorities for routes
* use the full or partial route name as index
* and a priority number between 0 and 1
*/
'routePriorities' => [],
/**
* Enable or disable schedule for the job
* If set to true the sitemap generator will be run
* using the 'cron' instruction
*/
'schedule' => true,
/**
* If you want to set specific
* instruction for when to schedule job
* you can set this with a cron instruction
* You can generate one using https://crontab.guru/
* https://laravel.com/docs/8.x/scheduling#schedule-frequency-options
*
* schedule must be set to true
*
* Default is every day at 15:45
*/
'cron' => '45 15 * * *'
];

View file

@ -1,397 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Tenant Model
|--------------------------------------------------------------------------
|
| This will allow you to override the tenant model with your own.
|
*/
'tenantModel' => \Slides\Saml2\Models\Tenant::class,
/*
|--------------------------------------------------------------------------
| Use built-in routes
|--------------------------------------------------------------------------
|
| If "useRoutes" set to true, the package defines five new routes:
|
| Method | URI | Name
| -------|---------------------------------|------------------
| POST | {routesPrefix}/{uuid}/acs | saml.acs
| GET | {routesPrefix}/{uuid}/login | saml.login
| GET | {routesPrefix}/{uuid}/logout | saml.logout
| GET | {routesPrefix}/{uuid}/metadata | saml.metadata
| GET | {routesPrefix}/{uuid}/sls | saml.sls
|
*/
'useRoutes' => true,
/*
|--------------------------------------------------------------------------
| Built-in routes prefix
|--------------------------------------------------------------------------
|
| Here you may define the prefix for built-in routes.
|
*/
'routesPrefix' => '/saml2',
/*
|--------------------------------------------------------------------------
| Middle groups to use for the SAML routes
|--------------------------------------------------------------------------
|
| Note, Laravel 5.2 requires a group which includes StartSession
|
*/
'routesMiddleware' => ['saml'],
/*
|--------------------------------------------------------------------------
| Signature validation
|--------------------------------------------------------------------------
|
| Set to true if you want to use parameters from $_SERVER to validate the signature.
|
*/
'retrieveParametersFromServer' => false,
/*
|--------------------------------------------------------------------------
| Login redirection URL.
|--------------------------------------------------------------------------
|
| The redirection URL after successful login.
|
*/
'loginRoute' => '/bo',
/*
|--------------------------------------------------------------------------
| Logout redirection URL.
|--------------------------------------------------------------------------
|
| The redirection URL after successful logout.
|
*/
'logoutRoute' => '/bo',
/*
|--------------------------------------------------------------------------
| Login error redirection URL.
|--------------------------------------------------------------------------
|
| The redirection URL after login failing.
|
*/
'errorRoute' => '/bo/login-error',
/*
|--------------------------------------------------------------------------
| Strict mode.
|--------------------------------------------------------------------------
|
| If 'strict' is True, then the PHP Toolkit will reject unsigned
| or unencrypted messages if it expects them signed or encrypted
| Also will reject the messages if not strictly follow the SAML
| standard: Destination, NameId, Conditions... are validated too.
|
*/
'strict' => true,
/*
|--------------------------------------------------------------------------
| Debug mode.
|--------------------------------------------------------------------------
|
| When enabled, errors must be printed.
|
*/
'debug' => env('SAML2_DEBUG', env('APP_DEBUG', false)),
/*
|--------------------------------------------------------------------------
| Whether to use `X-Forwarded-*` headers to determine port/domain/protocol.
|--------------------------------------------------------------------------
|
| If 'proxyVars' is True, then the Saml lib will trust proxy headers
| e.g X-Forwarded-Proto / HTTP_X_FORWARDED_PROTO. This is useful if
| your application is running behind a load balancer which terminates SSL.
|
*/
'proxyVars' => true,
/*
|--------------------------------------------------------------------------
| Service Provider configuration.
|--------------------------------------------------------------------------
|
| General setting of the service provider.
|
*/
'sp' => [
/*
|--------------------------------------------------------------------------
| NameID format.
|--------------------------------------------------------------------------
|
| Specifies constraints on the name identifier to be used to
| represent the requested subject.
|
*/
'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
/*
|--------------------------------------------------------------------------
| SP Certificates.
|--------------------------------------------------------------------------
|
| Usually x509cert and privateKey of the SP are provided by files placed at
| the certs folder. But we can also provide them with the following parameters.
|
*/
'x509cert' => env('SAML2_SP_CERT_x509', ''),
'privateKey' => env('SAML2_SP_CERT_PRIVATEKEY', ''),
/*
|--------------------------------------------------------------------------
| Identifier (URI) of the SP entity.
|--------------------------------------------------------------------------
|
| Leave blank to use the 'saml.metadata' route.
|
*/
'entityId' => env('SAML2_SP_ENTITYID', ''),
/*
|--------------------------------------------------------------------------
| The Assertion Consumer Service (ACS) URL.
|--------------------------------------------------------------------------
|
| URL Location where the <Response> from the IdP will be returned, using HTTP-POST binding.
| Leave blank to use the 'saml.acs' route.
|
*/
'assertionConsumerService' => [
'url' => '',
],
/*
|--------------------------------------------------------------------------
| The Single Logout Service URL.
|--------------------------------------------------------------------------
|
| Specifies info about where and how the <Logout Response> message MUST be
| returned to the requester, in this case our SP.
|
| URL Location where the <Response> from the IdP will be returned, using HTTP-Redirect binding.
| Leave blank to use the 'saml.sls' route.
|
*/
'singleLogoutService' => [
'url' => ''
],
],
/*
|--------------------------------------------------------------------------
| OneLogin security settings.
|--------------------------------------------------------------------------
|
|
|
*/
'security' => [
'signatureAlgorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
'digestAlgorithm' => 'http://www.w3.org/2001/04/xmlenc#sha256',
/*
|--------------------------------------------------------------------------
| NameId encryption
|--------------------------------------------------------------------------
|
| Indicates that the nameID of the <samlp:logoutRequest> sent by this SP
| will be encrypted.
|
*/
'nameIdEncrypted' => true,
/*
|--------------------------------------------------------------------------
| AuthnRequest signage
|--------------------------------------------------------------------------
|
| Indicates whether the <samlp:AuthnRequest> messages sent by
| this SP will be signed. The Metadata of the SP will offer this info
|
*/
'authnRequestsSigned' => true,
/*
|--------------------------------------------------------------------------
| Logout request signage
|--------------------------------------------------------------------------
|
| Indicates whether the <samlp:logoutRequest> messages sent by this SP
| will be signed.
|
*/
'logoutRequestSigned' => true,
/*
|--------------------------------------------------------------------------
| Logout response signage
|--------------------------------------------------------------------------
|
| Indicates whether the <samlp:logoutResponse> messages sent by this SP
| will be signed.
|
*/
'logoutResponseSigned' => true,
/*
|--------------------------------------------------------------------------
| Whether need to sign metadata.
|--------------------------------------------------------------------------
|
| The possible values:
| - false
| - true (use certs)
| - array:
| ```
| [
| 'keyFileName' => 'metadata.key',
| 'certFileName' => 'metadata.crt'
| ]
| ```
|
*/
'signMetadata' => true,
/*
|--------------------------------------------------------------------------
| Requirement to sign messages.
|--------------------------------------------------------------------------
|
| Indicates a requirement for the <samlp:Response>, <samlp:LogoutRequest> and
| <samlp:LogoutResponse> elements received by this SP to be signed.
|
*/
'wantMessagesSigned' => true,
/*
|--------------------------------------------------------------------------
| Requirement to sign assertion elements.
|--------------------------------------------------------------------------
|
| Indicates a requirement for the <saml:Assertion> elements received by
| this SP to be signed.
|
*/
'wantAssertionsSigned' => true,
/*
|--------------------------------------------------------------------------
| Requirement to encrypt NameID.
|--------------------------------------------------------------------------
|
| Indicates a requirement for the NameID received by this SP to be encrypted.
|
*/
'wantNameIdEncrypted' => false,
/*
|--------------------------------------------------------------------------
| Authentication context.
|--------------------------------------------------------------------------
|
| Set to false and no AuthContext will be sent in the AuthNRequest,
|
| Set true or don't present this parameter and you will get an
| AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
|
| Set an array with the possible auth context values:
| ['urn:oasis:names:tc:SAML:2.0:ac:classes:Password', 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509']
|
*/
'requestedAuthnContext' => true,
],
/*
|--------------------------------------------------------------------------
| Contact information.
|--------------------------------------------------------------------------
|
| It is recommended to supply a technical and support contacts.
|
*/
'contactPerson' => [
'technical' => [
'givenName' => env('SAML2_CONTACT_TECHNICAL_NAME', env('APP_NAME', 'Technical')),
'emailAddress' => env('SAML2_CONTACT_TECHNICAL_EMAIL', 'no@reply.com')
],
'support' => [
'givenName' => env('SAML2_CONTACT_SUPPORT_NAME', env('APP_NAME', 'Support')),
'emailAddress' => env('SAML2_CONTACT_SUPPORT_EMAIL', 'no@reply.com')
],
],
/*
|--------------------------------------------------------------------------
| Organization information.
|--------------------------------------------------------------------------
|
| The info in en_US lang is recommended, add more if required.
|
*/
'organization' => [
'en-US' => [
'name' => env('SAML2_ORGANIZATION_NAME', env('APP_NAME', 'Name')),
'displayname' => env('SAML2_ORGANIZATION_NAME', env('APP_NAME', 'Name')),
'url' => env('SAML2_ORGANIZATION_URL', env('APP_URL', 'http://url'))
],
],
/**
* Default tenant UUID
*/
'default_tenant_uuid' => env('SAML2_DEFAULT_TENANT_UUID', null)
];

View file

@ -1,82 +0,0 @@
<?php
return [
/**
* Single route names
*
* Ex:
*
* 'singlePages' => [
* 'homepage',
* ],
*/
'singlePages' => [
'home',
'engagments',
'local',
'recipes.index',
'ranges.index',
'contact.show',
'map',
'policies',
'legals'
],
/**
* Model namespaces with filters
*
* Ex:
* 'cruds' => [
* [
* 'model' => 'App\Models\Product',
* 'filters' => [
* ['published', '=', true]
* ]
* ],
* [
* 'model' => 'App\Models\Article',
* 'filters' => []
* ]
* ],
*/
'cruds' => [
[
'model' => 'App\Models\Product',
'filters' => [
['status', '=', true]
],
],
[
'model' => 'App\Models\Recipe',
'filters' => [
['status', '=', true]
],
],
[
'model' => 'App\Models\Range',
'filters' => [
['status', '=', true]
],
]
],
/**
* Enable or disable schedule for the job
* If set to true the sitemap generator will be run
* using the 'cron' instruction
*/
'schedule' => true,
/**
* If you want to set specific
* instruction for when to schedule job
* you can set this with a cron instruction
* You can generate one using https://crontab.guru/
*
* schedule must be set to true
*
* Default is every day at 15:45
*/
'cron' => '45 15 * * *',
];

View file

@ -15,8 +15,8 @@ class UserFactory extends Factory
public function definition()
{
return [
'first_name' => $this->faker->firstName(),
'last_name' => $this->faker->lastName(),
'firstname' => $this->faker->firstName(),
'lastname' => $this->faker->lastName(),
'email' => $this->faker->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',

View file

@ -1,37 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('static_pages', function (Blueprint $table) {
$table->id();
$table->unsignedTinyInteger('type')->unique()->comment('Page type (StaticPageEnum)');
$table->string('seo_title')->comment('Seo title about 70 chars as recommended');
$table->string('seo_description')->comment('Seo description about 160 chars as recommended');
$table->string('title')->comment('Title for the page');
$table->string('slug')->unique()->comment('Unique title slug');
$table->text('content')->comment('WYSIWYG text content using ckeditor');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('static_pages');
}
};

View file

@ -1,39 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('activity_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained('users')->nullOnDelete()
->comment('Id of the user associated, or nullable for a deleted user.');
$table->boolean('is_anonymous')->comment('If there is no user connected when action was realised.');
$table->boolean('is_console')->comment('If the action was realised in console.');
$table->string('model_class')->comment('Target model.');
$table->integer('model_id')->comment('Id of the target model.');
$table->integer('event')->comment('Event of this activity (ActivityLogsEventEnum).');
$table->json('data')->nullable()->comment('List of changes (old and new values).');
$table->timestamp('created_at')->comment('The date on which the user was published.');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('activity_logs');
}
};

View file

@ -1,50 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
// Drop.
$table->dropColumn('email_verified_at');
$table->dropColumn('password');
// Rename.
$table->renameColumn('firstname', 'first_name');
$table->renameColumn('lastname', 'last_name');
// New columns.
$table->unsignedTinyInteger('role')->after('remember_token')
->comment('Role of the user who defines the rights/access.');
$table->boolean('published')->after('role')
->comment('Is the user published ?');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
// New columns.
$table->timestamp('email_verified_at')->nullable()->after('email');
$table->string('password')->after('email_verified_at');
// Rename.
$table->renameColumn('first_name', 'firstname');
$table->renameColumn('last_name', 'lastname');
// Drop.
$table->dropColumn('role');
$table->dropColumn('published');
});
}
};

View file

@ -1,29 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('static_pages', function (Blueprint $table) {
$table->text('seo_title')->nullable()->comment('Seo title about 70 chars as recommended')->change();
$table->text('seo_description')->nullable()->comment('Seo description about 160 chars as recommended')->change();
$table->text('content')->nullable()->comment('WYSIWYG text content using ckeditor')->change();
$table->mediumText('data')->nullable()->after('content')->comment('Static page data.');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
}
};

View file

@ -1,154 +0,0 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// HOMEPAGE
$data = [
'left_title' => [
'label' => 'Titre de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'Des produits bien élevés qui ont le goût de la qualité',
'validate' => 'required|string|between:3,255',
'required' => true
],
'left_subtitle' => [
'label' => 'Sous-titre de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'Garantis Poulet 100% local',
'validate' => 'required|string|between:3,255',
'required' => true
],
'left_button_text' => [
'label' => 'Texte du bouton de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'Nous Découvrir >',
'validate' => 'required|string|between:3,255',
'required' => true
],
'right_title' => [
'label' => 'Titre de la section de droite',
'inputType' => InputType::string->value,
'value' => 'Des produits de qualité bons et accessibles',
'validate' => 'required|string|between:3,255',
'required' => true
],
'right_subtitle' => [
'label' => 'Sous-titre de la section de droite',
'inputType' => InputType::string->value,
'value' => 'Pour manger sain et régional',
'validate' => 'required|string|between:3,255',
'required' => true
],
'right_button_text' => [
'label' => 'Texte du bouton de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'Voir nos recettes >',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_title' => [
'label' => 'Titre de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Chez Lionor le',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_title_2' => [
'label' => 'Titre n°2 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Poulet 100% local',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_title_3' => [
'label' => 'Titre n°3 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'C\'est...',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_1' => [
'label' => 'Texte n°1 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des Volailles nées, élevées et préparées en Hauts-de-France.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_2' => [
'label' => 'Texte n°2 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des éleveurs animés par le développement de leur région.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_3' => [
'label' => 'Texte n°3 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des volailles bien élevées qui assurent des produits bons et accessibles.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_4' => [
'label' => 'Texte n°4 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Une filière soucieuse de la santé des hommes, des animaux et de la terre.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'product_title' => [
'label' => 'Titre de la section n°3',
'inputType' => InputType::string->value,
'value' => 'Découvrez nos volailles bien élevées',
'validate' => 'required|string|between:3,255',
'required' => true
],
'product_subtitle' => [
'label' => 'Titre n°2 de la section n°3',
'inputType' => InputType::string->value,
'value' => 'qui ont le goût de la qualité',
'validate' => 'required|string|between:3,255',
'required' => true
],
];
$staticPage = StaticPage::where('type', StaticPageType::homepage)->first();
if ($staticPage) {
$staticPage->data = $data;
$staticPage->saveQuietly();
} else {
StaticPage::query()->forceCreateQuietly(
[
'type' => StaticPageType::homepage->value(),
'seo_title' => 'Lionor, des volailles en Nord',
'seo_description' => '100% local, des produits bien élevés, de qualité et accessibles.',
'title' => 'Accueil',
'slug' => 'accueil',
'data' => $data
]
);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('static_pages', function () {
$staticPage = StaticPage::query()->where('type', StaticPageType::homepage)->first();
$staticPage->data = [];
$staticPage->saveQuietly();
});
}
};

View file

@ -1,119 +0,0 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// OUR PRODUCTS
$data = [
'page_title' => [
'label' => 'Titre de la section n°1',
'inputType' => InputType::string->value,
'value' => 'Nos volailles bien élevées',
'validate' => 'required|string|between:3,255',
'required' => true
],
'page_title_2' => [
'label' => 'Titre n°2 de la section n°1',
'inputType' => InputType::string->value,
'value' => 'qui ont le goût de la qualité',
'validate' => 'required|string|between:3,255',
'required' => true
],
'left_title_1' => [
'label' => 'Titre n°1 de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'Trouvez l\'inspiration',
'validate' => 'required|string|min:15',
'required' => true
],
'left_title_2' => [
'label' => 'Titre n°2 de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'avec nos recettes',
'validate' => 'required|string|min:15',
'required' => true
],
'left_title_3' => [
'label' => 'Titre n°3 de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'de volailles bonnes',
'validate' => 'required|string|min:15',
'required' => true
],
'left_title_4' => [
'label' => 'Titre n°4 de la section de gauche',
'inputType' => InputType::string->value,
'value' => 'et accessibles',
'validate' => 'required|string|min:15',
'required' => true
],
'right_title_1' => [
'label' => 'Titre n°1 de la section de droite',
'inputType' => InputType::string->value,
'value' => 'Découvrez les engagements',
'validate' => 'required|string|min:15',
'required' => true
],
'right_title_2' => [
'label' => 'Titre n°2 de la section de droite',
'inputType' => InputType::string->value,
'value' => 'd\'une filière engagée',
'validate' => 'required|string|min:15',
'required' => true
],
'right_title_3' => [
'label' => 'Titre n°3 de la section de droite',
'inputType' => InputType::string->value,
'value' => 'pour manger bon, sain',
'validate' => 'required|string|min:15',
'required' => true
],
'right_title_4' => [
'label' => 'Titre n°4 de la section de droite',
'inputType' => InputType::string->value,
'value' => 'et Poulet 100% local',
'validate' => 'required|string|min:15',
'required' => true
],
];
$staticPage = StaticPage::where('type', StaticPageType::our_products)->first();
if ($staticPage) {
$staticPage->data = $data;
$staticPage->saveQuietly();
} else {
StaticPage::query()->forceCreateQuietly(
[
'type' => StaticPageType::our_products->value(),
'seo_title' => 'Nos volailles bien élevées qui ont le goût de la qualité',
'seo_description' => 'Liste de toutes nos gammes de produits.',
'title' => 'Nos produits',
'slug' => 'gammes',
'data' => $data
]
);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('static_pages', function () {
$staticPage = StaticPage::query()->where('type', StaticPageType::our_products)->first();
$staticPage->data = [];
$staticPage->saveQuietly();
});
}
};

View file

@ -1,162 +0,0 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// OUR PRODUCTS
$data = [
'commitment_title_1' => [
'label' => 'Titre de l\'engagement n°1',
'inputType' => InputType::string->value,
'value' => 'Des produits de qualité bons et accessibles',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_subtitle_1' => [
'label' => 'Sous-titre de l\'engagement n°1',
'inputType' => InputType::string->value,
'value' => 'Garantis Poulet 100% Hauts-De-France',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_text_1' => [
'label' => 'Texte de l\'engagement n°1',
'inputType' => InputType::text->value,
'value' => '<p>Avec notre label Poulet 100% LOCAL, certifié et contrôlé par un cahier des charges précis, nous garantissons l\'origine de nos produits issus de poulets nés, élevés et préparés en région des Hauts de France.</p>',
'validate' => 'required|string|min:15',
'required' => true
],
'commitment_title_2' => [
'label' => 'Titre de l\'engagement n°2',
'inputType' => InputType::string->value,
'value' => 'Une filière soucieuse de la santé',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_subtitle_2' => [
'label' => 'Sous-titre de l\'engagement n°2',
'inputType' => InputType::string->value,
'value' => 'Des hommes, des animaux et de la terre',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_text_2' => [
'label' => 'Texte de l\'engagement n°2',
'inputType' => InputType::text->value,
'value' => '<p>LIONOR, c\'est une filière qui s\'engage pour élever des volailles dans le respect du vivant et qui favorise l\'économie locale. Fiers de nos 70 éleveurs et de nos 235 salariés, unis par la passion de leur métier, nous participons activement à la vie sociale et économique de notre région.</p>',
'validate' => 'required|string|min:15',
'required' => true
],
'commitment_title_3' => [
'label' => 'Titre de l\'engagement n°3',
'inputType' => InputType::string->value,
'value' => 'Des femmes et des hommes',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_subtitle_3' => [
'label' => 'Sous-titre de l\'engagement n°3',
'inputType' => InputType::string->value,
'value' => 'Qui maîtrisent la filière du grain à l\'assiette',
'validate' => 'required|string|between:3,255',
'required' => true
],
'commitment_text_3' => [
'label' => 'Texte de l\'engagement n°3',
'inputType' => InputType::text->value,
'value' => '<p>Grâce à la maitrise totale de notre filière, allant de l\'alimentation aux produits régionaux de qualité, en passant pas nos élevages se situant dans un rayon de 50kms autour de notre entreprise, nous garantissons aux consommateurs finaux, une qualité et une traçabilité irréprochables. Notre crédo : "élever bien pour manger sain en région des Hauts de France."</p>',
'validate' => 'required|string|min:15',
'required' => true
],
'container_title' => [
'label' => 'Titre de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Chez Lionor le',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_title_2' => [
'label' => 'Titre n°2 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Poulet 100% local',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_title_3' => [
'label' => 'Titre n°3 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'C\'est...',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_1' => [
'label' => 'Texte n°1 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des Volailles nées, élevées et préparées en Hauts-de-France.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_2' => [
'label' => 'Texte n°2 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des éleveurs animés par le développement de leur région.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_3' => [
'label' => 'Texte n°3 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Des volailles bien élevées qui assurent des produits bons et accessibles.',
'validate' => 'required|string|between:3,255',
'required' => true
],
'container_item_4' => [
'label' => 'Texte n°4 de la section n°2',
'inputType' => InputType::string->value,
'value' => 'Une filière soucieuse de la santé des hommes, des animaux et de la terre.',
'validate' => 'required|string|between:3,255',
'required' => true
],
];
$staticPage = StaticPage::where('type', StaticPageType::our_commitments)->first();
if ($staticPage) {
$staticPage->data = $data;
$staticPage->saveQuietly();
} else {
StaticPage::query()->forceCreateQuietly(
[
'type' => StaticPageType::our_commitments->value(),
'seo_title' => 'Les engagements Lionor',
'seo_description' => 'Des produits de qualité, une filière soucieuse de la santé, maîtrise la filière du grain à l\'assiette',
'title' => 'Nos engagements',
'slug' => 'nos-engagements',
'data' => $data
]
);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('static_pages', function () {
$staticPage = StaticPage::query()->where('type', StaticPageType::our_commitments)->first();
$staticPage->data = [];
$staticPage->saveQuietly();
});
}
};

View file

@ -1,110 +0,0 @@
<?php
use App\Enums\InputType;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// BANNER SECTION
$data = [
'banner_title_1' => [
'label' => 'Titre n°1 du bandeau',
'inputType' => InputType::string->value,
'value' => 'Choisir',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_title_2' => [
'label' => 'Titre n°2 du bandeau',
'inputType' => InputType::string->value,
'value' => 'les volailles',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_title_3' => [
'label' => 'Titre n°3 du bandeau',
'inputType' => InputType::string->value,
'value' => 'Lionor, c\'est...',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_title_1' => [
'label' => 'Titre de l\'élément n°1',
'inputType' => InputType::string->value,
'value' => 'Soutenir',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_text_1' => [
'label' => 'Texte de l\'élément n°1',
'inputType' => InputType::string->value,
'value' => 'L\'emploi local et les éleveurs régionaux',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_title_2' => [
'label' => 'Titre de l\'élément n°2',
'inputType' => InputType::string->value,
'value' => 'Choisir la fraîcheur',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_text_2' => [
'label' => 'Texte de l\'élément n°2',
'inputType' => InputType::string->value,
'value' => 'Et consommer des produits de proximité',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_title_3' => [
'label' => 'Titre de l\'élément n°3',
'inputType' => InputType::string->value,
'value' => 'Goûter la qualité',
'validate' => 'required|string|between:3,255',
'required' => true
],
'banner_item_text_3' => [
'label' => 'Texte de l\'élément n°3',
'inputType' => InputType::string->value,
'value' => 'D\'un bout à l\'autre de la filière',
'validate' => 'required|string|between:3,255',
'required' => true
],
];
$staticPage = StaticPage::where('type', StaticPageType::banner_section)->first();
if ($staticPage) {
$staticPage->data = $data;
$staticPage->saveQuietly();
} else {
StaticPage::query()->forceCreateQuietly(
[
'type' => StaticPageType::banner_section->value(),
'title' => 'Bannière',
'slug' => 'banner',
'data' => $data
]
);
}
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('static_pages', function () {
$staticPage = StaticPage::query()->where('type', StaticPageType::banner_section)->first();
$staticPage->data = [];
$staticPage->saveQuietly();
});
}
};

View file

@ -2,7 +2,6 @@
namespace Database\Seeders;
use Database\Seeders\StaticPageSeeder;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
@ -20,7 +19,6 @@ class DatabaseSeeder extends Seeder
ProductSeeder::class,
RecipeSeeder::class,
ShopSeeder::class,
StaticPageSeeder::class,
]);
}
}

View file

@ -1,148 +0,0 @@
<?php
namespace Database\Seeders;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Seeder;
class StaticPageSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
// Legals.
StaticPage::query()->updateOrCreate(
[
'type' => StaticPageType::legals,
],
[
'seo_title' => 'Mentions légales et CGU',
'seo_description' => 'Mentions légales et conditions générales d\'utilisation du site internet du site internet Lionor',
'title' => 'Mentions légales',
'type' => StaticPageType::legals,
'content' => "
<h2>Introduction</h2>
<p>Nous vous remercions de lire ces conditions générales d'utilisation qui régissent les conditions d'accès et d'utilisation du site <a target='_blank' href='https://lionor.s-web.test' rel='noreferrer noopener'>lionor.fr</a>
<br><br>
<strong>Nous vous invitons à consulter régulièrement cette rubrique car la société Lionor peut être amenée à la modifier à tout moment.</strong>
<br><br>
<strong>Tout utilisateur déclare avoir lu et accepté les mentions légales et les conditions générales d'utilisation et s'engage à s'y conformer.</strong><br><br><br>
Mise à jour le 15/03/2023.</p>
<h2>Mentions légales</h2>
<p>Lionor RTE DE LA GARE 59189 STEENBECQUE<br>Tél : +33(0)328436055 SAS au capital de 1 646 400 euros - RCS de Dunkerque sous le numéro 305 507 303<br><br>
<strong>Editeur du Site</strong> : LDC Services 98 à 102 Rue Albert Einstein 72000 Le Mans<br>
<strong>Directeur de la publication</strong> : Sylvain Gouet, Responsable Web Marketing Groupe LDC<br>
<strong>Conception du site et réalisation</strong> : LDC Services 98 à 102 Rue Albert Einstein 72000 Le Mans<br>
<strong>Illustrations</strong> : Médiapole 2020<br>
<strong>Le site est hébergé par</strong> : OVH, via LDC, SAS au capital de 10 069 020 euros, RCS Lille Métropole 537 407 926, 2 rue Kellermann - 59100 Roubaix, Tel : 09 72 10 10 07, Mail : hosting@ml.ovh.net<br></p>
<h2>Droit d'auteur - Droit des marques - Protection des bases de données</h2>
<p>La présentation et le contenu du site constituent, ensemble, une œuvre protégée par les lois en vigueur des droits d'auteur, de la propriété intellectuelle et industrielle.
<br><br>
Les créations apparaissant sur le site (dessins, photographies, images, textes, séquences animées sonores ou non et autres documentations représentées), sont objets de droits d'auteur, de propriété industrielle et/ou intellectuelle et sont, selon les cas, propriété de la société Lionor ou de tiers ayant autorisé la société Lionor à les utiliser.
<br>Aucune exploitation commerciale, reproduction, représentation, utilisation, adaptation, modification, incorporation, traduction, commercialisation, partielle ou intégrale de ces éléments (y compris ceux téléchargeables ou copiables) ne pourra en être faite sans l'accord préalable et écrit de la société Lionor, à l'exception de l'utilisation pour un usage privé sous réserve des dispositions différentes voire plus restrictives du Code de la propriété intellectuelle.
<br>Toute utilisation non expressément autorisée peut engager la responsabilité civile et/ou pénale de son auteur. Lionor se réserve le droit d'engager des poursuites judiciaires à l'encontre de toute personne qui n'aurait pas respecté cette interdiction.</p>
<h2>Vie privée - Données personnelles - Cookies</h2>
<p>Nous vous invitons à vous reporter à notre Charte de confidentialité en <a target='_blank' href='https://lionor.s-web.test/politique-de-donnees-personnelles' rel='noreferrer noopener'>cliquant ici</a>.</p>
<h2>Mentions Sanitaires :</h2>
<p>Pour votre santé :<br>- mangez au moins cinq fruits et légumes par jour<br>- pratiquez une activité physique régulière<br>- évitez de manger trop gras, trop sucré, trop salé<br>- évitez de grignoter entre les repas</p>
<p>Pour toute information complémentaire, consultez le site <a target='_blank' href='https://www.mangerbouger.fr' rel='noreferrer noopener'>www.mangerbouger.fr</a></p>
<h2>Responsabilité</h2>
<p>Lionor a déployé ses meilleurs efforts pour s'assurer de l'exactitude des informations présentes sur son site internet (ci-après le Site). Cependant, Lionor n'est responsable ni de l'exactitude, ni des erreurs, ni des omissions contenues sur le Site. L'utilisateur est seul responsable de l'utilisation de telles informations.
<br>Lionor ne donne aucune garantie, explicite ou implicite, et n'assume aucune responsabilité qui pourrait résulter de l'accès ou de l'utilisation de ce Site incluant toutes détériorations ou virus qui pourraient infecter l'équipement informatique ou tout autre bien de l'utilisateur.
<br>Par ailleurs.
<br>Lionor pourra unilatéralement et à tout moment interrompre temporairement ou définitivement l'accès à tout ou partie du Site, y compris notamment le contenu, les fonctionnalités ou les heures de disponibilité, sans que sa responsabilité ne puisse être recherchée et sans que cela ne puisse donner lieu à l'allocation de dommages et intérêts de quelque nature que ce soit.
<br>Les pages du Site peuvent contenir des liens hypertextes renvoyant vers d'autres sites internet gérés par des sociétés distinctes de Lionor, et sur lesquels Lionor n'exerce aucune sorte de contrôle. Lionor n'est pas responsable du contenu de tout autre site auquel vous pourriez avoir accès via le Site ou les autres sites internet du groupe LDC. Tout utilisateur ou visiteur du Site ne peut mettre en place un lien hypertexte en direction de ce Site sans l'autorisation expresse préalable de Lionor.</p>",
]
);
// Policies.
StaticPage::query()->updateOrCreate(
[
'type' => StaticPageType::confidentiality,
],
[
'seo_title' => 'Politique des données personnelles',
'seo_description' => 'Lionor vous informe sur les données que nous collectons, comment nous les traitons, dans quelles finalités.',
'title' => 'Politique des données personnelles',
'type' => StaticPageType::confidentiality,
'content' => "
<h2>Introduction</h2>
<p>Le nouveau Règlement Général de l'Union Européenne sur la Protection des Données (RGPD), entré en vigueur le 25 mai 2018, vise à mieux encadrer la collecte, le traitement et l'utilisation de vos données personnelles.<br>Lionor, en sa qualité de responsable du traitement, renforce ses engagements pour garantir la protection et la sécurité des données à caractère personnel que vous lui confiez.<br><br></p>
<p>A ce titre, la présente Charte de confidentialité vous informe sur le type de données que nous collectons, comment nous les traitons, dans quelles finalités, de manière à rendre leur utilisation la plus adaptée à vos besoins.<br><br>
Le responsable de traitement, qui collecte et gère vos données, est la société Lionor, SA au capital social de 1 646 400 immatriculée au registre du commerce et des sociétés de Dunkerque sous le numéro 305 507 303, dont le siège social est RTE DE LA GARE 59189 STEENBECQUE.<br><br>
Pour toute question relative à notre Charte de confidentialité, merci de nous contacter via le formulaire de contact suivant:<br><a target='_blank' href='https://www.ldc.fr/rgpd/professionnels/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/professionnels/</a><br><a target='_blank' href='https://www.ldc.fr/rgpd/consommateurs/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/consommateurs/</a>
<br><a target='_blank' href='https://www.ldc.fr/rgpd/salaries/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/salaries/</a><br><br>Nous vous invitons à consulter régulièrement cette rubrique car Lionor peut être amené à la modifier à tout moment.
<br>La présente Charte de confidentialité a été mise à jour le 15/03/2023.</p>
<h2>Nos Engagements</h2>
<p>Conformément à la réglementation relative à la protection des données personnelles et à la vie privée, notamment la loi n°78-17 du 6 janvier 1978 relative à l'informatique, aux fichiers et aux libertés dite loi « Informatique et Libertés » et au Règlement (UE) 2016/679 du 25 mai 2018 relatif à la protection des personnes physiques à l'égard du traitement des données à caractère personnel et à la libre circulation de ces données, (ci-après le « Règlement RGPD»), Lionor s'engage à respecter les principes suivants : <br><br>
<strong>Consentement</strong>: vous devez avoir consenti à la collecte et au traitement de vos données. Si tel n'était pas le cas ou si vous avez changé d'avis, merci de vous reporter à l'article ci-dessous relatif à vos droits.<br><br>
<strong>Limitation des données collectées</strong>: nous ne collectons que les données qui sont strictement nécessaires au traitement et à l'utilisation envisagée. Nous vous rappelons qu'il n'y a aucune obligation à nous transmettre ces données. Toutefois certains services ou opérations promotionnelles nécessitent certaines données, faute de quoi vous ne pourriez y accéder.<br><br>
<strong>Limitation des finalités</strong>: les données que nous collectons sont traitées uniquement dans les finalités décrites ci-dessous.<br><br>
<strong>Limitation des durées de conservation</strong>: vos données sont conservées uniquement le temps nécessaire aux finalités de traitement pour lequel elles ont été collectées. Au-delà de cette durée si aucune obligation légale impose leur conservation, nous veillerons à ce que vos données soient supprimées, détruites ou anonymisées.<br><br>
<strong>Protection des données</strong>: en vue de sécuriser vos données et leur traitement, nous avons mis en place des mesures techniques et organisationnelles détaillées ci-dessous.<br><br>
<strong>Transparence et loyauté</strong>: lorsque Lionor collecte une donnée vous concernant, nous vous informons de l'utilisation qui en sera faite. C'est pourquoi nous vous conseillons de lire attentivement les informations qui vous sont transmises à travers notre site internet.</p>
<h2>Quelles données personnelles sont collectées ?</h2>
<p>Au sens du Règlement RGPD, une donnée à caractère personnel désigne toute information relative à une personne physique identifiée ou identifiable.<br><br>Les données personnelles collectées varient en fonction du but de la collecte et du service dont vous souhaitez bénéficier.<br>
<br>La société Lionor est amenée à collecter les catégories de données personnelles suivantes:<br><br>- coordonnées personnelles (civilité, nom, prénom, adresse e-mail, adresse postale) ; <br>- coordonnées personnelles non obligatoires (date de naissance, numéro de téléphone); <br>- identifiants utilisés pour accéder à votre compte en ligne (adresse IP, adresse e-mail et mot de passe);<br>- données relatives aux tickets de caisse de l'utilisateur ainsi que son RIB ou son compte Paypal;<br>- historiques de navigation, telles que les pages visitées et actions sur le site.<br>
<br>Vous vous engagez à fournir des données personnelles actualisées et valides et garantissez ne faire aucune fausse déclaration et ne donner aucune information erronée.</p>
<h2>Pourquoi vos données personnelles sont collectées ?</h2><p>Vos données peuvent être collectées dans la mesure où vous avez consenti de manière spécifique, libre et éclairée. Elles peuvent également être collectées pour satisfaire à l'exécution d'une obligation légale ou dans le cadre de la relation contractuelle construite avec vous (notamment conditions générales d'utilisation, conditions générales de vente) ou encore dans l'intérêt légitime de Lionor (par exemple pour assurer la sécurité des transactions).<br>
<br>Nous collectons vos données personnelles afin de vous fournir la meilleure expérience utilisateur à travers notre site internet.<br><br><strong>Ces données sont collectées aux fins suivantes </strong><br>- Gestion des fonctionnalités et de la qualité du site internet<br>- Gestion de votre inscription à notre Newsletter et de votre Compte utilisateur<br>- Accès à votre Compte utilisateur<br>- Gestion du recrutement<br>- Traitement de vos demandes de renseignements<br>- Communication des informations (promotions, nouveaux produits, activité de la marque)<br>- Gestion de votre participation à nos jeux, tirages au sort, activités promotionnelles<br>- Développement et amélioration des produits Lionor<br>- Statistiques<br>- Proposition de produits personnalisés et de communications adaptées ou pour recommandation de produits en fonction des besoins de consommateurs<br>- Partages sur les réseaux sociaux</p>
<h2>Quels sont vos droits ?</h2><p>Vous disposez d'un certain nombre de droits concernant vos données personnelles listés ci-dessous. <br><br>
<strong>Droit d'accès</strong> Vous pouvez demander la communication de vos données sous une forme accessible, la confirmation que vos données font ou ne font plus l'objet d'un traitement ainsi que la communication des informations relatives à vos données. Afin que vos données personnelles soient exactes, complètes, pertinentes et à jour, vous avez le droit d'accéder, de corriger ou de mettre à jour vos données personnelles à tout moment.<br><br>
<strong>Droit de rectification</strong><br>Vous êtes en droit de demander à ce que vos données soient rectifiées, complétées ou mises à jour lorsqu'elles sont inexactes, incomplètes, équivoques ou périmées.<br><br>
<strong>Droit à l'effacement </strong><br>Vous avez le droit de demander la suppression de vos données personnelles notamment lorsqu'elles ne sont plus nécessaires aux finalités pour lesquelles elles ont été collectées. Si les données personnelles collectées ne sont plus nécessaires à aucune finalité et qu'aucune obligation légale nous impose de les conserver, nous mettrons tout en œuvre pour supprimer, détruire ou anonymiser ces données.<br><br>
<strong>Droit à la limitation du traitement</strong><br>Vous avez le droit de demander la limitation du traitement de vos données personnelles si : <br><br>- vous contestez l'exactitude des données;<br>- vous souhaitez limiter le traitement des données qui n'avaient pas été traitées de manière licite plutôt que leur suppression; <br>- nous n'avons plus besoin de vos données personnelles mais celles-ci sont nécessaires pour la constatation, l'exercice ou la défense de vos droits en justice; <br>- vous vous êtes opposés au traitement de vos données personnelles et attendez de savoir si vos intérêts liés à cette objection l'emportent sur les motifs légitimes que nous poursuivons. <br><br>
<strong>Droit à la portabilité de vos données </strong><br>Lorsque le traitement est effectué à partir de procédés automatisés et que vous avez librement consenti au traitement de vos données personnelles pour une finalité que nous vous avions communiqué au préalable; ou lorsque nous traitons vos données dans le cadre d'une opération commerciale (ex: vous fournir un produit) vous avez le droit de demander:<br>- à recevoir vos données personnelles dans un format structuré, couramment utilisé et lisible par une machine;<br>- à ce que vos données personnelles soient transmises directement à une autre responsable de traitement, lorsque que cela est techniquement possible.<br><br>
<strong>Droit d'opposition</strong><br>Lorsque vos données sont traitées à des fins de prospection commerciale, vous avez le droit, à tout moment, de vous opposer au traitement de vos données personnelles et ce sans motif.</p>
<h2>Droit de porter réclamation auprès de la CNIL</h2>
<p>Si vous estimez que le traitement de vos données personnelles constitue une violation de la réglementation, vous pouvez introduire une réclamation auprès d'une autorité de contrôle, telle que la CNIL.<br><br>
<strong>Vous pouvez exercer ces droits à tout moment en nous contactant via le formulaire suivant :</strong><br><a target='_blank' href='https://www.ldc.fr/rgpd/professionnels/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/professionnels/</a><br><a target='_blank' href='https://www.ldc.fr/rgpd/consommateurs/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/consommateurs/</a><br><a target='_blank' href='https://www.ldc.fr/rgpd/salaries/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/salaries/</a><br><br>
<strong>Vous recevrez une réponse de notre part dans un délai d'un mois à compter de la réception de la demande. En cas de demande complexe ou du nombre important de demandes, le délai peut être prolongé de deux mois. </strong><br><br>
<strong>L'exercice des droits se fait à titre gratuit. Si toutefois la demande était manifestement infondée ou excessive, Lionor se réserve la possibilité d'appliquer des frais administratifs ou de refuser de donner suite à la demande. </strong></p>
<h2>Comment vos données personnelles sont protégées ?</h2>
<p>Afin d'assurer la sécurité de vos données personnelles nous avons mis en place des mesures de sécurité, notamment des contrôles d'accès, des pare-feux, des serveurs sécurisés.<br><br>Ces mesures visent à protéger vos données personnelles contre toute perte, détournement, mauvaise utilisation, divulgation, modification, interférence, dommage, accès non autorisé des données.<br><br>En cas de violation de vos données personnelles susceptible d'engendrer un risque pour vos droits et libertés, Lionor en notifie la violation à la CNIL dans les meilleurs délais et, si possible, 72 heures au plus tard après en avoir pris connaissance. Lionor vous en informera dans les meilleurs délais.</p>
<h2>Accès à vos données personnelles :</h2>
<p>Vos données personnelles sont destinées au personnel de Lionor habilité à les traiter, notamment le service marketing, développement durable, informatique, commercial, qualité et administratif.</p>
<h2>Partage de vos données personnelles :</h2>
<p>Conformément à la règlementation, quand nous sommes amenés à partager vos données personnelles avec des sous-traitants pour certaines finalités décrites dans la présente Charte, nous nous assurons que leur dispositif en matière de sécurité des données et de leur stockage est au moins aussi important que celui de notre organisation. Nous veillons également à ce qu'ils respectent les lois relatives à la protection des données personnelles dans des conditions au moins équivalentes à celles que nous respectons.<br>
<br>Par ailleurs, les sous-traitants étant amenés à traiter de vos données personnelles pour notre compte et sur nos instructions, nous nous engageons à ce que vos données personnelles ne soient pas partagées ou divulguées pour des finalités autres que celles décrites dans la présente Charte. Nous pouvons cependant être amenés à transmettre vos données, pour des finalités autres que celles prévues dans la Charte, dans les cas où leur communication est requise par la loi et les autorités gouvernementales.</p>
<h2>Transfert de vos données en dehors de l'espace économique Européen :</h2>
<p>Nous nous informons que vos données personnelles ne sont pas traitées en dehors de l'Espace Economique Européen (EEE). En cas de changement, nous vous en avertirons et mettrons tout en œuvre pour que ce transfert international de données dispose d'un niveau et de garanties de sécurité adéquats.</p>
<h2>Utilisation des cookies</h2>
<p>Les cookies sont des petits fichiers textes contenant des informations propres vous concernant stockés sur le disque dur de l'équipement informatique avec lequel vous vous êtes connectés sur le site internet et pouvant être lus seulement par le serveur qui l'a fourni. Les cookies sont gérés par votre navigateur internet.<br><br>Les cookies indiquent la fréquentation des pages que vous visitez, ce qui nous aide à connaître les informations qui vous intéressent. Cela permet de vous offrir plus de contenu qui vous intéresse et moins de contenu qui ne vous intéresse pas. <br><br>Les cookies vous permettent de stocker des préférences et les noms des utilisateurs, enregistrer des produits et des services, personnaliser des pages ou faire de la publicité ciblée.<br>
<br>A travers ces cookies, vos données personnelles peuvent être recueilles telles que :<br>
<br>- des informations concernant le navigateur de votre appareil et le système d'exploitation que vous utilisez;
<br>- l'adresse IP de l'appareil que vous utilisez;
<br>- les pages Web que vous consultez;
<br>- les liens sur lesquelles vous cliquez pendant que vous êtes en interaction avec nos services.<br>
<br>Le site internet <a target='_blank' href='https://lionor.s-web.test' rel='noreferrer noopener'>lionor.fr</a> utilise trois types de cookies qui ont des finalités différentes. Tous les cookies de type non techniques doivent recueillir votre consentement avant d'être installés. Vous pouvez donner votre consentement sur la page d'accueil du site internet, en cliquant sur « accepter ».<br>
<br>- Les cookies strictement nécessaires et fonctionnels Ces cookies sont nécessaires au bon fonctionnement du site internet. Ils vous permettent d'utiliser les principales fonctionnalités. Sans ces cookies, vous ne pourrez pas utiliser normalement le site internet.
<br>- Les cookies analytiques Ces cookies nous permettent de connaître l'utilisation et les performances du site internet et d'en améliorer son fonctionnement. <br>
<br>- Les cookies de ciblage<br>
<br>Ces cookies sont utilisés pour vous présenter des publicités ou vous adresser des informations adaptées à vos centres d'intérêts en dehors du site <a target='_blank' href='https://lionor.s-web.test' rel='noreferrer noopener'>lionor.fr</a> lors de votre navigation sur Internet. Le refus de ces cookies publicitaires n'a pas d'impact sur l'utilisation du site internet. Cependant, le fait de refuser les cookies publicitaires n'entraînera pas l'arrêt de la publicité sur Internet. Cela aura seulement pour effet d'afficher une publicité qui ne tiendra pas compte de vos centres d'intérêt ou de vos préférences.<br><br>
<strong>Vous avez le choix :</strong><br>
<br><strong>- d'accepter</strong> ces cookies en cliquant sur le bouton «Accepter» présent sur le bandeau Cookies, apparaissant au bas de votre page internet. Tant que vous n'avez pas accepté, les cookies ne pourront être stockés sur votre appareil; ou<br><br>
<strong>de refuser</strong> ces cookies via le bouton «Paramétrer» présent sur le bandeau Cookies ou les paramètres de votre navigateur internet.<br>
<br>Lorsque vous cliquez sur le bouton «Paramétrer» du bandeau Cookies, une liste s'affiche présentant les différents types de cookies par finalité. Si vous consentez au dépôt des cookies, il vous suffit de les activer puis de cliquer sur le bouton «Enregistrer» en bas de la fenêtre. Si vous refusez le dépôt des cookies, il vous suffit de cliquer sur le bouton «Enregistrer», le dépôt des cookies étant par défaut désactivé.<br>
<br>Pour en savoir plus sur la désactivation des cookies via votre navigateur internet :<br>
<br>Internet Explorer: <a target='_blank' href='http://windows.microsoft.com/fr-FR/windows-vista/Block-or-allow-cookies' rel='noreferrer noopener'>http://windows.microsoft.com/fr-FR/windows-vista/Block-or-allow-cookies</a>
<br>Chrome: <a target='_blank' href='http://support.google.com/chrome/bin/answer.py?hl=fr&amp;hlrm=en&amp;answer=95647' rel='noreferrer noopener'>http://support.google.com/chrome/bin/answer.py?hl=fr&amp;hlrm=en&amp;answer=95647</a>
<br>Firefox: <a target='_blank' href='https://support.mozilla.org/fr/kb/protection-renforcee-contre-pistage-firefox-ordinateur' rel='noreferrer noopener'>https://support.mozilla.org/fr/kb/protection-renforcee-contre-pistage-firefox-ordinateur</a>
<br>Safari: <a target='_blank' href='https://support.apple.com/fr-fr/guide/safari/sfri11471/mac' rel='noreferrer noopener'>https://support.apple.com/fr-fr/guide/safari/sfri11471/mac</a><br>
<br>La durée de conservation de ces informations dans votre appareil est d'une durée maximale de 13 mois. A l'expiration de ce délai, nous recueillerons de nouveau votre consentement. <br>Pour plus d'informations sur le paramétrage de vos cookies, vous pouvez consulter :<br><a target='_blank' href='http://www.youronlinechoices.com/fr/' rel='noreferrer noopener'>http://www.youronlinechoices.com/fr/</a><br><a target='_blank' href='https://www.allaboutcookies.org/' rel='noreferrer noopener'>https://www.allaboutcookies.org/</a></p>
<h2>Sites internet de tiers</h2>
<p>Le site internet Lionor peut être associé à des sites internet tiers. Les sites hébergés par des tiers sont hors de notre contrôle. Lorsque vous renseignez des données personnelles sur un site internet tiers, le traitement de ces données n'est pas effectué sur notre site internet, mais sur le site internet tiers concerné. Toute donnée personnelle que vous fournissez sera collectée et traitée conformément à la politique relative à la confidentialité des données du tiers concerné.<br>
<br>Par conséquent, nous n'assumons aucune responsabilité concernant les pratiques exercées par lesdits tiers en matière de protection des données.</p>
<h2>Droit applicable</h2>
<p>La présente Charte de confidentialité est régie et sera interprétée conformément au droit français.</p>
<h2>Nous contacter</h2>
<p>Si vous avez des questions, remarques ou réclamations relatives à la présente Charte de confidentialité, ou le traitement de vos données personnelles, veuillez-nous contacter à l'adresse :<br><br><a target='_blank' href='https://www.ldc.fr/rgpd/professionnels/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/professionnels/</a><br><a target='_blank' href='https://www.ldc.fr/rgpd/consommateurs/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/consommateurs/</a><br><a target='_blank' href='https://www.ldc.fr/rgpd/salaries/' rel='noreferrer noopener'>https://www.ldc.fr/rgpd/salaries/</a></p>
",
]
);
}
}

View file

@ -8,9 +8,9 @@ data:
APP_ENV: "${APP_APP_ENV}"
APP_KEY: "${APP_APP_KEY}"
APP_DEBUG: "${APP_APP_DEBUG}"
APP_URL: "https://${REDIRECT}${APP_DOMAIN}"
APP_URL: "https://www.${APP_DOMAIN}"
LOG_CHANNEL: "k8s"
DB_HOST: "${APP_DB_HOST}"
DB_HOST: "mariadb-service.common-services"
DB_DATABASE: "${APP_DB_DATABASE}"
DB_USERNAME: "${APP_DB_USERNAME}"
DB_PASSWORD: "${APP_DB_PASSWORD}"
@ -28,12 +28,6 @@ data:
GOOGLE_RECAPTCHA_VERSION: "${APP_GOOGLE_RECAPTCHA_VERSION}"
SENTRY_LARAVEL_DSN: "${APP_SENTRY_LARAVEL_DSN}"
MATOMO_SITE_ID: "${APP_MATOMO_SITE_ID}"
SAML2_SP_CERT_x509: "${APP_SAML2_SP_CERT_x509}"
SAML2_SP_CERT_PRIVATEKEY: "${APP_SAML2_SP_CERT_PRIVATEKEY}"
SAML2_CONTACT_TECHNICAL_EMAIL: "${APP_SAML2_CONTACT_TECHNICAL_EMAIL}"
SAML2_CONTACT_SUPPORT_EMAIL: "${APP_SAML2_CONTACT_SUPPORT_EMAIL}"
SAML2_DEFAULT_TENANT_UUID: "${APP_SAML2_DEFAULT_TENANT_UUID}"
UWIL_ADMIN_SECURE_CIDR_LIST: "${APP_GLOBAL_UWIL_ADMIN_SECURE_CIDR_LIST}"
---
apiVersion: v1
@ -69,22 +63,15 @@ spec:
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
resources:
limits:
cpu: "${APP_CI_CPU_LIMIT}"
memory: "${APP_CI_MEMORY_LIMIT}"
requests:
cpu: "${APP_CI_CPU_REQUEST}"
memory: "${APP_CI_MEMORY_REQUEST}"
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
imagePullSecrets:
- name: docker-registry-credentials
securityContext:
runAsUser: 82
runAsGroup: 82
fsGroup: 82
runAsUser: 33
runAsGroup: 33
fsGroup: 33
fsGroupChangePolicy: "OnRootMismatch"
volumes:
- name: storage
@ -102,10 +89,10 @@ spec:
app: ${CI_PROJECT_PATH_SLUG}-app
ports:
- port: 80
targetPort: 80
targetPort: 443
---
apiVersion: traefik.io/v1alpha1
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ${CI_PROJECT_PATH_SLUG}-ingress-route
@ -114,15 +101,30 @@ spec:
- web
- websecure
routes:
- match: Host(`${APP_DOMAIN}`) || Host(`www.${APP_DOMAIN}`)
- match: Host(`${APP_DOMAIN}`, `www.${APP_DOMAIN}`)
kind: Rule
services:
- name: ${CI_PROJECT_PATH_SLUG}-service
port: 80
middlewares:
- name: redirect-no-www-to-www
namespace: common-services
tls:
secretName: ${CRT_TLS_NAME}
secretName: $APP_DOMAIN
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: $APP_DOMAIN
spec:
dnsNames:
- $APP_DOMAIN
- www.${APP_DOMAIN}
secretName: $APP_DOMAIN
issuerRef:
name: $APP_CERT_ISSUER
kind: ClusterIssuer
---
apiVersion: batch/v1
@ -131,9 +133,6 @@ metadata:
name: ${CI_PROJECT_PATH_SLUG}-cronjob
spec:
schedule: "* * * * *"
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
@ -152,7 +151,7 @@ spec:
containers:
- name: ${CI_PROJECT_PATH_SLUG}-scheduled
image: $IMAGE_TAG
imagePullPolicy: IfNotPresent
imagePullPolicy: Always
command:
- "php"
args:
@ -171,8 +170,8 @@ spec:
persistentVolumeClaim:
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
securityContext:
runAsUser: 82
runAsGroup: 82
fsGroup: 82
runAsUser: 33
runAsGroup: 33
fsGroup: 33
fsGroupChangePolicy: "OnRootMismatch"
restartPolicy: OnFailure

307
package-lock.json generated
View file

@ -1,5 +1,5 @@
{
"name": "srcs",
"name": "lionor",
"lockfileVersion": 3,
"requires": true,
"packages": {
@ -11,7 +11,7 @@
"@fortawesome/free-regular-svg-icons": "latest",
"@fortawesome/free-solid-svg-icons": "latest",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@ldc-web/cookie-notice": "^6.0.3",
"@ldc-web/cookie-notice": "^3.0.8",
"@rushstack/eslint-patch": "^1.3.2",
"@sipec/vue3-tags-input": "^3.0.4",
"@types/bootstrap": "^5.2.7",
@ -31,11 +31,10 @@
"fslightbox": "^3.4.1",
"laravel-vite-plugin": "latest",
"leaflet": "^1.9.4",
"meow": "^9.0.0",
"postcss-discard-comments": "^5.1.2",
"prettier": "latest",
"print-js": "^1.6.0",
"resolve-url-loader": "^5.0.0",
"rollup-plugin-copy": "^3.5.0",
"sass": "latest",
"slugify": "^1.6.6",
"stylelint-config-idiomatic-order": "github:ream88/stylelint-config-idiomatic-order",
@ -76,12 +75,12 @@
}
},
"node_modules/@babel/code-frame": {
"version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
"integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==",
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dev": true,
"dependencies": {
"@babel/highlight": "^7.23.4",
"@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
},
"engines": {
@ -128,12 +127,12 @@
}
},
"node_modules/@babel/generator": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz",
"integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz",
"integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==",
"dev": true,
"dependencies": {
"@babel/types": "^7.23.6",
"@babel/types": "^7.23.0",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
@ -407,9 +406,9 @@
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz",
"integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==",
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
@ -462,9 +461,9 @@
}
},
"node_modules/@babel/highlight": {
"version": "7.23.4",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz",
"integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==",
"version": "7.22.20",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz",
"integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==",
"dev": true,
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.20",
@ -476,9 +475,9 @@
}
},
"node_modules/@babel/parser": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.6.tgz",
"integrity": "sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz",
"integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@ -1685,20 +1684,20 @@
}
},
"node_modules/@babel/traverse": {
"version": "7.23.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.7.tgz",
"integrity": "sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.0.tgz",
"integrity": "sha512-t/QaEvyIoIkwzpiZ7aoSKK8kObQYeF7T2v+dazAYCb8SXtp58zEVkWW7zAnju8FNKNdr4ScAOEDmMItbyOmEYw==",
"dev": true,
"dependencies": {
"@babel/code-frame": "^7.23.5",
"@babel/generator": "^7.23.6",
"@babel/code-frame": "^7.22.13",
"@babel/generator": "^7.23.0",
"@babel/helper-environment-visitor": "^7.22.20",
"@babel/helper-function-name": "^7.23.0",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.23.6",
"@babel/types": "^7.23.6",
"debug": "^4.3.1",
"@babel/parser": "^7.23.0",
"@babel/types": "^7.23.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
"engines": {
@ -1706,12 +1705,12 @@
}
},
"node_modules/@babel/types": {
"version": "7.23.6",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.6.tgz",
"integrity": "sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==",
"version": "7.23.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz",
"integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==",
"dev": true,
"dependencies": {
"@babel/helper-string-parser": "^7.23.4",
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.20",
"to-fast-properties": "^2.0.0"
},
@ -2436,9 +2435,9 @@
}
},
"node_modules/@ldc-web/cookie-notice": {
"version": "6.0.3",
"resolved": "https://lab.s-web.fr/api/v4/projects/50/packages/npm/@ldc-web/cookie-notice/-/@ldc-web/cookie-notice-6.0.3.tgz",
"integrity": "sha1-mW2DD+8ipWkmvKhZ3m9h5wemAHI=",
"version": "3.0.8",
"resolved": "https://lab.s-web.fr/api/v4/projects/50/packages/npm/@ldc-web/cookie-notice/-/@ldc-web/cookie-notice-3.0.8.tgz",
"integrity": "sha1-FOc6hEv1L9NOnRDOdyBnbIsd31A=",
"dev": true
},
"node_modules/@nodelib/fs.scandir": {
@ -2552,31 +2551,12 @@
"integrity": "sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==",
"dev": true
},
"node_modules/@types/fs-extra": {
"version": "8.1.5",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz",
"integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/geojson": {
"version": "7946.0.11",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.11.tgz",
"integrity": "sha512-L7A0AINMXQpVwxHJ4jxD6/XjZ4NDufaRlUJHjNIFKYUFBH1SvOW+neaqb0VTRSLW5suSrSu19ObFEFnfNcr+qg==",
"dev": true
},
"node_modules/@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
"dev": true,
"dependencies": {
"@types/minimatch": "*",
"@types/node": "*"
}
},
"node_modules/@types/json-schema": {
"version": "7.0.13",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz",
@ -2592,32 +2572,19 @@
"@types/geojson": "*"
}
},
"node_modules/@types/minimatch": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
"dev": true
},
"node_modules/@types/minimist": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.3.tgz",
"integrity": "sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==",
"dev": true
},
"node_modules/@types/node": {
"version": "22.14.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz",
"integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==",
"dev": true,
"dependencies": {
"undici-types": "~6.21.0"
}
"peer": true
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.2.tgz",
"integrity": "sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/@types/semver": {
"version": "7.5.3",
@ -3303,6 +3270,7 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz",
"integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -3324,9 +3292,9 @@
"dev": true
},
"node_modules/axios": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz",
"integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==",
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz",
"integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.0",
@ -3496,6 +3464,8 @@
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=6"
}
@ -3505,6 +3475,8 @@
"resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
"integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"camelcase": "^5.3.1",
"map-obj": "^4.0.0",
@ -3612,12 +3584,6 @@
"dev": true,
"peer": true
},
"node_modules/colorette": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
"integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -3789,6 +3755,7 @@
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -3798,6 +3765,7 @@
"resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz",
"integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==",
"dev": true,
"peer": true,
"dependencies": {
"decamelize": "^1.1.0",
"map-obj": "^1.0.0"
@ -3814,6 +3782,7 @@
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
"integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -3884,6 +3853,7 @@
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
"dev": true,
"peer": true,
"dependencies": {
"is-arrayish": "^0.2.1"
}
@ -4588,6 +4558,7 @@
"resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz",
"integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
"dev": true,
"peer": true,
"engines": {
"node": ">=6"
}
@ -4624,6 +4595,7 @@
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
"integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
"dev": true,
"peer": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@ -4636,6 +4608,7 @@
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"peer": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -4647,7 +4620,8 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/html-tags": {
"version": "3.3.1",
@ -4723,6 +4697,8 @@
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -4754,7 +4730,8 @@
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/is-binary-path": {
"version": "2.1.0",
@ -4834,6 +4811,7 @@
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
"integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -4894,7 +4872,8 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/json-schema-traverse": {
"version": "0.4.1",
@ -4946,6 +4925,7 @@
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -4995,7 +4975,8 @@
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/loader-utils": {
"version": "2.0.4",
@ -5083,6 +5064,7 @@
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true,
"peer": true,
"engines": {
"node": ">=8"
},
@ -5113,6 +5095,8 @@
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
"integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"@types/minimist": "^1.2.0",
"camelcase-keys": "^6.2.2",
@ -5139,6 +5123,8 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz",
"integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=10"
},
@ -5194,6 +5180,7 @@
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
"integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
"dev": true,
"peer": true,
"engines": {
"node": ">=4"
}
@ -5215,6 +5202,7 @@
"resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz",
"integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
"dev": true,
"peer": true,
"dependencies": {
"arrify": "^1.0.1",
"is-plain-obj": "^1.1.0",
@ -5271,6 +5259,7 @@
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
"integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
"dev": true,
"peer": true,
"dependencies": {
"hosted-git-info": "^4.0.1",
"is-core-module": "^2.5.0",
@ -5286,6 +5275,7 @@
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
"dev": true,
"peer": true,
"dependencies": {
"yallist": "^4.0.0"
},
@ -5298,6 +5288,7 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
"dev": true,
"peer": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@ -5312,7 +5303,8 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/normalize-path": {
"version": "3.0.0",
@ -5408,6 +5400,8 @@
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=6"
}
@ -5429,6 +5423,7 @@
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
"dev": true,
"peer": true,
"dependencies": {
"@babel/code-frame": "^7.0.0",
"error-ex": "^1.3.1",
@ -5659,6 +5654,12 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/print-js": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/print-js/-/print-js-1.6.0.tgz",
"integrity": "sha512-BfnOIzSKbqGRtO4o0rnj/K3681BSd2QUrsIZy/+WdCIugjIswjmx3lDEZpXB2ruGf9d4b3YNINri81+J0FsBWg==",
"dev": true
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
@ -5708,6 +5709,8 @@
"resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz",
"integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -5717,6 +5720,8 @@
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
"integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"@types/normalize-package-data": "^2.4.0",
"normalize-package-data": "^2.5.0",
@ -5732,6 +5737,8 @@
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
"integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"find-up": "^4.1.0",
"read-pkg": "^5.2.0",
@ -5749,6 +5756,8 @@
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"locate-path": "^5.0.0",
"path-exists": "^4.0.0"
@ -5762,6 +5771,8 @@
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"p-locate": "^4.1.0"
},
@ -5774,6 +5785,8 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"p-try": "^2.0.0"
},
@ -5789,6 +5802,8 @@
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"p-limit": "^2.2.0"
},
@ -5801,6 +5816,8 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
"integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -5809,13 +5826,17 @@
"version": "2.8.9",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/read-pkg/node_modules/normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
@ -5828,6 +5849,8 @@
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
"dev": true,
"optional": true,
"peer": true,
"bin": {
"semver": "bin/semver"
}
@ -5837,6 +5860,8 @@
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
"integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -5858,6 +5883,8 @@
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
"integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"indent-string": "^4.0.0",
"strip-indent": "^3.0.0"
@ -6043,82 +6070,6 @@
"fsevents": "~2.3.2"
}
},
"node_modules/rollup-plugin-copy": {
"version": "3.5.0",
"resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.5.0.tgz",
"integrity": "sha512-wI8D5dvYovRMx/YYKtUNt3Yxaw4ORC9xo6Gt9t22kveWz1enG9QrhVlagzwrxSC455xD1dHMKhIJkbsQ7d48BA==",
"dev": true,
"dependencies": {
"@types/fs-extra": "^8.0.1",
"colorette": "^1.1.0",
"fs-extra": "^8.1.0",
"globby": "10.0.1",
"is-plain-object": "^3.0.0"
},
"engines": {
"node": ">=8.3"
}
},
"node_modules/rollup-plugin-copy/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/rollup-plugin-copy/node_modules/globby": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz",
"integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==",
"dev": true,
"dependencies": {
"@types/glob": "^7.1.1",
"array-union": "^2.1.0",
"dir-glob": "^3.0.1",
"fast-glob": "^3.0.3",
"glob": "^7.1.3",
"ignore": "^5.1.1",
"merge2": "^1.2.3",
"slash": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/rollup-plugin-copy/node_modules/is-plain-object": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz",
"integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/rollup-plugin-copy/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/rollup-plugin-copy/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@ -6307,6 +6258,7 @@
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
"integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
"dev": true,
"peer": true,
"dependencies": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
@ -6316,13 +6268,15 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"peer": true,
"dependencies": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
@ -6332,7 +6286,8 @@
"version": "3.0.16",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.16.tgz",
"integrity": "sha512-eWN+LnM3GR6gPu35WxNgbGl8rmY1AEmoMDvL/QD6zYmPWgywxWqJWNdLGT+ke8dKNWrcYgYjPpG5gbTfghP8rw==",
"dev": true
"dev": true,
"peer": true
},
"node_modules/string-width": {
"version": "4.2.3",
@ -6366,6 +6321,8 @@
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
"integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"min-indent": "^1.0.0"
},
@ -6873,9 +6830,9 @@
"peer": true
},
"node_modules/sweetalert2": {
"version": "11.10.2",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.10.2.tgz",
"integrity": "sha512-BYlIxGw6OF9Rw2z1wlnh1U+fvHHkvtg4BGyimV9nZxQRGvCBfx9uonxgwuYpJuYqCtM+2W1KOm8iMIEb/2v7Hg==",
"version": "11.7.32",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.7.32.tgz",
"integrity": "sha512-44tNNe2oLe7T94mT6dus4hc9G7qg6jZU/K5qZzpNS6e5HGPrSF6Kie6oZ7B5puIJydB34V2h/8f5EhIFivYo4A==",
"dev": true,
"funding": {
"type": "individual",
@ -6991,6 +6948,8 @@
"resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz",
"integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -7044,12 +7003,6 @@
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true
},
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz",
@ -7164,15 +7117,16 @@
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"peer": true,
"dependencies": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"node_modules/vite": {
"version": "4.5.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.5.1.tgz",
"integrity": "sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==",
"version": "4.4.11",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.11.tgz",
"integrity": "sha512-ksNZJlkcU9b0lBwAGZGGaZHCMqHsc8OpgtoYhsQ4/I2v5cnpmmmqe5pM4nv/4Hn6G/2GhTdj0DhZh2e+Er1q5A==",
"dev": true,
"peer": true,
"dependencies": {
@ -7791,6 +7745,7 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
"peer": true,
"engines": {
"node": ">=10"
}

View file

@ -18,7 +18,7 @@
"@fortawesome/free-regular-svg-icons": "latest",
"@fortawesome/free-solid-svg-icons": "latest",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@ldc-web/cookie-notice": "^6.0.3",
"@ldc-web/cookie-notice": "^3.0.8",
"@rushstack/eslint-patch": "^1.3.2",
"@sipec/vue3-tags-input": "^3.0.4",
"@types/bootstrap": "^5.2.7",
@ -38,11 +38,10 @@
"fslightbox": "^3.4.1",
"laravel-vite-plugin": "latest",
"leaflet": "^1.9.4",
"meow": "^9.0.0",
"postcss-discard-comments": "^5.1.2",
"prettier": "latest",
"print-js": "^1.6.0",
"resolve-url-loader": "^5.0.0",
"rollup-plugin-copy": "^3.5.0",
"sass": "latest",
"slugify": "^1.6.6",
"stylelint-config-idiomatic-order": "github:ream88/stylelint-config-idiomatic-order",

View file

@ -1,10 +0,0 @@
includes:
- vendor/larastan/larastan/extension.neon
parameters:
paths:
- app/
# Level 9 is the highest level
level: 5

View file

@ -1,13 +0,0 @@
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: $APP_DOMAIN
spec:
dnsNames:
- $APP_DOMAIN
- www.${APP_DOMAIN}
secretName: $APP_DOMAIN
issuerRef:
name: $APP_CERT_ISSUER
kind: ClusterIssuer

View file

@ -1,13 +1,2 @@
User-agent: *
Disallow: /bo
User-agent: GPTBot
Disallow: /
User-agent: Amazonbot
Disallow: /
User-agent: ClaudeBot
Disallow: /
Sitemap: https://www.lionor.fr/sitemap.xml
Disallow:

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Some files were not shown because too many files have changed in this diff Show more