Compare commits
No commits in common. "k/file-documentations" and "addBanner" have entirely different histories.
k/file-doc
...
addBanner
183 changed files with 28066 additions and 1795 deletions
15
.env.example
15
.env.example
|
|
@ -56,6 +56,14 @@ MAIL_PASSWORD=null
|
|||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
MAIL_LIST_BREEDER=
|
||||
MAIL_LIST_COMMITMENTS=
|
||||
MAIL_LIST_EXPORT=
|
||||
MAIL_LIST_FINANCE=
|
||||
MAIL_LIST_INFO=
|
||||
MAIL_LIST_PRESS=
|
||||
MAIL_LIST_RESTAURATION=
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
||||
|
||||
# TELESCOPE
|
||||
|
|
@ -80,3 +88,10 @@ MEILISEARCH_KEY=
|
|||
UWIL_ADMIN_SECURE_CIDR_LIST=""
|
||||
UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST=""
|
||||
|
||||
# MATOMO
|
||||
MATOMO_SITE_ID=
|
||||
|
||||
# RECAPTCHA
|
||||
GOOGLE_RECAPTCHA_PUBLIC=
|
||||
GOOGLE_RECAPTCHA_PRIVATE=
|
||||
GOOGLE_RECAPTCHA_VERSION=
|
||||
3
.husky/commit-msg
Normal file
3
.husky/commit-msg
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
npx --no -- commitlint --config node_modules/@uwil/husky-config/commitlint.config.cjs --edit $1
|
||||
|
|
@ -29,14 +29,17 @@ COPY --from=vendor /app/vendor/ /app/vendor/
|
|||
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 config set @uwil:registry=https://lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/
|
||||
RUN npm config set -- '//lab.s-web.fr/api/v4/groups/ldc-web/-/packages/npm/:_authToken' "${GITLAB_PASSWORD}"
|
||||
RUN npm ci && npm run prod
|
||||
|
||||
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/uwil-production-8.4:170625-130159
|
||||
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 --chown=${USER}:${USER} ./resources/favicon/front/ /app/srcs/public/
|
||||
COPY --chown=${USER}:${USER} ./resources/favicon/back/ /app/srcs/public/back/
|
||||
COPY --from=vendor --chown=${USER}:${USER} /app/vendor/ /app/srcs/vendor/
|
||||
COPY --from=frontend --chown=${USER}:${USER} /app/public/build/ /app/srcs/public/build/
|
||||
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@ La `storeRequest` d'un model ayant des images, devra importer le trait `HasInert
|
|||
*/
|
||||
protected function prepareForValidation(): void
|
||||
{
|
||||
$this->mergeInertiaPicture();
|
||||
$this->mergeInertiaPicture('picture');
|
||||
// cette partie est à revoir car le paramètre picture ne sera sans doute plus utile
|
||||
}
|
||||
```
|
||||
Cette méthode permet de merge le fichier de la request présent dans un tableau à part des attributes classiques...dans le tableau des attributes.
|
||||
|
|
@ -72,4 +73,19 @@ public function inertiaFill(array $attributes)
|
|||
|
||||
|
||||
## Côté front
|
||||
Un point important, lors de l'utilisation de `useForm` d'Inertia, il faudra utiliser un post au moment du submit (car utiliser un put ou un patch; pour l'update du form, ne fonctionne pas...la présence d'un fichier vide la request) / Pas de panique un hook est là pour s'occuper de tout. cf 
|
||||
Un point important, lors de l'utilisation de `useForm` d'Inertia, il faudra utiliser un post au moment du submit (car utiliser un put ou un patch; pour l'update du form, ne fonctionne pas...la présence d'un fichier vide la request)
|
||||
```js
|
||||
const submit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const routeUrl = route(`back.${modelRoute}.${action}`, model?.id);
|
||||
if (method !== Methods.PATCH && method !== Methods.POST) throw new Error("The method must be post or patch");
|
||||
post(routeUrl); // on utilise post ici
|
||||
};
|
||||
```
|
||||
Mais dans le `useForm` on surcharge la méthode :
|
||||
```js
|
||||
const { post, setData, data, reset } = useForm({
|
||||
_method: method === Methods.POST ? 'post' : 'put',
|
||||
picture: model?.picture,
|
||||
});
|
||||
```
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ class LocalDateTime implements CastsAttributes
|
|||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param string|integer|null $value
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return \Carbon\Carbon|null
|
||||
* @phpcs:disable
|
||||
*/
|
||||
public function get(Model $model, string $key, string|int|null $value, array $attributes): ?\Carbon\Carbon
|
||||
public function get(Model $model, string $key, mixed $value, array $attributes): ?\Carbon\Carbon
|
||||
{
|
||||
// phpcs:enable
|
||||
return $value
|
||||
|
|
@ -31,12 +31,12 @@ class LocalDateTime implements CastsAttributes
|
|||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $key
|
||||
* @param string|integer|null $value
|
||||
* @param mixed $value
|
||||
* @param array $attributes
|
||||
* @return \Carbon\Carbon|null
|
||||
* @phpcs:disable
|
||||
*/
|
||||
public function set(Model $model, string $key, string|int|null $value, array $attributes): ?\Carbon\Carbon
|
||||
public function set(Model $model, string $key, mixed $value, array $attributes): ?\Carbon\Carbon
|
||||
{
|
||||
// phpcs:enable
|
||||
return $value
|
||||
|
|
|
|||
|
|
@ -27,225 +27,20 @@ enum ContactFormType: int
|
|||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::collaborator->name => 'enums.contactFormType.collaborator.label',
|
||||
self::breeder->name => 'enums.contactFormType.breeder.label',
|
||||
self::professional->name => 'enums.contactFormType.professional.label',
|
||||
self::restauration->name => 'enums.contactFormType.restauration.label',
|
||||
self::export->name => 'enums.contactFormType.export.label',
|
||||
self::finance->name => 'enums.contactFormType.finance.label',
|
||||
self::commitments->name => 'enums.contactFormType.commitments.label',
|
||||
self::product->name => 'enums.contactFormType.product.label',
|
||||
self::info->name => 'enums.contactFormType.info.label',
|
||||
self::press->name => 'enums.contactFormType.press.label',
|
||||
self::collaborator->name => 'enums.contactFormType.collaborator',
|
||||
self::breeder->name => 'enums.contactFormType.breeder',
|
||||
self::professional->name => 'enums.contactFormType.professional',
|
||||
self::restauration->name => 'enums.contactFormType.restauration',
|
||||
self::export->name => 'enums.contactFormType.export',
|
||||
self::finance->name => 'enums.contactFormType.finance',
|
||||
self::commitments->name => 'enums.contactFormType.commitments',
|
||||
self::product->name => 'enums.contactFormType.product',
|
||||
self::info->name => 'enums.contactFormType.info',
|
||||
self::press->name => 'enums.contactFormType.press',
|
||||
];
|
||||
|
||||
/**
|
||||
* Optionnal SUBTYPES definition.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const SUBTYPES = [
|
||||
self::professional->name => [
|
||||
self::restauration,
|
||||
self::export,
|
||||
],
|
||||
self::collaborator->name => [],
|
||||
self::breeder->name => [],
|
||||
self::restauration->name => [],
|
||||
self::export->name => [],
|
||||
self::finance->name => [],
|
||||
self::commitments->name => [],
|
||||
self::product->name => [],
|
||||
self::info->name => [],
|
||||
self::press->name => [],
|
||||
];
|
||||
|
||||
/**
|
||||
* Get Class
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function isSubtype(): bool
|
||||
{
|
||||
foreach (self::SUBTYPES as $subtypes) {
|
||||
if (in_array($this, $subtypes, true)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionnal TOPLEVELS definition.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const TOPLEVELS = [
|
||||
self::professional->name => true,
|
||||
self::collaborator->name => true,
|
||||
self::breeder->name => true,
|
||||
self::restauration->name => false,
|
||||
self::export->name => false,
|
||||
self::finance->name => true,
|
||||
self::commitments->name => true,
|
||||
self::product->name => true,
|
||||
self::info->name => true,
|
||||
self::press->name => true,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get Class
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function topLevel(): ?string
|
||||
{
|
||||
return !empty(self::TOPLEVELS[$this->name]) ?
|
||||
self::TOPLEVELS[$this->name] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data-target
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function dataTarget(): string
|
||||
{
|
||||
return match ($this) {
|
||||
self::professional => 'askPro',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subtitle
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function subtitle(): string
|
||||
{
|
||||
return match (true) {
|
||||
$this === self::restauration => __('enums.contactFormType.' . $this->name . '.subtitle'),
|
||||
default => __('enums.contactFormType.' . $this->name . '.label'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug for each type
|
||||
*
|
||||
* @param string|null $locale
|
||||
* @return string|null
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity
|
||||
*/
|
||||
public function slug(?string $locale = null): ?string
|
||||
{
|
||||
// phpcs:enable
|
||||
return match ($this) {
|
||||
self::collaborator => Lang::get('enums.contactFormType.collaborator.slug', [], $locale),
|
||||
self::breeder => Lang::get('enums.contactFormType.breeder.slug', [], $locale),
|
||||
self::professional => null,
|
||||
self::restauration => Lang::get('enums.contactFormType.restauration.slug', [], $locale),
|
||||
self::export => Lang::get('enums.contactFormType.export.slug', [], $locale),
|
||||
self::finance => Lang::get('enums.contactFormType.finance.slug', [], $locale),
|
||||
self::commitments => Lang::get('enums.contactFormType.commitments.slug', [], $locale),
|
||||
self::product => Lang::get('enums.contactFormType.product.slug', [], $locale),
|
||||
self::info => Lang::get('enums.contactFormType.info.slug', [], $locale),
|
||||
self::press => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current lang slug match in specific lang slug
|
||||
*
|
||||
* @param string $slug
|
||||
* @param string $locale
|
||||
* @return self
|
||||
*/
|
||||
public static function fromSlug(string $slug, string $locale): ?self
|
||||
{
|
||||
foreach (self::cases() as $case) {
|
||||
$caseSlug = $case->slug($locale);
|
||||
|
||||
if (
|
||||
$caseSlug !== null &&
|
||||
mb_strtolower($caseSlug) === mb_strtolower($slug)
|
||||
) {
|
||||
return $case;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function route(): ?string
|
||||
{
|
||||
$commonRoutes = [
|
||||
self::collaborator,
|
||||
self::breeder,
|
||||
self::export,
|
||||
self::finance,
|
||||
self::commitments,
|
||||
self::product,
|
||||
self::info,
|
||||
self::restauration
|
||||
];
|
||||
return match (true) {
|
||||
in_array($this, $commonRoutes, true) => route('front.contact.show', ['slug' => $this->slug()]),
|
||||
$this === self::professional => null,
|
||||
$this === self::press =>
|
||||
RouteHelper::routeNameChecker('front.dynamic-pages.espace-presse') . '#page-press-form',
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the route for the contact form type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getLabelisedRoutes(): array
|
||||
{
|
||||
return collect(self::cases())->mapWithKeys(fn($enum) => [
|
||||
$enum->name => (object) [
|
||||
"label" => $enum->label(),
|
||||
"route" => $enum->route(),
|
||||
],
|
||||
])->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug for each type
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function seo(): array
|
||||
{
|
||||
$commonRoutes = [
|
||||
self::collaborator,
|
||||
self::breeder,
|
||||
self::export,
|
||||
self::finance,
|
||||
self::commitments,
|
||||
self::professional,
|
||||
self::product,
|
||||
self::info,
|
||||
self::press,
|
||||
self::restauration
|
||||
];
|
||||
return match (true) {
|
||||
in_array($this, $commonRoutes, true) => [
|
||||
'title' => __('enums.contactFormType.' . $this->name . '.seo.title'),
|
||||
'description' => __('enums.contactFormType.' . $this->name . '.seo.description'),
|
||||
],
|
||||
default => [],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the slug for each type
|
||||
* Get the slug for each type.
|
||||
*
|
||||
* @return array
|
||||
* @phpcs:disable Generic.Metrics.CyclomaticComplexity
|
||||
|
|
@ -254,34 +49,16 @@ enum ContactFormType: int
|
|||
{
|
||||
// phpcs:enable
|
||||
return match ($this) {
|
||||
self::collaborator => [],
|
||||
self::breeder => [
|
||||
'marie-charlotte.garnier@ldc.fr',
|
||||
'dylan.chevalier@ldc.fr',
|
||||
],
|
||||
self::professional => null,
|
||||
self::restauration => [
|
||||
'rhd@ldc.fr',
|
||||
],
|
||||
self::export => [
|
||||
'alexis.lambert@ldc.fr',
|
||||
'bruno.lafon@ldc.fr',
|
||||
],
|
||||
self::finance => [
|
||||
'david.eluaud@ldc.fr',
|
||||
'malaury.offe@ldc.fr',
|
||||
],
|
||||
self::commitments => [
|
||||
'claire-marie.gesland@ldc.fr',
|
||||
],
|
||||
self::product => [],
|
||||
self::info => [
|
||||
'blandine.durand-gratian@ldc.fr',
|
||||
'malaury.offe@ldc.fr',
|
||||
],
|
||||
self::press => [
|
||||
'communicationgroupe@ldc.fr',
|
||||
],
|
||||
self::breeder => config('mail.to.breeder'),
|
||||
self::restauration => config('mail.to.restauration'),
|
||||
self::export => config('mail.to.export'),
|
||||
self::finance => config('mail.to.finance'),
|
||||
self::commitments => config('mail.to.commitments'),
|
||||
self::info => config('mail.to.info'),
|
||||
self::press => config('mail.to.press'),
|
||||
self::collaborator,
|
||||
self::product,
|
||||
self::professional => null
|
||||
// phpcs:ignore Squiz.Commenting.InlineComment
|
||||
}; //end match
|
||||
}
|
||||
|
|
|
|||
25
app/Enums/Contact/ExportAskingType.php
Normal file
25
app/Enums/Contact/ExportAskingType.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums\Contact;
|
||||
|
||||
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
|
||||
|
||||
enum ExportAskingType: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case customer = 1;
|
||||
case provider = 2;
|
||||
case other = 3;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::customer->name => 'enums.exportAskingType.customer',
|
||||
self::provider->name => 'enums.exportAskingType.provider',
|
||||
self::other->name => 'enums.exportAskingType.other',
|
||||
];
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ enum DynamicFieldTypes: int
|
|||
{
|
||||
use BaseEnum;
|
||||
|
||||
// Name must be one word in lowercase.
|
||||
// Name must be one word in lowercase.
|
||||
case string = 1;
|
||||
case textarea = 2;
|
||||
case wysiwyg = 3;
|
||||
|
|
@ -25,9 +25,11 @@ enum DynamicFieldTypes: int
|
|||
case color = 14;
|
||||
case model = 15;
|
||||
case staticroute = 16;
|
||||
case video = 17;
|
||||
|
||||
// Specific inputs.
|
||||
case position = 101;
|
||||
// Specific inputs.
|
||||
case position = 101;
|
||||
case contactform = 102;
|
||||
|
||||
/**
|
||||
* Labels definition.
|
||||
|
|
@ -51,9 +53,11 @@ enum DynamicFieldTypes: int
|
|||
self::color->name => 'enums.dynamic-field-types.color',
|
||||
self::model->name => 'enums.dynamic-field-types.model',
|
||||
self::staticroute->name => 'enums.dynamic-field-types.staticroute',
|
||||
self::video->name => 'enums.dynamic-field-types.video',
|
||||
|
||||
// Specific inputs.
|
||||
self::position->name => 'enums.dynamic-field-types.position',
|
||||
self::position->name => 'enums.dynamic-field-types.position',
|
||||
self::contactform->name => 'enums.dynamic-field-types.contactform',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -70,15 +74,17 @@ enum DynamicFieldTypes: int
|
|||
self::image->name => 'required|file|mimes:jpg,jpeg,png,webp|max:5120|dimensions:width=100,height=100',
|
||||
self::list->name => 'required|array|min:1|max:10',
|
||||
self::file->name => 'required|file|mimes:pdf|max:5120',
|
||||
self::vimeoid->name => 'required|string|min:8|max:10',
|
||||
self::youtubeid->name => 'required|string|size:11',
|
||||
self::vimeoid->name => 'required',
|
||||
self::youtubeid->name => 'required',
|
||||
self::date->name => 'required|date',
|
||||
self::color->name => 'required|string|hex_color',
|
||||
self::model->name => 'required|numeric|exists:dynamic_pages,id',
|
||||
self::staticroute->name => 'required|numeric|between:1,99',
|
||||
self::video->name => 'required',
|
||||
|
||||
// Specific inputs.
|
||||
self::position->name => 'required|numeric|between:1,5',
|
||||
self::contactform->name => 'required|numeric|between:1,10'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -95,6 +101,7 @@ enum DynamicFieldTypes: int
|
|||
* Is array definition.
|
||||
*
|
||||
* @return array
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const ISARRAY = [
|
||||
self::string->name => false,
|
||||
|
|
@ -113,8 +120,10 @@ enum DynamicFieldTypes: int
|
|||
self::color->name => false,
|
||||
self::model->name => false,
|
||||
self::staticroute->name => false,
|
||||
self::video->name => false,
|
||||
|
||||
// Specific inputs.
|
||||
self::position->name => false,
|
||||
self::position->name => false,
|
||||
self::contactform->name => false,
|
||||
];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,18 +21,18 @@ enum OrdersEnum: int
|
|||
self::desc->name => 'enums.orders.desc',
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* Get the orders to display using the app locale
|
||||
* Get the orders to display using the app locale.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function ordersWithLocal(): bool
|
||||
{
|
||||
$locale = config('app.locale');
|
||||
return match (true) {
|
||||
$this === self::asc => true,
|
||||
$this === self::desc => true,
|
||||
return match ($this) {
|
||||
self::asc,
|
||||
/** @phpstan-ignore-next-line */
|
||||
self::desc => true,
|
||||
default => $locale === "fr" ? true : false,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,17 +10,6 @@ enum StaticRoutesEnum: int
|
|||
use BaseEnum;
|
||||
|
||||
// Set all cases for each static routes.
|
||||
case collaborator = 1;
|
||||
case breeder = 2;
|
||||
case professional = 3;
|
||||
case restauration = 4;
|
||||
case export = 5;
|
||||
case finance = 6;
|
||||
case commitments = 7;
|
||||
case product = 8;
|
||||
case info = 9;
|
||||
case press = 10;
|
||||
case search = 11;
|
||||
|
||||
/**
|
||||
* Get array of each route with his matching value.
|
||||
|
|
@ -28,29 +17,29 @@ enum StaticRoutesEnum: int
|
|||
* @param callable|null $callback
|
||||
* @return array
|
||||
*/
|
||||
public static function getRoutes(?callable $callback = null): array
|
||||
public static function getRoutes(?callable $callback = null)
|
||||
{
|
||||
$dataKeyValue = array_merge(ContactFormType::getLabelisedRoutes(), [
|
||||
self::search->name => (object) [
|
||||
'label' => trans('front.search.title'),
|
||||
'route' => '/recherche?texte=LDC',
|
||||
]
|
||||
]);
|
||||
return collect($dataKeyValue)
|
||||
->reject(fn($item) => is_null($item->label) || is_null($item->route))
|
||||
->map(function ($item, $key) {
|
||||
$targetEnum = collect(self::cases())
|
||||
->first(fn($enum) => $enum->name === $key);
|
||||
// $dataKeyValue = array_merge([], [
|
||||
// self::search->name => (object) [
|
||||
// 'label' => trans('front.search.title'),
|
||||
// 'route' => '/recherche?texte=LDC',
|
||||
// ]
|
||||
// ]);
|
||||
// return collect($dataKeyValue)
|
||||
// ->reject(fn($item) => is_null($item->label) || is_null($item->route))
|
||||
// ->map(function ($item, $key) {
|
||||
// $targetEnum = collect(self::cases())
|
||||
// ->first(fn($enum) => $enum->name === $key);
|
||||
|
||||
return (object) [
|
||||
'label' => ucfirst($item->label),
|
||||
'value' => $targetEnum->value,
|
||||
'route' => $item->route,
|
||||
];
|
||||
})
|
||||
->when(!is_null($callback), fn ($collection) => $collection->filter($callback))
|
||||
->values()
|
||||
->toArray();
|
||||
// return (object) [
|
||||
// 'label' => ucfirst($item->label),
|
||||
// 'value' => $targetEnum->value,
|
||||
// 'route' => $item->route,
|
||||
// ];
|
||||
// })
|
||||
// ->when(!is_null($callback), fn ($collection) => $collection->filter($callback))
|
||||
// ->values()
|
||||
// ->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
34
app/Enums/VideoProviderEnum.php
Normal file
34
app/Enums/VideoProviderEnum.php
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
use Uwil\LaravelCommon\Traits\Enums\BaseEnum;
|
||||
|
||||
enum VideoProviderEnum: int
|
||||
{
|
||||
use BaseEnum;
|
||||
|
||||
case youtube = 1;
|
||||
case vimeo = 2;
|
||||
|
||||
/**
|
||||
* Optionnal labels definition.
|
||||
*
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const LABELS = [
|
||||
self::youtube->name => 'youtube',
|
||||
self::vimeo->name => 'viméo',
|
||||
];
|
||||
|
||||
/**
|
||||
* Is array definition.
|
||||
*
|
||||
* @return array
|
||||
* @phpstan-ignore-next-line
|
||||
*/
|
||||
private const COLOR = [
|
||||
self::youtube->name => "#FF0000",
|
||||
self::vimeo->name => "#00B2EA",
|
||||
];
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ use App\Enums\DynamicFieldTypes;
|
|||
use App\Enums\DynamicPages\PageStatusEnum;
|
||||
use App\Models\DynamicPage;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Str;
|
||||
use Uwil\LaravelCommon\Helpers\FileHelper;
|
||||
use Uwil\LaravelCommon\Helpers\ToolboxHelper;
|
||||
|
||||
|
|
@ -151,7 +151,7 @@ class DynamicPageHelper
|
|||
? ''
|
||||
: data_get($newContent, $key, '');
|
||||
if ($old) {
|
||||
FileHelper::removeOldModelFilesFromHtml($new, $old);
|
||||
FileHelper::removeOldModelFilesFromHtml($new ?? '', $old);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -181,9 +181,9 @@ class DynamicPageHelper
|
|||
/**
|
||||
* Get all segments url for dynamic pages.
|
||||
*
|
||||
* @param \App\Models\DynamicPage $page
|
||||
* @param \Illuminate\Support\Collection $pages
|
||||
* @param string $locale
|
||||
* @param \App\Models\DynamicPage $page
|
||||
* @param \Illuminate\Database\Eloquent\Collection $pages
|
||||
* @param string $locale
|
||||
* @return array|null
|
||||
*/
|
||||
public static function getAllDynamicPageSegments(DynamicPage $page, Collection $pages, string $locale): ?array
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace App\Helpers;
|
||||
|
||||
use App\Enums\Contact\ContactFormType;
|
||||
use App\Models\DynamicPage;
|
||||
use Illuminate\Routing\Route;
|
||||
use Uwil\LaravelCommon\Controllers\Controller;
|
||||
|
|
@ -63,7 +62,6 @@ class RouteHelper extends Controller
|
|||
}
|
||||
return self::resolve(
|
||||
self::getDynamicPageLocalizedUrl($route, $localeCode),
|
||||
self::getContactShowLocalizedUrl($route, $localeCode),
|
||||
) ?? LaravelLocalization::getLocalizedURL($localeCode, null, [], false);
|
||||
}
|
||||
|
||||
|
|
@ -83,48 +81,6 @@ class RouteHelper extends Controller
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the localized url for contact page show.
|
||||
*
|
||||
* @param \Illuminate\Routing\Route $route
|
||||
* @param string $localeCode
|
||||
* @return string|false|null
|
||||
*/
|
||||
private static function getContactShowLocalizedUrl(Route $route, string $localeCode)
|
||||
{
|
||||
$contactPage = $route->parameter('contactPage');
|
||||
|
||||
if ($contactPage) {
|
||||
$localizedUrl = LaravelLocalization::getLocalizedURL(
|
||||
$localeCode,
|
||||
null,
|
||||
[],
|
||||
false
|
||||
);
|
||||
$parsed = parse_url($localizedUrl);
|
||||
if (!isset($parsed['path'])) {
|
||||
return $localizedUrl;
|
||||
}
|
||||
|
||||
$segments = explode('/', trim($parsed['path'], '/'));
|
||||
$currentSlug = end($segments);
|
||||
|
||||
$enum = ContactFormType::fromSlug(
|
||||
$currentSlug,
|
||||
app()->getLocale()
|
||||
);
|
||||
|
||||
if ($enum) {
|
||||
$translatedSlug = $enum->slug($localeCode);
|
||||
if ($translatedSlug) {
|
||||
$segments[count($segments) - 1] = $translatedSlug;
|
||||
return '/' . implode('/', $segments);
|
||||
}
|
||||
}
|
||||
return $localizedUrl;
|
||||
} //end if
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the localized url for all dynamic pages.
|
||||
*
|
||||
|
|
@ -137,10 +93,10 @@ class RouteHelper extends Controller
|
|||
/** @var \App\Models\DynamicPage|null $dynamicPage */
|
||||
$dynamicPage = $route->parameter('dynamicPage');
|
||||
if ($dynamicPage instanceof DynamicPage) {
|
||||
$path = $dynamicPage->getLocalizedPath($localeCode);
|
||||
if ($path) {
|
||||
return LaravelLocalization::getLocalizedURL($localeCode, "/{$path}", [], false);
|
||||
}
|
||||
$path = $dynamicPage->getLocalizedPath($localeCode);
|
||||
$defPath = $path ?? "";
|
||||
return LaravelLocalization::getLocalizedURL($localeCode, "/{$defPath}", [], false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,29 +64,43 @@ class TranslationHelper
|
|||
}
|
||||
|
||||
/**
|
||||
* Assert value is unique in a translation field.
|
||||
* Assert that a value is unique inside a JSON translation field.
|
||||
*
|
||||
* The uniqueness can optionally be scoped to another attribute/value pair.
|
||||
* If $anotherDbAttribute and $anotherValue are provided, the uniqueness
|
||||
* will be checked only within that scope. Otherwise, the value must be
|
||||
* globally unique.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model The model instance being validated.
|
||||
* @param string $value The translated value to check.
|
||||
* @param string $dbAttribute The JSON column containing translations.
|
||||
* @param string|null $anotherValue Optional value used to scope uniqueness.
|
||||
* @param string|null $anotherDbAttribute Optional database column used to scope uniqueness.
|
||||
* @param string|null $errorAttribute Optional attribute name used in the validation error.
|
||||
* @param string|null $errorTranslation Optional translated attribute label for the error message.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Model $model
|
||||
* @param string $value
|
||||
* @param string $dbAttribute
|
||||
* @param string|null $errorAttribute
|
||||
* @param string|null $errorTranslation
|
||||
* @return void
|
||||
* @throws \Illuminate\Validation\ValidationException Value is already used.
|
||||
*
|
||||
* @throws \Illuminate\Validation\ValidationException If the value is already used.
|
||||
*/
|
||||
public static function assertTranslationFieldIsUnique(
|
||||
Model $model,
|
||||
string $value,
|
||||
string $dbAttribute,
|
||||
?string $anotherValue = null,
|
||||
?string $anotherDbAttribute = null,
|
||||
?string $errorAttribute = null,
|
||||
?string $errorTranslation = null,
|
||||
): void {
|
||||
$modelClass = get_class($model);
|
||||
$exists = $modelClass::query()
|
||||
$query = $modelClass::query()
|
||||
->whereKeyNot($model->getKey())
|
||||
->whereRaw("JSON_SEARCH($dbAttribute, 'one', ?) IS NOT NULL", [$value])
|
||||
->exists();
|
||||
if ($exists) {
|
||||
->whereRaw("JSON_SEARCH(`{$dbAttribute}`, 'one', ?) IS NOT NULL", [$value]);
|
||||
|
||||
if ($anotherDbAttribute !== null) {
|
||||
$query->where($anotherDbAttribute, $anotherValue);
|
||||
}
|
||||
if ($query->exists()) {
|
||||
throw ValidationException::withMessages([
|
||||
$errorAttribute ?? $dbAttribute => trans("validation.unique", [
|
||||
'attribute' => $errorTranslation ?? trans("validation.attributes.$dbAttribute"),
|
||||
|
|
|
|||
36
app/Http/Controllers/Api/CompanyController.php
Normal file
36
app/Http/Controllers/Api/CompanyController.php
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\Company;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class CompanyController
|
||||
{
|
||||
/**
|
||||
* Retrieve all published companies with their associated activity and sector.
|
||||
*
|
||||
* @return \Illuminate\Http\JsonResponse
|
||||
*/
|
||||
public function getCompanies(): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$companies = Company::query()->where('published', true)->with('companyActivity', 'companySector')->get();
|
||||
|
||||
$data = $companies->map(function ($company) {
|
||||
return array_filter([
|
||||
'name' => $company->name,
|
||||
'logo' => $company->logo,
|
||||
'informations' => $company->informations,
|
||||
'responsable' => $company->responsable,
|
||||
'employees' => $company->employees,
|
||||
'url' => $company->url,
|
||||
'latitude' => $company->latitude,
|
||||
'longitude' => $company->longitude,
|
||||
'type_activite' => Str::lower($company->companyActivity->name),
|
||||
'filiere' => $company->companySector?->name,
|
||||
], fn($value) => !is_null($value));
|
||||
});
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
25
app/Http/Controllers/Back/AuthenticatedSessionController.php
Normal file
25
app/Http/Controllers/Back/AuthenticatedSessionController.php
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Uwil\LaravelBackend\Controllers\AuthenticatedSessionController as PackageAuthenticatedSessionController;
|
||||
|
||||
class AuthenticatedSessionController extends PackageAuthenticatedSessionController
|
||||
{
|
||||
/**
|
||||
* Destroy an authenticated session.
|
||||
*/
|
||||
public function destroy(Request $request): RedirectResponse
|
||||
{
|
||||
Auth::guard('backend')->logout();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return to_route('front.dynamic-pages.accueil.fr');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use App\Http\Requests\Back\DocumentCategory\StoreDocumentCategoryRequest;
|
||||
use App\Http\Requests\Back\DocumentCategory\UpdateDocumentCategoryRequest;
|
||||
use App\Http\Requests\Back\DocumentCategories\StoreDocumentCategoryRequest;
|
||||
use App\Http\Requests\Back\DocumentCategories\UpdateDocumentCategoryRequest;
|
||||
use App\Models\DocumentCategory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -99,7 +99,7 @@ class DocumentCategoryController extends Controller
|
|||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\StoreDocumentCategoryRequest $request
|
||||
* @param \App\Http\Requests\Back\DocumentCategories\StoreDocumentCategoryRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreDocumentCategoryRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -131,8 +131,8 @@ class DocumentCategoryController extends Controller
|
|||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\UpdateDocumentCategoryRequest $request
|
||||
* @param \App\Models\DocumentCategory $document_category
|
||||
* @param \App\Http\Requests\Back\DocumentCategories\UpdateDocumentCategoryRequest $request
|
||||
* @param \App\Models\DocumentCategory $document_category
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ namespace App\Http\Controllers\Back;
|
|||
use App\Enums\Document\DocumentStatusEnum;
|
||||
use App\Enums\YearsEnum;
|
||||
use App\Helpers\DocumentHelper;
|
||||
use App\Http\Requests\Back\Document\StoreDocumentRequest;
|
||||
use App\Http\Requests\Back\Document\UpdateDocumentRequest;
|
||||
use App\Http\Requests\Back\Documents\StoreDocumentRequest;
|
||||
use App\Http\Requests\Back\Documents\UpdateDocumentRequest;
|
||||
use App\Models\Document;
|
||||
use App\Models\DocumentCategory;
|
||||
use App\Models\DocumentTag;
|
||||
|
|
@ -108,7 +108,7 @@ class DocumentController extends Controller
|
|||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\Document\StoreDocumentRequest $request
|
||||
* @param \App\Http\Requests\Back\Documents\StoreDocumentRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreDocumentRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -144,8 +144,8 @@ class DocumentController extends Controller
|
|||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\Document\UpdateDocumentRequest $request
|
||||
* @param \App\Models\Document $document
|
||||
* @param \App\Http\Requests\Back\Documents\UpdateDocumentRequest $request
|
||||
* @param \App\Models\Document $document
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateDocumentRequest $request, Document $document): \Illuminate\Http\RedirectResponse
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use App\Http\Requests\Back\DocumentTag\StoreDocumentTagRequest;
|
||||
use App\Http\Requests\Back\DocumentTag\UpdateDocumentTagRequest;
|
||||
use App\Http\Requests\Back\DocumentTags\StoreDocumentTagRequest;
|
||||
use App\Http\Requests\Back\DocumentTags\UpdateDocumentTagRequest;
|
||||
use App\Models\DocumentTag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -99,7 +99,7 @@ class DocumentTagController extends Controller
|
|||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\StoreDocumentTagRequest $request
|
||||
* @param \App\Http\Requests\Back\DocumentTags\StoreDocumentTagRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreDocumentTagRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -131,8 +131,8 @@ class DocumentTagController extends Controller
|
|||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\UpdateDocumentTagRequest $request
|
||||
* @param \App\Models\DocumentTag $document_tag
|
||||
* @param \App\Http\Requests\Back\DocumentTags\UpdateDocumentTagRequest $request
|
||||
* @param \App\Models\DocumentTag $document_tag
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use App\Enums\Contact\ContactFormType;
|
||||
use App\Enums\DynamicPages\PageStatusEnum;
|
||||
use App\Enums\ElementPositionEnum;
|
||||
use App\Http\Requests\Back\DynamicPages\StoreDynamicPageRequest;
|
||||
|
|
@ -92,6 +93,7 @@ class DynamicPageController extends Controller
|
|||
'pageConfigModels' => $pageConfigModels,
|
||||
'enumDynamicPageStatus' => PageStatusEnum::toArray(),
|
||||
'enumElementPosition' => ElementPositionEnum::toArray(),
|
||||
'enumContactFormType' => ContactFormType::toArray(),
|
||||
'modelName' => $this->modelName,
|
||||
]);
|
||||
}
|
||||
|
|
@ -150,7 +152,9 @@ class DynamicPageController extends Controller
|
|||
'pageConfigModels' => $pageConfigModels,
|
||||
'enumDynamicPageStatus' => PageStatusEnum::toArray(),
|
||||
'enumElementPosition' => ElementPositionEnum::toArray(),
|
||||
'enumContactFormType' => ContactFormType::toArray(),
|
||||
'modelName' => $this->modelName,
|
||||
'dynamicPageUrl' => DynamicPage::getAccessModel($dynamic_page->id),
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ class InputsController extends Controller
|
|||
|
||||
$url = (method_exists($modelClass, 'getAccessModel'))
|
||||
? $modelClass::getAccessModel($validated['selectedEntry'])
|
||||
: static::getAccessModel($validated['selectedEntry'], $validated['modelName']);
|
||||
: self::getAccessModel($validated['selectedEntry'], $validated['modelName']);
|
||||
|
||||
$token = Crypt::encryptString(json_encode([
|
||||
'modelName' => $validated['modelName'],
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use App\Enums\News\NewsStatusEnum;
|
||||
use App\Http\Requests\Back\NewsCategory\StoreNewsCategoryRequest;
|
||||
use App\Http\Requests\Back\NewsCategory\UpdateNewsCategoryRequest;
|
||||
use App\Http\Requests\Back\NewsCategories\StoreNewsCategoryRequest;
|
||||
use App\Http\Requests\Back\NewsCategories\UpdateNewsCategoryRequest;
|
||||
use App\Models\NewsCategory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -102,7 +102,7 @@ class NewsCategoryController extends Controller
|
|||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\NewsCategory\StoreNewsCategoryRequest $request
|
||||
* @param \App\Http\Requests\Back\NewsCategories\StoreNewsCategoryRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreNewsCategoryRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -134,8 +134,8 @@ class NewsCategoryController extends Controller
|
|||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\UpdateNewsCategoryRequest $request
|
||||
* @param \App\Models\NewsCategory $news_category
|
||||
* @param \App\Http\Requests\Back\NewsCategories\UpdateNewsCategoryRequest $request
|
||||
* @param \App\Models\NewsCategory $news_category
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
namespace App\Http\Controllers\Back;
|
||||
|
||||
use App\Enums\News\NewsStatusEnum;
|
||||
use App\Http\Requests\Back\NewsTag\StoreNewsTagRequest;
|
||||
use App\Http\Requests\Back\NewsTag\UpdateNewsTagRequest;
|
||||
use App\Http\Requests\Back\NewsTags\StoreNewsTagRequest;
|
||||
use App\Http\Requests\Back\NewsTags\UpdateNewsTagRequest;
|
||||
use App\Models\NewsTag;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
|
@ -102,7 +102,7 @@ class NewsTagController extends Controller
|
|||
/**
|
||||
* Store a newly created resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\NewsTag\StoreNewsTagRequest $request
|
||||
* @param \App\Http\Requests\Back\NewsTags\StoreNewsTagRequest $request
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(StoreNewsTagRequest $request): \Illuminate\Http\RedirectResponse
|
||||
|
|
@ -134,8 +134,8 @@ class NewsTagController extends Controller
|
|||
/**
|
||||
* Update the specified resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Back\News\UpdateNewsTagRequest $request
|
||||
* @param \App\Models\NewsTag $news_tag
|
||||
* @param \App\Http\Requests\Back\NewsTags\UpdateNewsTagRequest $request
|
||||
* @param \App\Models\NewsTag $news_tag
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(UpdateNewsTagRequest $request, NewsTag $news_tag): \Illuminate\Http\RedirectResponse
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use App\Http\Requests\Front\Contact\ContactRequest;
|
|||
use App\Jobs\Mails\SendContactMail;
|
||||
use App\Models\ContactCount;
|
||||
use App\Models\MailContactData;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use LDC\LDCCRM\Enums\ApiError;
|
||||
|
|
@ -17,33 +18,6 @@ use LDC\LDCCRM\Models\SingleOptin;
|
|||
|
||||
class ContactController
|
||||
{
|
||||
/**
|
||||
* Show contact index page.
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function index(): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$types = ContactFormType::toArray();
|
||||
return view('front.pages.contact.index', compact('types'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show contact show page.
|
||||
*
|
||||
* @param string $slug
|
||||
* @return \Illuminate\Contracts\View\View
|
||||
*/
|
||||
public function show(string $slug): \Illuminate\Contracts\View\View
|
||||
{
|
||||
$enumType = collect(ContactFormType::all())->first(function ($enum) use ($slug) {
|
||||
return $enum->slug() === $slug;
|
||||
});
|
||||
if (!$enumType) {
|
||||
return redirect()->route('front.contact.index');
|
||||
}
|
||||
return view('front.pages.contact.show', compact('enumType'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact send.
|
||||
|
|
@ -53,12 +27,16 @@ class ContactController
|
|||
*/
|
||||
public function send(ContactRequest $request): \Illuminate\Http\RedirectResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$messageContact = new MailContactData($data);
|
||||
$validatedData = $request->validated();
|
||||
$messageContact = new MailContactData($validatedData);
|
||||
$contactMessage = "";
|
||||
$hasContactError = false;
|
||||
$newsletterMessage = null;
|
||||
$hasNewsletterError = false;
|
||||
|
||||
/** @var boolean $isNewsletter */
|
||||
$isNewsletter = isset($validatedData['newsletter']) ? $validatedData['newsletter'] : false;
|
||||
|
||||
try {
|
||||
dispatch(new SendContactMail($messageContact))
|
||||
->onConnection('database')
|
||||
|
|
@ -70,7 +48,7 @@ class ContactController
|
|||
$hasContactError = true;
|
||||
$contactMessage = trans('Une erreur est survenue lors de l\'envoi du message de contact.');
|
||||
}
|
||||
if (!empty($data['newsletter']) && $data['newsletter']) {
|
||||
if ($isNewsletter) {
|
||||
try {
|
||||
SingleOptin::setStatus($request->email, true);
|
||||
$newsletterMessage = trans("Votre inscription à la newsletter a été prise en compte");
|
||||
|
|
@ -90,8 +68,8 @@ class ContactController
|
|||
}
|
||||
}
|
||||
ContactCount::updateOrInsert(
|
||||
['type' => $data['enumType']],
|
||||
['submissions' => \DB::raw('submissions + 1')]
|
||||
['type' => $validatedData['enumType']],
|
||||
['submissions' => DB::raw('submissions + 1')]
|
||||
);
|
||||
|
||||
$contactMessage = trans('Votre message a bien été envoyé.');
|
||||
|
|
|
|||
|
|
@ -68,13 +68,14 @@ class HomeController extends Controller
|
|||
/**
|
||||
* Call the CRM to add a single optin
|
||||
*
|
||||
* @param \App\Http\Requests\Fo\SingleOptinRequest $request
|
||||
* @param \App\Http\Requests\Front\SingleOptinRequest $request
|
||||
* @return string|array|null|\Illuminate\Contracts\Translation\Translator
|
||||
* @throws \LDC\LDCCRM\Exceptions\ValidationException ValidationException.
|
||||
* @throws \Illuminate\Contracts\Container\BindingResolutionException BindingResolutionException.
|
||||
*/
|
||||
public function addOptIn(SingleOptinRequest $request)
|
||||
{
|
||||
public function addOptIn(
|
||||
SingleOptinRequest $request,
|
||||
): string|array|null|\Illuminate\Contracts\Translation\Translator {
|
||||
try {
|
||||
SingleOptin::setStatus($request->email, true);
|
||||
return \response(trans("Votre inscription à la newsletter a été prise en compte"), 200);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
namespace App\Http\Controllers\Front;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Enums\OrdersEnum;
|
||||
use App\Enums\YearsEnum;
|
||||
use App\Http\Requests\Front\PostsRequest;
|
||||
use App\Models\Document;
|
||||
use App\Models\DocumentTag;
|
||||
use App\Models\News;
|
||||
use App\Models\NewsTag;
|
||||
use App\Enums\YearsEnum;
|
||||
use App\Models\Document;
|
||||
use App\Enums\OrdersEnum;
|
||||
use App\Models\DocumentTag;
|
||||
use Illuminate\Support\Str;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Support\Collection;
|
||||
use App\Http\Requests\Front\PostsRequest;
|
||||
use Illuminate\Support\Str;
|
||||
use Uwil\LaravelCommon\Controllers\Controller;
|
||||
use Uwil\LaravelCommon\Helpers\CollectionHelper;
|
||||
|
||||
|
|
@ -26,12 +27,14 @@ class PostsController extends Controller
|
|||
/**
|
||||
* Extract unique years from date field in a collection.
|
||||
*
|
||||
* @param \Illuminate\Support\Collection $model
|
||||
* @param string $field
|
||||
* @param EloquentCollection $model
|
||||
* @param string $field
|
||||
* @return \Illuminate\Support\Collection
|
||||
*/
|
||||
private function getUniqueYearsFromCollection(Collection $model, string $field): \Illuminate\Support\Collection
|
||||
{
|
||||
private function getUniqueYearsFromCollection(
|
||||
EloquentCollection $model,
|
||||
string $field,
|
||||
): \Illuminate\Support\Collection {
|
||||
return $model->pluck($field)
|
||||
->map(fn($date) => Carbon::parse($date)->year)
|
||||
->unique()
|
||||
|
|
@ -55,27 +58,30 @@ class PostsController extends Controller
|
|||
}
|
||||
|
||||
/**
|
||||
* Build the common filters configuration for front-end listing pages
|
||||
* Build the common filters configuration for front-end listing pages.
|
||||
* If the type is "documents" the "order" filter is included.
|
||||
*
|
||||
* If the type is "documents" the "order" filter is included
|
||||
*
|
||||
* @param string $type
|
||||
* @param string $category
|
||||
* @param \Illuminate\Database\Eloquent\Collection $publishedTags
|
||||
* @param \Illuminate\Database\Eloquent\Collection $model
|
||||
* @param string $type
|
||||
* @param string $category
|
||||
* @param EloquentCollection $publishedTags
|
||||
* @param EloquentCollection $model
|
||||
* @return array
|
||||
*/
|
||||
private function getFilters(string $type, string $category, Collection $publishedTags, Collection $model): array
|
||||
{
|
||||
private function getFilters(
|
||||
string $type,
|
||||
string $category,
|
||||
EloquentCollection $publishedTags,
|
||||
EloquentCollection $model,
|
||||
): array {
|
||||
$filters = [];
|
||||
|
||||
$tags = collect($publishedTags)
|
||||
->map(fn($tag) => [
|
||||
'title' => $tag->name,
|
||||
'tag' => $tag->slug,
|
||||
'category' => $category,
|
||||
])
|
||||
->values();
|
||||
/** @phpstan-ignore-next-line */
|
||||
$tags = $publishedTags->map(fn(DocumentTag|NewsTag $tag) => [
|
||||
'title' => $tag->name,
|
||||
'tag' => $tag->slug,
|
||||
'category' => $category,
|
||||
])
|
||||
->values();
|
||||
|
||||
$enumYears = collect(YearsEnum::toArray())
|
||||
->map(fn($year) => YearsEnum::make((int) $year->value))
|
||||
|
|
@ -140,18 +146,19 @@ class PostsController extends Controller
|
|||
*/
|
||||
public function ajaxLoadNews(PostsRequest $request): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$validatedData = $request->validated();
|
||||
$perPage = 9;
|
||||
$selectedFilters = [];
|
||||
|
||||
$category = $data['category'];
|
||||
app()->setLocale($data['locale']);
|
||||
/** @var string $slugCategory */
|
||||
$slugCategory = $validatedData['category'];
|
||||
|
||||
$publishedNewsTags = NewsTag::getTagsByCategoryInCurrentLocale($category);
|
||||
app()->setLocale($validatedData['locale']);
|
||||
|
||||
$newsModel = News::getPublishedNewsByCategoryInCurrentLocale($category);
|
||||
$publishedNewsTags = NewsTag::getTagsByCategoryInCurrentLocale($slugCategory);
|
||||
$newsModel = News::getPublishedNewsByCategoryInCurrentLocale($slugCategory);
|
||||
|
||||
$requestedTags = collect($data['filters']['tags'] ?? '')
|
||||
$requestedTags = collect($validatedData['filters']['tags'] ?? '')
|
||||
->filter()
|
||||
->unique()
|
||||
->values();
|
||||
|
|
@ -162,13 +169,13 @@ class PostsController extends Controller
|
|||
'title' => $tag->name,
|
||||
'tag' => $tag->slug,
|
||||
'color' => $tag->color,
|
||||
'category' => $category,
|
||||
'category' => $slugCategory,
|
||||
])
|
||||
->values();
|
||||
|
||||
if ($selectedTags->isNotEmpty()) {
|
||||
$newsModel = $newsModel->filter(function ($news) use ($selectedTags) {
|
||||
return $news->tags->contains(
|
||||
$newsModel = $newsModel->filter(function ($newsModel) use ($selectedTags) {
|
||||
return $newsModel->tags->contains(
|
||||
fn ($tag) => $selectedTags->contains('id', $tag->id)
|
||||
);
|
||||
});
|
||||
|
|
@ -176,14 +183,14 @@ class PostsController extends Controller
|
|||
$selectedFilters['tags'] = $selectedTags;
|
||||
}
|
||||
|
||||
$selectedYears = collect($data['filters']['years'] ?? '')
|
||||
$selectedYears = collect($validatedData['filters']['years'] ?? '')
|
||||
->map(fn (string $label) => YearsEnum::makeFromLabel(Str::replace('-', '/', $label)))
|
||||
->filter()
|
||||
->map(fn($year) => [
|
||||
'title' => $year->label(),
|
||||
'tag' => Str::replace('/', '-', $year->label()),
|
||||
'value' => explode('/', $year->label())[1],
|
||||
'category' => $category,
|
||||
'category' => $slugCategory,
|
||||
])
|
||||
->values();
|
||||
|
||||
|
|
@ -197,7 +204,7 @@ class PostsController extends Controller
|
|||
|
||||
$newsModel = $newsModel->sortByDesc('published_at');
|
||||
|
||||
$filters = $this->getFilters('news', $category, $publishedNewsTags, $newsModel);
|
||||
$filters = $this->getFilters('news', $slugCategory, $publishedNewsTags, $newsModel);
|
||||
|
||||
// Custom pagination.
|
||||
$newsModel = CollectionHelper::paginate($newsModel, $perPage);
|
||||
|
|
@ -227,36 +234,36 @@ class PostsController extends Controller
|
|||
*/
|
||||
public function ajaxLoadDocuments(PostsRequest $request): \Illuminate\Http\JsonResponse
|
||||
{
|
||||
$data = $request->validated();
|
||||
$validatedData = $request->validated();
|
||||
$perPage = 3;
|
||||
$selectedFilters = [];
|
||||
|
||||
$category = $data['category'];
|
||||
app()->setLocale($data['locale']);
|
||||
/** @var string $slugCategory */
|
||||
$slugCategory = $validatedData['category'];
|
||||
|
||||
$publishedDocumentsTags = DocumentTag::getTagsByCategoryInCurrentLocale($category);
|
||||
app()->setLocale($validatedData['locale']);
|
||||
|
||||
$documentsModel = Document::getPublishedDocumentsByCategoryInCurrentLocale($category);
|
||||
$documentTagModels = DocumentTag::getTagsByCategoryInCurrentLocale($slugCategory);
|
||||
$documentModels = Document::getPublishedDocumentsByCategoryInCurrentLocale($slugCategory);
|
||||
|
||||
$requestedTags = collect($data['filters']['tags'] ?? '')
|
||||
$requestedTags = collect($validatedData['filters']['tags'] ?? '')
|
||||
->filter()
|
||||
->unique()
|
||||
->values();
|
||||
|
||||
$selectedTags = $publishedDocumentsTags
|
||||
$selectedTags = $documentTagModels
|
||||
->filter(fn ($tag) => $requestedTags->contains($tag->slug ?? null))
|
||||
->map(fn ($tag) => [
|
||||
'id' => $tag->id,
|
||||
'title' => $tag->name,
|
||||
'tag' => $tag->slug,
|
||||
'color' => $tag->color,
|
||||
'category' => $category,
|
||||
'category' => $slugCategory,
|
||||
])
|
||||
->values();
|
||||
|
||||
if ($selectedTags->isNotEmpty()) {
|
||||
$documentsModel = $documentsModel->filter(function ($documents) use ($selectedTags) {
|
||||
return $documents->tags->contains(
|
||||
$documentModels = $documentModels->filter(function ($documentModel) use ($selectedTags) {
|
||||
return $documentModel->tags->contains(
|
||||
fn ($tag) => $selectedTags->contains('id', $tag->id)
|
||||
);
|
||||
});
|
||||
|
|
@ -264,64 +271,65 @@ class PostsController extends Controller
|
|||
$selectedFilters['tags'] = $selectedTags;
|
||||
}
|
||||
|
||||
$selectedYears = collect($data['filters']['years'] ?? '')
|
||||
$selectedYears = collect($validatedData['filters']['years'] ?? '')
|
||||
->map(fn (string $label) => YearsEnum::makeFromLabel(Str::replace('-', '/', $label)))
|
||||
->filter()
|
||||
->map(fn($year) => [
|
||||
'title' => $year->label(),
|
||||
'tag' => Str::replace('/', '-', $year->label()),
|
||||
'value' => $year->value,
|
||||
'category' => $category,
|
||||
'category' => $slugCategory,
|
||||
])
|
||||
->values();
|
||||
|
||||
if ($selectedYears->isNotEmpty()) {
|
||||
$documentsModel = $documentsModel->filter(function ($documents) use ($selectedYears) {
|
||||
return $selectedYears->contains('value', $documents->exercice);
|
||||
$documentModels = $documentModels->filter(function ($documentModel) use ($selectedYears) {
|
||||
return $selectedYears->contains('value', $documentModel->exercice);
|
||||
});
|
||||
|
||||
$selectedFilters['years'] = $selectedYears;
|
||||
}
|
||||
|
||||
$selectedOrder = collect($data['filters']['order'] ?? '')
|
||||
$selectedOrder = collect($validatedData['filters']['order'] ?? '')
|
||||
->map(fn (string $label) => OrdersEnum::makeFromLabel($label))
|
||||
->filter()
|
||||
->map(fn($order) => [
|
||||
'title' => trans($order->label()),
|
||||
'tag' => $order->name(),
|
||||
'category' => $category,
|
||||
'category' => $slugCategory,
|
||||
])
|
||||
->values();
|
||||
|
||||
if ($selectedOrder->isNotEmpty()) {
|
||||
$documentsModel = $selectedOrder->map->tag->first() === 'desc'
|
||||
? $documentsModel->sortByDesc('published_at')
|
||||
: $documentsModel->sortBy('published_at');
|
||||
$firstTag = $selectedOrder->map(fn($order) => $order['tag'])->first();
|
||||
$documentModels = $firstTag === 'desc'
|
||||
? $documentModels->sortByDesc('published_at')
|
||||
: $documentModels->sortBy('published_at');
|
||||
|
||||
$selectedFilters['order'] = $selectedOrder->all();
|
||||
} else {
|
||||
$documentsModel = $documentsModel->sortByDesc('published_at');
|
||||
$documentModels = $documentModels->sortByDesc('published_at');
|
||||
}
|
||||
|
||||
$filters = $this->getFilters('documents', $category, $publishedDocumentsTags, $documentsModel);
|
||||
$filters = $this->getFilters('documents', $slugCategory, $documentTagModels, $documentModels);
|
||||
|
||||
// Custom pagination.
|
||||
$documentsModel = CollectionHelper::paginate($documentsModel, $perPage);
|
||||
$documentModels = CollectionHelper::paginate($documentModels, $perPage);
|
||||
|
||||
return response()->json([
|
||||
'pagination' => [
|
||||
'total' => $documentsModel->total(),
|
||||
'nbItems' => count($documentsModel->items()),
|
||||
'currentPage' => $documentsModel->currentPage(),
|
||||
'lastPage' => $documentsModel->lastPage(),
|
||||
'hasMore' => $documentsModel->currentPage() < $documentsModel->lastPage(),
|
||||
'total' => $documentModels->total(),
|
||||
'nbItems' => count($documentModels->items()),
|
||||
'currentPage' => $documentModels->currentPage(),
|
||||
'lastPage' => $documentModels->lastPage(),
|
||||
'hasMore' => $documentModels->currentPage() < $documentModels->lastPage(),
|
||||
],
|
||||
'filters' => view('components.front.posts.filters-dropdowns', ['filters' => $filters])->render(),
|
||||
'selectedFilters' => view('components.front.posts.selected-filters', [
|
||||
'selectedFilters' => $selectedFilters,
|
||||
])->render(),
|
||||
'news' => view('components.front.posts.documents', [
|
||||
'documentsList' => $documentsModel->items(),
|
||||
'documentsList' => $documentModels->items(),
|
||||
])->render()
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Middleware;
|
|||
use App\Enums\DynamicFieldTypes;
|
||||
use App\Enums\ModelsList;
|
||||
use App\Enums\StaticRoutesEnum;
|
||||
use App\Enums\VideoProviderEnum;
|
||||
use App\Helpers\TranslationHelper;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyActivity;
|
||||
|
|
@ -139,6 +140,7 @@ class HandleInertiaRequests extends Middleware
|
|||
'enumDynamicFieldTypes' => DynamicFieldTypes::toArray(),
|
||||
'enumModelsList' => ModelsList::toArray(),
|
||||
'enumStaticRoutes' => StaticRoutesEnum::getRoutes(),
|
||||
'videoProviderEnum' => VideoProviderEnum::toArray(),
|
||||
'errors' => $errors,
|
||||
'translations' => $translations,
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\DocumentCategory;
|
||||
namespace App\Http\Requests\Back\DocumentCategories;
|
||||
|
||||
use App\Models\DocumentCategory;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\DocumentCategory;
|
||||
namespace App\Http\Requests\Back\DocumentCategories;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\DocumentTag;
|
||||
namespace App\Http\Requests\Back\DocumentTags;
|
||||
|
||||
use App\Models\DocumentTag;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\DocumentTag;
|
||||
namespace App\Http\Requests\Back\DocumentTags;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\Document;
|
||||
namespace App\Http\Requests\Back\Documents;
|
||||
|
||||
use App\Enums\Document\DocumentStatusEnum;
|
||||
use App\Enums\YearsEnum;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\Document;
|
||||
namespace App\Http\Requests\Back\Documents;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -4,6 +4,7 @@ namespace App\Http\Requests\Back\News;
|
|||
|
||||
use App\Enums\News\NewsStatusEnum;
|
||||
use App\Models\News;
|
||||
use App\Rules\Video;
|
||||
use App\Traits\Requests\HasInertiaPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\NewsCategory;
|
||||
namespace App\Http\Requests\Back\NewsCategories;
|
||||
|
||||
use App\Models\NewsCategory;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\NewsCategory;
|
||||
namespace App\Http\Requests\Back\NewsCategories;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\NewsTag;
|
||||
namespace App\Http\Requests\Back\NewsTags;
|
||||
|
||||
use App\Models\NewsTag;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Back\NewsTag;
|
||||
namespace App\Http\Requests\Back\NewsTags;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ class StorePageConfigRequest extends FormRequest
|
|||
'string',
|
||||
'between:3,255',
|
||||
'regex:/^[a-z0-9]+(?:[-_][a-z0-9]+)*$/',
|
||||
function ($attribute, $value, $fail) use ($prefix) {
|
||||
function ($attribute, $value, $fail) {
|
||||
$parentPath = implode('.', array_slice(explode('.', $attribute), 0, -2));
|
||||
$levelItems = data_get($this->input(), $parentPath, []);
|
||||
$keysAtLevel = collect($levelItems)->pluck('key')->filter()->toArray();
|
||||
|
|
|
|||
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
namespace App\Http\Requests\Back;
|
||||
|
||||
use App\Enums\Contact\ContactFormType;
|
||||
use App\Enums\ElementPositionEnum;
|
||||
use App\Enums\DynamicFieldTypes;
|
||||
use App\Enums\StaticRoutesEnum;
|
||||
use App\Models\PageConfig;
|
||||
use App\Rules\Video;
|
||||
use App\Traits\Requests\HasInertiaPicture;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
|
|
@ -201,6 +203,19 @@ abstract class UpdateDynamicFieldRequest extends FormRequest
|
|||
),
|
||||
]);
|
||||
break;
|
||||
case DynamicFieldTypes::contactform->value:
|
||||
$rules = array_merge($rules, [
|
||||
$ruleKey => array_merge(
|
||||
$this->compileValidate(DynamicFieldTypes::position->getValidation(), $input),
|
||||
[new Enum(ContactFormType::class)],
|
||||
),
|
||||
]);
|
||||
break;
|
||||
case DynamicFieldTypes::video->value:
|
||||
$rules = array_merge($rules, [
|
||||
$ruleKey => new Video(),
|
||||
]);
|
||||
break;
|
||||
default:
|
||||
throw new \UnexpectedValueException("Unrecognized dynamic type: " . ($input['type'] ?? 'null'));
|
||||
} //end switch
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ namespace App\Http\Requests\Front\Contact;
|
|||
use App\Enums\Contact\BreedType;
|
||||
use App\Enums\Contact\ContactFormType;
|
||||
use App\Enums\Contact\CustomerAskingType;
|
||||
use App\Enums\Contact\ExportAskingType;
|
||||
use App\Enums\Contact\PressType;
|
||||
use App\Enums\Department;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
|
@ -93,14 +94,21 @@ class ContactRequest extends FormRequest
|
|||
|
||||
// Validation for restauration and export commun fields.
|
||||
if (
|
||||
ContactFormType::restauration->equals(ContactFormType::make($typeContactForm)) ||
|
||||
ContactFormType::export->equals(ContactFormType::make($typeContactForm))
|
||||
ContactFormType::restauration->equals(ContactFormType::make($typeContactForm))
|
||||
) {
|
||||
$rules = array_merge($rules, [
|
||||
'type' => ['array', 'min:1'],
|
||||
'type.*' => [Rule::enum(CustomerAskingType::class)],
|
||||
]);
|
||||
}
|
||||
if (
|
||||
ContactFormType::export->equals(ContactFormType::make($typeContactForm))
|
||||
) {
|
||||
$rules = array_merge($rules, [
|
||||
'type' => ['array', 'min:1'],
|
||||
'type.*' => [Rule::enum(ExportAskingType::class)],
|
||||
]);
|
||||
}
|
||||
|
||||
// Validation for press enum.
|
||||
if (ContactFormType::press->equals(ContactFormType::make($typeContactForm))) {
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
|
||||
namespace App\Http\Requests\Front;
|
||||
|
||||
use App\Traits\Requests\Api\HasLaravelFailedValidation;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class PostsRequest extends FormRequest
|
||||
{
|
||||
use HasLaravelFailedValidation;
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
|
|
@ -16,7 +19,7 @@ class PostsRequest extends FormRequest
|
|||
return [
|
||||
'page' => 'required|integer',
|
||||
'category' => 'required|string',
|
||||
'locale' => 'required|string',
|
||||
'locale' => ['required', 'string', Rule::in(config('app.locales'))],
|
||||
'filters' => 'nullable|array',
|
||||
'filters.order' => 'nullable|string|in:asc,desc',
|
||||
'filters.tags' => 'nullable|array',
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ class Company extends Model
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string, array[]>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
|
|
@ -67,7 +67,7 @@ class Company extends Model
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'companyActivity',
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class CompanyActivity extends Model implements TranslatableModel
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string, array[]>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ class CompanySector extends Model implements TranslatableModel
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string, array[]>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ class ContactCount extends Model
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'type',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use App\Enums\Document\DocumentStatusEnum;
|
|||
use App\Enums\YearsEnum;
|
||||
use App\Helpers\DocumentHelper;
|
||||
use App\Helpers\TranslationHelper;
|
||||
use App\Models\Documentable;
|
||||
use App\Traits\Models\HasCache;
|
||||
use App\Traits\Models\HasTranslations;
|
||||
use App\Traits\Models\HasUwilImages;
|
||||
|
|
@ -18,6 +19,32 @@ use Illuminate\Support\Facades\App;
|
|||
use Laravel\Scout\Searchable;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* Id of the document.
|
||||
* @property string $name
|
||||
* Name of the document.
|
||||
* @property string $path
|
||||
* Path of the document.
|
||||
* @property boolean $is_issuu
|
||||
* Issu status of the document.
|
||||
* @property string $issuu_path
|
||||
* Issu path of the document.
|
||||
* @property string $exercice
|
||||
* Exercice of the document.
|
||||
* @property \App\Enums\Document\DocumentStatusEnum $status
|
||||
* Status of the document.
|
||||
* @property-read \Carbon\Carbon $created_at
|
||||
* Created date of the document.
|
||||
* @property-read \Carbon\Carbon $updated_at
|
||||
* Updated date of the document.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at
|
||||
* Deleted date of the document.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\DocumentCategory> $categories
|
||||
* Associated document categories of the document.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\DocumentTag> $tags
|
||||
* Associated document tags of the document.
|
||||
*/
|
||||
class Document extends Model implements TranslatableModel
|
||||
{
|
||||
use ActivityLog;
|
||||
|
|
@ -81,7 +108,7 @@ class Document extends Model implements TranslatableModel
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'categories',
|
||||
|
|
@ -324,24 +351,24 @@ class Document extends Model implements TranslatableModel
|
|||
/**
|
||||
* Get all published news by category.
|
||||
*
|
||||
* @param string $category
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @param string $slugCategory
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document>
|
||||
*/
|
||||
public static function getPublishedDocumentsByCategoryInCurrentLocale(string $category)
|
||||
{
|
||||
public static function getPublishedDocumentsByCategoryInCurrentLocale(
|
||||
string $slugCategory,
|
||||
): \Illuminate\Database\Eloquent\Collection {
|
||||
$locale = App::getLocale();
|
||||
|
||||
return Document::getModelsCached()
|
||||
->where('status', DocumentStatusEnum::published)
|
||||
->filter(function ($document) use ($category, $locale) {
|
||||
->filter(function ($document) use ($slugCategory, $locale) {
|
||||
$hasPath = !empty($document->getTranslation('path', $locale, false));
|
||||
|
||||
$hasIssuuPath = $document->is_issuu === true
|
||||
&& !empty($document->getTranslation('issuu_path', $locale, false));
|
||||
|
||||
return ($hasPath || $hasIssuuPath) && $document->categories->contains(
|
||||
fn($cat) => $cat->slug === $category
|
||||
fn($documentCategoryModel) => $documentCategoryModel->slug === $slugCategory
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -349,11 +376,11 @@ class Document extends Model implements TranslatableModel
|
|||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Relation to Tag
|
||||
* Relation to Tag.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function tags(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
public function tags(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphedByMany(
|
||||
DocumentTag::class,
|
||||
|
|
@ -361,15 +388,15 @@ class Document extends Model implements TranslatableModel
|
|||
'documentables',
|
||||
'document_id',
|
||||
'documentable_id'
|
||||
)->withTimestamps();
|
||||
)->using(Documentable::class)->withTimestamps();
|
||||
}
|
||||
|
||||
/**
|
||||
* Relation to Category
|
||||
* Relation to Category.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function categories(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
public function categories(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphedByMany(
|
||||
DocumentCategory::class,
|
||||
|
|
@ -377,6 +404,6 @@ class Document extends Model implements TranslatableModel
|
|||
'documentables',
|
||||
'document_id',
|
||||
'documentable_id'
|
||||
)->withTimestamps();
|
||||
)->using(Documentable::class)->withTimestamps();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,22 @@ use Illuminate\Database\Eloquent\Model;
|
|||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* Id of the category.
|
||||
* @property string $name
|
||||
* Name of the category.
|
||||
* @property string $slug
|
||||
* Slug of the category.
|
||||
* @property-read \Carbon\Carbon $created_at
|
||||
* Created date of the category.
|
||||
* @property-read \Carbon\Carbon $updated_at
|
||||
* Updated date of the category.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at
|
||||
* Deleted date of the category.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
|
||||
* Associated documents of the category.
|
||||
*/
|
||||
class DocumentCategory extends Model implements TranslatableModel
|
||||
{
|
||||
use ActivityLog;
|
||||
|
|
@ -38,12 +54,14 @@ class DocumentCategory extends Model implements TranslatableModel
|
|||
'slug'
|
||||
];
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Relation to Document
|
||||
* Associated documents of the category.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function documents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
public function documents(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
Document::class,
|
||||
|
|
|
|||
|
|
@ -11,6 +11,24 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
|||
use Illuminate\Support\Facades\App;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
|
||||
/**
|
||||
* @property integer $id
|
||||
* Id of the tag.
|
||||
* @property string $name
|
||||
* Name of the tag.
|
||||
* @property string $slug
|
||||
* Slug of the tag.
|
||||
* @property string $color
|
||||
* Color of the tag.
|
||||
* @property-read \Carbon\Carbon $created_at
|
||||
* Created date of the tag.
|
||||
* @property-read \Carbon\Carbon $updated_at
|
||||
* Updated date of the tag.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at
|
||||
* Deleted date of the tag.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\Document> $documents
|
||||
* Associated documents of the tag.
|
||||
*/
|
||||
class DocumentTag extends Model implements TranslatableModel
|
||||
{
|
||||
use ActivityLog;
|
||||
|
|
@ -43,34 +61,20 @@ class DocumentTag extends Model implements TranslatableModel
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'documents',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relation to Document
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function documents(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
Document::class,
|
||||
'documentable',
|
||||
'documentables',
|
||||
'documentable_id',
|
||||
'document_id'
|
||||
);
|
||||
}
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get all Tags by category.
|
||||
*
|
||||
* @param string $slugCategory
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\DocumentTag>
|
||||
*/
|
||||
public static function getTagsByCategoryInCurrentLocale(
|
||||
string $slugCategory,
|
||||
|
|
@ -92,4 +96,22 @@ class DocumentTag extends Model implements TranslatableModel
|
|||
})->isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Associated documents of the tag.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function documents(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
Document::class,
|
||||
'documentable',
|
||||
'documentables',
|
||||
'documentable_id',
|
||||
'document_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
app/Models/Documentable.php
Normal file
35
app/Models/Documentable.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphPivot;
|
||||
|
||||
/**
|
||||
* @property integer $id Id of the document.
|
||||
* @property \App\Models\Document $document_id Name of the document.
|
||||
* @property \App\Models\DocumentCategory|\App\Models\DocumentTag $documentable_id Name of the category or tag.
|
||||
* @property class-string<static> $documentable_type Class of the category or tag.
|
||||
* @property-read \Carbon\Carbon $created_at Created date of the document.
|
||||
* @property-read \Carbon\Carbon $updated_at Updated date of the document.
|
||||
*/
|
||||
class Documentable extends MorphPivot
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $table = 'documentables';
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::created(fn() => Document::resetCache());
|
||||
static::updated(fn() => Document::resetCache());
|
||||
static::deleted(fn() => Document::resetCache());
|
||||
}
|
||||
}
|
||||
|
|
@ -13,11 +13,15 @@ use App\Traits\Models\HasUwilImages;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
use Spatie\Sitemap\Contracts\Sitemapable;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
use Uwil\SitemapGenerator\Lib\SitemapHelper;
|
||||
|
||||
/**
|
||||
* @property string $id_key Unique key identification of the dynamic page.
|
||||
|
|
@ -33,7 +37,7 @@ use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
|||
* @property-read \Illuminate\Support\Carbon $updated_at Date on which the dynamic page was updated.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at Date on which the dynamic page was updated.
|
||||
*/
|
||||
class DynamicPage extends Model implements TranslatableModel
|
||||
class DynamicPage extends Model implements TranslatableModel, Sitemapable
|
||||
{
|
||||
use ActivityLog;
|
||||
use HasCache;
|
||||
|
|
@ -86,7 +90,7 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'config',
|
||||
|
|
@ -104,7 +108,14 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
static::creating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
// Set id_key from title.
|
||||
$model->id_key = Str::slug($model->title);
|
||||
$idKey = self::generateUniqueField(
|
||||
'dynamic_pages',
|
||||
'id_key',
|
||||
Str::slug($model->title),
|
||||
$model->id
|
||||
);
|
||||
|
||||
$model->id_key = $idKey;
|
||||
});
|
||||
static::updating(function (self $model) {
|
||||
self::manageFields($model);
|
||||
|
|
@ -131,6 +142,42 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
}
|
||||
|
||||
// * METHODS
|
||||
/**
|
||||
* Generate unique value from model field
|
||||
*
|
||||
* @param string $table
|
||||
* @param string $field
|
||||
* @param string $baseValue
|
||||
* @param mixed $ignoreId
|
||||
* @return string
|
||||
*/
|
||||
public static function generateUniqueField(
|
||||
string $table,
|
||||
string $field,
|
||||
string $baseValue,
|
||||
int|string|null $ignoreId = null
|
||||
): string {
|
||||
$value = $baseValue;
|
||||
$counter = 1;
|
||||
|
||||
// Boucle tant que le slug existe en base
|
||||
while (true) {
|
||||
$query = DB::table($table)->where($field, $value);
|
||||
|
||||
if ($ignoreId) {
|
||||
$query->where('id', '!=', $ignoreId);
|
||||
}
|
||||
|
||||
if (!$query->exists()) {
|
||||
// On a trouvé un slug unique
|
||||
return $value;
|
||||
}
|
||||
|
||||
// Sinon on incrémente le suffixe
|
||||
$value = $baseValue . '_' . $counter;
|
||||
$counter++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpublished all children pages if change status published from draft/unpublished.
|
||||
|
|
@ -198,8 +245,8 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
self::assertRootIsUnique($model);
|
||||
} else {
|
||||
$slug = $model->slug ?? Str::slug($model->title);
|
||||
TranslationHelper::assertTranslationFieldIsUnique($model, $model->title, 'title');
|
||||
TranslationHelper::assertTranslationFieldIsUnique($model, $slug, 'slug');
|
||||
TranslationHelper::assertTranslationFieldIsUnique($model, $model->title, 'title', $model->page_id, 'page_id');
|
||||
TranslationHelper::assertTranslationFieldIsUnique($model, $slug, 'slug', $model->page_id, 'page_id');
|
||||
$model->setAttribute('slug', $slug);
|
||||
}
|
||||
// Check translations.
|
||||
|
|
@ -327,7 +374,7 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
/**
|
||||
* Get fields that we want to keep in the current locale.
|
||||
*
|
||||
* @return array.
|
||||
* @return array
|
||||
*/
|
||||
public static function searchTransFields(): array
|
||||
{
|
||||
|
|
@ -413,7 +460,7 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
? strip_tags(collect($this->getTranslation('content', $locale))
|
||||
->flatten()
|
||||
->filter(fn($value, $key) =>
|
||||
is_string($value) &&
|
||||
is_string($value) &&
|
||||
!empty($value) &&
|
||||
!is_numeric($value) &&
|
||||
!in_array($key, ['order', 'id']) &&
|
||||
|
|
@ -428,6 +475,47 @@ class DynamicPage extends Model implements TranslatableModel
|
|||
return $data;
|
||||
}
|
||||
|
||||
// * SITEMAP
|
||||
|
||||
/**
|
||||
* toSitemapTag method
|
||||
*
|
||||
* @return Url|string|array
|
||||
*/
|
||||
public function toSitemapTag(): Url | string | array
|
||||
{
|
||||
$sitemapLines = [];
|
||||
$locales = array_keys(config('laravellocalization.supportedLocales', ['fr' => "Français"]));
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
$slug = $this->getTranslation('slug', $locale, false);
|
||||
|
||||
// Homepage has no slug.
|
||||
if ($slug || $this->isRoot) {
|
||||
$sitemapLines[] = SitemapHelper::generateUrl(
|
||||
$this->getLocalizedUrl($locale),
|
||||
locales: $this->locales(),
|
||||
getAlternate: fn (string $locale) => $this->getLocalizedUrl($locale)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $sitemapLines;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized URL.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return string|false
|
||||
*/
|
||||
public function getLocalizedUrl(string $locale): string|false
|
||||
{
|
||||
return in_array($locale, $this->locales()) ?
|
||||
route("front.dynamic-pages.$this->id_key.$locale") :
|
||||
false;
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ class MailContactData extends Model
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string, array[]>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'enumType',
|
||||
|
|
|
|||
|
|
@ -17,34 +17,56 @@ use Illuminate\Support\Arr;
|
|||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Scout\Searchable;
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
use Mcamara\LaravelLocalization\Interfaces\LocalizedUrlRoutable;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
use Spatie\Sitemap\Tags\Url;
|
||||
use Uwil\SitemapGenerator\Interfaces\Sitemapable;
|
||||
use Uwil\SitemapGenerator\Lib\SitemapHelper;
|
||||
|
||||
/**
|
||||
* @property string $seo_title SEO title of the news.
|
||||
* @property string $seo_description SEO description of the news.
|
||||
* @property string $title Title of the news.
|
||||
* @property string $slug Slug of the news.
|
||||
* @property string $picture Main img of the news.
|
||||
* @property string $picture_alt Alt text for the picture.
|
||||
* @property string $picture_title Title for the picture.
|
||||
* @property string $content Content of the news.
|
||||
* @property boolean $inverted Are image and content inverted in front
|
||||
* @property string $vimeo_id Optionnal vimeo_id
|
||||
* @property boolean $has_show_picture Show the picture in the show page
|
||||
* @property \App\Enums\News\NewsStatusEnum $status Status of the news.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Date on which the news was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Date on which the news was updated.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at Date on which the news was updated.
|
||||
* @property string $seo_title
|
||||
* SEO title of the news.
|
||||
* @property string $seo_description
|
||||
* SEO description of the news.
|
||||
* @property string $title
|
||||
* Title of the news.
|
||||
* @property string $slug
|
||||
* Slug of the news.
|
||||
* @property string $picture
|
||||
* Main img of the news.
|
||||
* @property string $picture_alt
|
||||
* Alt text for the picture.
|
||||
* @property string $picture_title
|
||||
* Title for the picture.
|
||||
* @property string $content
|
||||
* Content of the news.
|
||||
* @property boolean $inverted
|
||||
* Are image and content inverted in front
|
||||
* @property string $vimeo_id
|
||||
* Optionnal vimeo_id
|
||||
* @property boolean $has_show_picture
|
||||
* Show the picture in the show page
|
||||
* @property \App\Enums\News\NewsStatusEnum $status
|
||||
* Status of the news.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at
|
||||
* Date on which the news was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at
|
||||
* Date on which the news was updated.
|
||||
* @property-read \Illuminate\Support\Carbon $deleted_at
|
||||
* Date on which the news was updated.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\NewsCategory> $categories
|
||||
* Associated news categories of the news.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\NewsTag> $tags
|
||||
* Associated news tags of the news.
|
||||
*/
|
||||
class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
||||
class News extends Model implements TranslatableModel, LocalizedUrlRoutable, Sitemapable
|
||||
{
|
||||
use ActivityLog;
|
||||
use HasCache;
|
||||
use HasFactory;
|
||||
use HasTranslations;
|
||||
use SoftDeletes;
|
||||
use HasCache;
|
||||
use HasUwilImages {
|
||||
HasUwilImages::inertiaFill as traitInertiaFill;
|
||||
}
|
||||
|
|
@ -113,7 +135,7 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
'unpublished_at',
|
||||
'inverted',
|
||||
'vimeo_id',
|
||||
'has_show_picture'
|
||||
'has_show_picture',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -145,7 +167,7 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'categories',
|
||||
|
|
@ -332,9 +354,9 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
* Get the url to access a specific resource.
|
||||
*
|
||||
* @param integer $id Id field of the target model.
|
||||
* @return string
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getAccessModel(int $id): string
|
||||
public static function getAccessModel(int $id): ?string
|
||||
{
|
||||
$targetModel = self::getModelsCached()->firstWhere('id', $id);
|
||||
if (!$targetModel) {
|
||||
|
|
@ -432,8 +454,7 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
* Get all published news by category.
|
||||
*
|
||||
* @param string $category
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\News>
|
||||
*/
|
||||
public static function getPublishedNewsByCategoryInCurrentLocale(string $category)
|
||||
{
|
||||
|
|
@ -448,6 +469,49 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
});
|
||||
}
|
||||
|
||||
// * SITEMAP
|
||||
|
||||
/**
|
||||
* toSitemapTag method
|
||||
*
|
||||
* @return Url|string|array
|
||||
*/
|
||||
public function toSitemapTag(): Url | string | array
|
||||
{
|
||||
$model = $this;
|
||||
$locales = $model->locales();
|
||||
$sitemapUrls = [];
|
||||
|
||||
foreach ($locales as $locale) {
|
||||
if (in_array($locale, config('sitemap-generator.supported_locales'))) {
|
||||
$sitemapUrls[] = SitemapHelper::generateUrl(
|
||||
$model->getLocalizedUrl($locale),
|
||||
$model,
|
||||
$locales,
|
||||
currentLocale: $locale,
|
||||
isNews: true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $sitemapUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get localized URL from Mcamara\LaravelLocalization.
|
||||
*
|
||||
* @param string $locale
|
||||
* @return string
|
||||
*/
|
||||
public function getLocalizedUrl(string $locale): string
|
||||
{
|
||||
return LaravelLocalization::getURLFromRouteNameTranslated(
|
||||
$locale,
|
||||
'routes.front.news',
|
||||
['news' => $this]
|
||||
) ?? route('front.news.show', $this);
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
|
|
@ -463,7 +527,7 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
'newsables',
|
||||
'news_id',
|
||||
'newsable_id'
|
||||
);
|
||||
)->using(Newsable::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -479,6 +543,6 @@ class News extends Model implements TranslatableModel, LocalizedUrlRoutable
|
|||
'newsables',
|
||||
'news_id',
|
||||
'newsable_id'
|
||||
)->withTimestamps();
|
||||
)->using(Newsable::class)->withTimestamps();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,21 @@ use App\Traits\Models\HasCache;
|
|||
use App\Traits\Models\HasTranslations;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Support\Str;
|
||||
use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
||||
|
||||
/**
|
||||
* @property int $news_id News focused.
|
||||
* @property string $name Name of the category.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Date on which the news was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Date on which the news was updated.
|
||||
* @property int $id
|
||||
* Id of the news.
|
||||
* @property string $name
|
||||
* Name of the category.
|
||||
* @property-read \Illuminate\Support\Carbon $created_at
|
||||
* Date on which the category was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at
|
||||
* Date on which the category was updated.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\News> $news
|
||||
* Associated news of the category.
|
||||
*/
|
||||
class NewsCategory extends Model implements TranslatableModel
|
||||
{
|
||||
|
|
@ -30,7 +35,7 @@ class NewsCategory extends Model implements TranslatableModel
|
|||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array<int, string, array[]>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
|
|
@ -63,6 +68,8 @@ class NewsCategory extends Model implements TranslatableModel
|
|||
});
|
||||
}
|
||||
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Manage model fields.
|
||||
*
|
||||
|
|
@ -80,12 +87,14 @@ class NewsCategory extends Model implements TranslatableModel
|
|||
TranslationHelper::validateFallbackTranslations($model);
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* The relation with News
|
||||
* Associated news of the category.
|
||||
*
|
||||
* @return BelongsToMany
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function news(): BelongsToMany
|
||||
public function news(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
News::class,
|
||||
|
|
|
|||
|
|
@ -12,11 +12,18 @@ use Uwil\LaravelActivityLogs\Traits\ActivityLog;
|
|||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
/**
|
||||
* @property int $news_id News focused.
|
||||
* @property string $name Name of the tag.
|
||||
* @property string $color Color of the tag
|
||||
* @property-read \Illuminate\Support\Carbon $created_at Date on which the news was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at Date on which the news was updated.
|
||||
* @property int $news_id
|
||||
* News focused.
|
||||
* @property string $name
|
||||
* Name of the tag.
|
||||
* @property string $color
|
||||
* Color of the tag
|
||||
* @property-read \Illuminate\Support\Carbon $created_at
|
||||
* Date on which the tag was created.
|
||||
* @property-read \Illuminate\Support\Carbon $updated_at
|
||||
* Date on which the tag was updated.
|
||||
* @property \Illuminate\Database\Eloquent\Collection<int, \App\Models\News> $news
|
||||
* Associated news of the tag.
|
||||
*/
|
||||
class NewsTag extends Model implements TranslatableModel
|
||||
{
|
||||
|
|
@ -50,33 +57,19 @@ class NewsTag extends Model implements TranslatableModel
|
|||
/**
|
||||
* Relations should be included in the cache.
|
||||
*
|
||||
* @var array<string, string>
|
||||
* @var list<string>
|
||||
*/
|
||||
protected static $cacheRelations = [
|
||||
'news',
|
||||
];
|
||||
|
||||
/**
|
||||
* Relation to Document
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
*/
|
||||
public function news(): \Illuminate\Database\Eloquent\Relations\BelongsToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
News::class,
|
||||
'newsable',
|
||||
'newsables',
|
||||
'newsable_id',
|
||||
'news_id'
|
||||
);
|
||||
}
|
||||
// * METHODS
|
||||
|
||||
/**
|
||||
* Get all Tags by category.
|
||||
*
|
||||
* @param string $slugCategory
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
* @return \Illuminate\Database\Eloquent\Collection<int, \App\Models\NewsTag>
|
||||
*/
|
||||
public static function getTagsByCategoryInCurrentLocale(
|
||||
string $slugCategory,
|
||||
|
|
@ -93,4 +86,22 @@ class NewsTag extends Model implements TranslatableModel
|
|||
})->isNotEmpty();
|
||||
});
|
||||
}
|
||||
|
||||
// * RELATIONSHIPS
|
||||
|
||||
/**
|
||||
* Associated news of the tag.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
*/
|
||||
public function news(): \Illuminate\Database\Eloquent\Relations\MorphToMany
|
||||
{
|
||||
return $this->morphToMany(
|
||||
News::class,
|
||||
'newsable',
|
||||
'newsables',
|
||||
'newsable_id',
|
||||
'news_id'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
35
app/Models/Newsable.php
Normal file
35
app/Models/Newsable.php
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\MorphPivot;
|
||||
|
||||
/**
|
||||
* @property integer $id Id of the news.
|
||||
* @property \App\Models\News $news_id Name of the news.
|
||||
* @property \App\Models\NewsCategory|\App\Models\NewsTag $newsable_id Name of the category or tag.
|
||||
* @property class-string<static> $newsable_type Class of the category or tag.
|
||||
* @property-read \Carbon\Carbon $created_at Created date of the news.
|
||||
* @property-read \Carbon\Carbon $updated_at Updated date of the news.
|
||||
*/
|
||||
class Newsable extends MorphPivot
|
||||
{
|
||||
/**
|
||||
* The table associated with the model.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $table = 'newsables';
|
||||
|
||||
/**
|
||||
* Perform any actions required after the model boots.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::created(fn() => News::resetCache());
|
||||
static::updated(fn() => News::resetCache());
|
||||
static::deleted(fn() => News::resetCache());
|
||||
}
|
||||
}
|
||||
|
|
@ -113,51 +113,21 @@ class PageConfig extends Model implements TranslatableModel
|
|||
TranslationHelper::validateFallbackTranslations($model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the config is updated.
|
||||
*
|
||||
* @param array $newArray
|
||||
* @param array $originalArray
|
||||
* @return boolean
|
||||
*/
|
||||
private static function isConfigUpdated(array $newArray, array $originalArray): bool
|
||||
{
|
||||
if (count($newArray) !== count($originalArray)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($newArray as $i => $subA) {
|
||||
$subB = $originalArray[$i] ?? null;
|
||||
if ($subB === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ksort($subA);
|
||||
ksort($subB);
|
||||
|
||||
if ($subA != $subB) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the target model from a specific id and model name.
|
||||
*
|
||||
* @param string $modelId
|
||||
* @param string $modelName
|
||||
* @return \Illuminate\Database\Eloquent\Model
|
||||
* @return \Illuminate\Database\Eloquent\Model|null
|
||||
*/
|
||||
public static function getModelFromInputModelConfig(
|
||||
string $modelId,
|
||||
string $modelName,
|
||||
): \Illuminate\Database\Eloquent\Model {
|
||||
): ?\Illuminate\Database\Eloquent\Model {
|
||||
$studly = Str::studly(Str::singular($modelName));
|
||||
$class = "App\\Models\\{$studly}";
|
||||
|
||||
if (!$class || !class_exists($class)) {
|
||||
if (!class_exists($class)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class DynamicPageRouteServiceProvider extends ServiceProvider
|
|||
$slug = $dynamicPageModel->getTranslation('slug', $locale);
|
||||
|
||||
// If no slug for the locale → skip.
|
||||
/** @phpstan-ignore-next-line */
|
||||
if (!empty($slug) && !$slug) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
187
app/Rules/Video.php
Normal file
187
app/Rules/Video.php
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use App\Enums\VideoProviderEnum;
|
||||
use Illuminate\Contracts\Validation\DataAwareRule;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
use Illuminate\Support\Arr;
|
||||
use Uwil\LaravelCommon\Rules\IsValidVimeoId;
|
||||
use Uwil\LaravelCommon\Rules\IsValidYoutubeId;
|
||||
|
||||
/**
|
||||
* Check the length of a Wysiwyg field, excluding tags and end spaces.
|
||||
*
|
||||
* @package Uwil\LaravelCommon\Rules
|
||||
*/
|
||||
class Video implements ValidationRule, DataAwareRule
|
||||
{
|
||||
protected array $data = [];
|
||||
|
||||
public function setData(array $data): static
|
||||
{
|
||||
$this->data = $data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct method.
|
||||
*
|
||||
* @param integer $min The minimum length of the content (excluding tags). Default is 15.
|
||||
* @param integer|null $max The maximum length of the content (excluding tags).
|
||||
* Default is null (max will be defined by the type of the column in DB).
|
||||
* @return void
|
||||
* @throws \InvalidArgumentException If $max < $min.
|
||||
*/
|
||||
public function __construct() {}
|
||||
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param \Closure $fail
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, \Closure $fail): void
|
||||
{
|
||||
|
||||
$lengthRules = [
|
||||
$attribute . '.alt' => ['min' => 3, 'max' => 255],
|
||||
$attribute . '.caption' => ['min' => 3, 'max' => 255],
|
||||
];
|
||||
|
||||
if (!$value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fields = [
|
||||
$attribute . '.alt',
|
||||
$attribute . '.caption',
|
||||
$attribute . '.isMuted',
|
||||
$attribute . '.hasControls',
|
||||
$attribute . '.canFullScreen',
|
||||
$attribute . '.provider',
|
||||
$attribute . '.videoId',
|
||||
$attribute . '.format',
|
||||
];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
$fieldValue = Arr::get($this->data, $field);
|
||||
if (in_array($field, [
|
||||
$attribute . '.alt',
|
||||
$attribute . '.caption'
|
||||
])) {
|
||||
$lengthRule = $lengthRules[$field] ?? null;
|
||||
|
||||
if ($lengthRule) {
|
||||
$min = $lengthRule['min'] ?? 0;
|
||||
$max = $lengthRule['max'] ?? PHP_INT_MAX;
|
||||
if ($fieldValue === null && $field === $attribute . '.caption') {
|
||||
continue;
|
||||
}
|
||||
if ($fieldValue === null && $field === $attribute . '.title') {
|
||||
continue;
|
||||
}
|
||||
if (strlen($fieldValue) < $min) {
|
||||
$fail(__('validation.min.string', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
'min' => $min
|
||||
]));
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen($fieldValue) > $max) {
|
||||
$fail(__('validation.max.string', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
'max' => $max
|
||||
]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Boolean fields
|
||||
if (in_array($field, [$attribute . '.isMuted', $attribute . '.hasControls', $attribute . '.canFullScreen'])) {
|
||||
$fieldValue = (bool)$fieldValue;
|
||||
if (!is_bool($fieldValue)) {
|
||||
$fail(__('validation.boolean', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if($field === $attribute . '.format') {
|
||||
if($fieldValue != "landscape" && $fieldValue != "portrait") {
|
||||
$fail(__('validation.custom.format', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($field === $attribute . '.provider') {
|
||||
|
||||
$fieldValue = (int) $fieldValue;
|
||||
|
||||
if (!is_int($fieldValue)) {
|
||||
$fail(__('validation.integer', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
|
||||
$enumValues = array_map(fn($case) => $case->value, VideoProviderEnum::cases());
|
||||
if (!in_array($fieldValue, $enumValues)) {
|
||||
$fail(__('validation.in', [
|
||||
'attribute' => self::getTranslatedAttribute($field),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
|
||||
if ($field === $attribute . '.videoId') {
|
||||
$provider = Arr::get($this->data, $attribute . '.provider');
|
||||
|
||||
if ($provider === \App\Enums\VideoProviderEnum::vimeo->value) {
|
||||
$rule = new IsValidVimeoId();
|
||||
} elseif ($provider === \App\Enums\VideoProviderEnum::youtube->value) {
|
||||
$rule = new IsValidYoutubeId();
|
||||
} else {
|
||||
$fail(__('validation.invalid', [
|
||||
'attribute' => self::getTranslatedAttribute($attribute . '.provider'),
|
||||
]));
|
||||
return;
|
||||
}
|
||||
$rule->validate($field, $fieldValue, $fail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct translation for the given attrribute.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @return string
|
||||
*/
|
||||
private static function getTranslatedAttribute(string $attribute): string
|
||||
{
|
||||
$parts = explode('.', $attribute);
|
||||
if (count($parts) > 1) {
|
||||
$parts[0] = 'video_';
|
||||
$attribute = implode('.', $parts);
|
||||
}
|
||||
|
||||
/** @var \Illuminate\Translation\Translator $trans */
|
||||
$trans = trans();
|
||||
if ($trans->has('validation.attributes.' . $attribute)) {
|
||||
return trans('validation.attributes.' . $attribute);
|
||||
}
|
||||
if ($trans->has('validation.custom.' . $attribute)) {
|
||||
return trans('validation.custom.' . $attribute);
|
||||
}
|
||||
return $attribute;
|
||||
}
|
||||
}
|
||||
|
|
@ -28,10 +28,13 @@ trait HasCache
|
|||
static::deleted(function () {
|
||||
self::resetCache();
|
||||
});
|
||||
/** @phpstan-ignore-next-line */
|
||||
if (method_exists(static::class, 'initializeSoftDeletes')) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
static::restored(function () {
|
||||
self::resetCache();
|
||||
});
|
||||
/** @phpstan-ignore-next-line */
|
||||
static::forceDeleted(function () {
|
||||
self::resetCache();
|
||||
});
|
||||
|
|
@ -57,11 +60,12 @@ trait HasCache
|
|||
public static function getModelsCached(): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return Cache::remember(static::getCacheKey(), 86400, function () {
|
||||
/** @var class-string<static> $class */
|
||||
$class = static::class;
|
||||
|
||||
/** @var \Illuminate\Database\Eloquent\Model $class */
|
||||
/** @phpstan-ignore-next-line */
|
||||
return $class::query()
|
||||
->when(
|
||||
/** @phpstan-ignore-next-line */
|
||||
property_exists($class, 'cacheRelations') && is_array($class::$cacheRelations),
|
||||
function ($query) use ($class) {
|
||||
$query->with($class::$cacheRelations);
|
||||
|
|
@ -76,8 +80,8 @@ trait HasCache
|
|||
*
|
||||
* @return string
|
||||
*/
|
||||
private static function getCacheKey(): string
|
||||
public static function getCacheKey(): string
|
||||
{
|
||||
return 'cache_' . str_replace('\\', '_', static::class);
|
||||
return sprintf("cache_%s", str_replace('\\', '_', static::class));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,9 +42,11 @@ trait HasUwilImages
|
|||
*/
|
||||
protected static function bootHasUwilImages(): void
|
||||
{
|
||||
/** @phpstan-ignore-next-line */
|
||||
$hadSoftDelete = method_exists(static::class, 'initializeSoftDeletes');
|
||||
|
||||
if ($hadSoftDelete) {
|
||||
/** @phpstan-ignore-next-line */
|
||||
static::forceDeleted(function (self $model) {
|
||||
self::initLoop($model, self::FORCE_DELETE_ACTION);
|
||||
});
|
||||
|
|
@ -134,9 +136,8 @@ trait HasUwilImages
|
|||
continue;
|
||||
} //end if
|
||||
|
||||
// PAS TESTE.
|
||||
// TODO pas testé
|
||||
if (is_array($originalValue) && is_array($currentValue)) {
|
||||
dd('juste array');
|
||||
static::traverseAndManage(
|
||||
$model,
|
||||
$originalValue,
|
||||
|
|
@ -168,7 +169,7 @@ trait HasUwilImages
|
|||
$forceDeleteImage = $eventType === self::FORCE_DELETE_ACTION;
|
||||
$imagesFields = self::getImageFields($model);
|
||||
$wysiwygFields = self::getWysiwygFields($model);
|
||||
$isTranslatable = method_exists($model, 'getTranslations') ?? false;
|
||||
$isTranslatable = method_exists($model, 'getTranslations');
|
||||
$locale = config('app.locale');
|
||||
$availableLocales = array_keys(config('laravellocalization.supportedLocales', ['fr' => "Frenchouz"]));
|
||||
|
||||
|
|
@ -230,7 +231,6 @@ trait HasUwilImages
|
|||
$path = $isList ? "$fieldPath.$key" : (is_int($key) ? $fieldPath : "$fieldPath.$key");
|
||||
// TODO cette récursivité n'a pas encore été testé.
|
||||
if (is_array($oldValue) || is_array($newValue)) {
|
||||
dd('pas testé');
|
||||
static::traverseAndManage(
|
||||
$model,
|
||||
(array) $oldValue,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ trait HasLaravelFailedValidation
|
|||
* @return void
|
||||
* @throws \Illuminate\Http\Exceptions\HttpResponseException Override ajax code error.
|
||||
*/
|
||||
protected function failedValidation(Validator $validator): \Illuminate\Http\Exceptions\HttpResponseException
|
||||
protected function failedValidation(Validator $validator): void
|
||||
{
|
||||
$response = response()->json([
|
||||
'errors' => $validator->errors(),
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ trait HasInertiaPicture
|
|||
*/
|
||||
protected function mergeInertiaPicture(): void
|
||||
{
|
||||
if (empty($this->convertedFiles) || !is_array($this->convertedFiles)) {
|
||||
if (empty($this->convertedFiles)) {
|
||||
return;
|
||||
}
|
||||
$original = $this->all();
|
||||
|
|
@ -47,7 +47,7 @@ trait HasInertiaPicture
|
|||
* Set picture rules (UploadedFile or string path).
|
||||
*
|
||||
* @param string $rules
|
||||
* @return string
|
||||
* @return array<mixed>
|
||||
*/
|
||||
protected function pictureRules(string $rules): array
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Illuminate\View\Component;
|
|||
|
||||
class NewsNextEventComponent extends Component
|
||||
{
|
||||
/** @var array<\App\Models\News> $nextEvents List of published news. */
|
||||
/** @var \Illuminate\Support\Collection<int, \App\Models\News> $nextEvents List of published news. */
|
||||
public $nextEvents;
|
||||
|
||||
/**
|
||||
|
|
@ -17,12 +17,15 @@ class NewsNextEventComponent extends Component
|
|||
*/
|
||||
public function __construct()
|
||||
{
|
||||
/** @var \App\Models\NewsCategory|null $newsCategory */
|
||||
$newsCategory = NewsCategory::getModelsCached()
|
||||
->filter(fn($category) => $category->getTranslation('slug', 'fr') === "actualites")
|
||||
->first();
|
||||
|
||||
/** @var \App\Models\NewsTag|null $eventTag */
|
||||
$eventTag = NewsTag::getTagsByCategoryInCurrentLocale($newsCategory->slug)
|
||||
->filter(fn($tag) => $tag->getTranslation('slug', 'fr') === "evenement")
|
||||
->filter(
|
||||
fn(\Illuminate\Database\Eloquent\Model $tag) => $tag->getTranslation('slug', 'fr') === "actualites-rh"
|
||||
)
|
||||
->first();
|
||||
|
||||
$eventTagId = $eventTag ? $eventTag->id : null;
|
||||
|
|
@ -30,7 +33,7 @@ class NewsNextEventComponent extends Component
|
|||
$this->nextEvents = News::getPublishedNewsByCategoryInCurrentLocale($newsCategory->slug)
|
||||
->filter(function ($news) use ($eventTagId) {
|
||||
return $news->tags->contains(
|
||||
fn ($tag) => $eventTagId === $tag->id
|
||||
fn($tag) => $eventTagId === $tag->id
|
||||
);
|
||||
})
|
||||
->sortByDesc('published_at')
|
||||
|
|
|
|||
|
|
@ -11,13 +11,13 @@ class OurImplantations extends Component
|
|||
/** @var string $id Identification of the section. */
|
||||
public string $id;
|
||||
|
||||
/** @var $implantations Content section. */
|
||||
/** @var array Content section. */
|
||||
public $implantations;
|
||||
|
||||
/** @var array<\App\Models\Company> $companyModels List of published companies. */
|
||||
/** @var \Illuminate\Database\Eloquent\Collection $companyModels List of published companies. */
|
||||
public $companyModels;
|
||||
|
||||
/** @var array<\App\Models\CompanyActivity> $companyActivityModels List of published company activities. */
|
||||
/** @var \Illuminate\Database\Eloquent\Collection $companyActivityModels List of published company activities. */
|
||||
public $companyActivityModels;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ return Application::configure(basePath: dirname(__DIR__))
|
|||
__DIR__ . '/../routes/web-front.php',
|
||||
__DIR__ . '/../routes/web-back.php',
|
||||
],
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
|
|
|
|||
1
commitlint.config.cjs
Normal file
1
commitlint.config.cjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('@uwil/husky-config/commitlint.config.cjs');
|
||||
|
|
@ -24,10 +24,12 @@
|
|||
"sentry/sentry-laravel": "^4.18",
|
||||
"spatie/laravel-translatable": "^6.11",
|
||||
"tightenco/ziggy": "^2.5",
|
||||
"uwil/admin-secure-laravel": "^5.0",
|
||||
"uwil/admin-secure-laravel": "^5.1",
|
||||
"uwil/k8s-laravel": "^2.0",
|
||||
"uwil/laravel-backend": "^3.0",
|
||||
"uwil/laravel-common": "^4.1"
|
||||
"uwil/laravel-common": "^4.1",
|
||||
"uwil/sitemap-generator": "^6.0",
|
||||
"verschuur/laravel-robotstxt": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
|
|
|
|||
1234
composer.lock
generated
1234
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -14,7 +14,7 @@ return [
|
|||
*
|
||||
*/
|
||||
|
||||
'default' => env('MAIL_MAILER', 'log'),
|
||||
'default' => env('MAIL_MAILER', 'smtp'),
|
||||
|
||||
/**
|
||||
*--------------------------------------------------------------------------
|
||||
|
|
@ -114,4 +114,14 @@ return [
|
|||
'name' => env('MAIL_FROM_NAME', 'Example'),
|
||||
],
|
||||
|
||||
'to' => [
|
||||
'breeder' => explode(',', env('MAIL_LIST_BREEDER')) ?? [],
|
||||
'commitments' => explode(',', env('MAIL_LIST_COMMITMENTS')) ?? [],
|
||||
'export' => explode(',', env('MAIL_LIST_EXPORT')) ?? [],
|
||||
'finance' => explode(',', env('MAIL_LIST_FINANCE')) ?? [],
|
||||
'info' => explode(',', env('MAIL_LIST_INFO')) ?? [],
|
||||
'press' => explode(',', env('MAIL_LIST_PRESS')) ?? [],
|
||||
'restauration' => explode(',', env('MAIL_LIST_RESTAURATION')) ?? [],
|
||||
]
|
||||
|
||||
];
|
||||
|
|
|
|||
29
config/robots-txt.php
Normal file
29
config/robots-txt.php
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
return [
|
||||
'environments' => [
|
||||
'production' => [
|
||||
'paths' => [
|
||||
'*' => [
|
||||
'disallow' => [
|
||||
'/admin',
|
||||
'*.pdf$'
|
||||
],
|
||||
'allow' => []
|
||||
],
|
||||
],
|
||||
'sitemaps' => [
|
||||
'sitemap.xml'
|
||||
]
|
||||
],
|
||||
'staging' => [
|
||||
'paths' => [
|
||||
'*' => [
|
||||
'disallow' => [
|
||||
'/'
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
]
|
||||
];
|
||||
|
|
@ -20,7 +20,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'driver' => env('SCOUT_DRIVER', 'meilisearch'),
|
||||
'driver' => env('SCOUT_DRIVER', null),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -37,5 +37,14 @@ return [
|
|||
|
||||
'google' => [
|
||||
'maps' => env('GOOGLE_MAPS_KEY'),
|
||||
'recaptcha' => [
|
||||
'public' => env('GOOGLE_RECAPTCHA_PUBLIC'),
|
||||
'private' => env('GOOGLE_RECAPTCHA_PRIVATE'),
|
||||
'version' => env('GOOGLE_RECAPTCHA_VERSION', "3") === "3" ? true : false
|
||||
]
|
||||
],
|
||||
|
||||
'matomo' => [
|
||||
'site_id' => env('MATOMO_SITE_ID')
|
||||
],
|
||||
];
|
||||
|
|
|
|||
74
config/sitemap-generator.php
Normal file
74
config/sitemap-generator.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
use Mcamara\LaravelLocalization\Facades\LaravelLocalization;
|
||||
|
||||
return [
|
||||
|
||||
/**
|
||||
* Single route names
|
||||
*
|
||||
* Ex:
|
||||
*
|
||||
* 'singlePages' => [
|
||||
* 'homepage',
|
||||
* ],
|
||||
*/
|
||||
'singlePages' => [
|
||||
'front.search',
|
||||
],
|
||||
|
||||
/**
|
||||
* Model namespaces with filters
|
||||
*
|
||||
* Ex: (modelFilters & relationFilters are not necessaries)
|
||||
* 'cruds' => [
|
||||
* [
|
||||
* 'model' => 'App\Models\Product',
|
||||
* 'modelFilters' => [
|
||||
* ['published', '=', true]
|
||||
* ],
|
||||
* 'relationFilters' => [
|
||||
* 'range' => fn(Builder $q) => $q->where('status', true),
|
||||
* ],
|
||||
* ],
|
||||
* [
|
||||
* 'model' => 'App\Models\Article',
|
||||
* ]
|
||||
* ],
|
||||
*/
|
||||
'cruds' => [
|
||||
[
|
||||
'model' => 'App\Models\News',
|
||||
'filters' => [
|
||||
['status', '=', \App\Enums\News\NewsStatusEnum::published]
|
||||
],
|
||||
],
|
||||
[
|
||||
'model' => 'App\Models\DynamicPage',
|
||||
'filters' => [
|
||||
['status', '=', \App\Enums\DynamicPages\PageStatusEnum::published]
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/**
|
||||
* 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 * * *',
|
||||
|
||||
'supported_locales' => array_keys(config('laravellocalization.supportedLocales', ['fr' => "Fr"]))
|
||||
];
|
||||
|
|
@ -192,7 +192,7 @@ return [
|
|||
"delete" => "delete the color",
|
||||
],
|
||||
"wysiwyg" => [
|
||||
"undo" => "Undo",
|
||||
"undo" => "Undo",
|
||||
"redo" => "Redo",
|
||||
"bold" => "Bold",
|
||||
"italic" => "Italic",
|
||||
|
|
@ -371,4 +371,40 @@ return [
|
|||
"select_value" => "select a value from the list",
|
||||
],
|
||||
"associated_model" => "associated :model|associated :model",
|
||||
"input_video" => [
|
||||
"placeholder" => "Click to add a new video",
|
||||
"title" => "Insert an video",
|
||||
"subtitle" => "Add a new video to your content.",
|
||||
"insert" => "Insert",
|
||||
"cancel" => "Cancel",
|
||||
"tooltip_cancel" => "Cancel adding this video",
|
||||
"edit_title" => "Update a video",
|
||||
"edit_subtitle" => "Edit the attributes associated with the video.",
|
||||
"edit_apply" => "Apply",
|
||||
"edit_cancel" => "Cancel",
|
||||
"position" => "Image position",
|
||||
"format" => "Image format",
|
||||
"video" => "Image",
|
||||
"alt" => "ALT attribute of the video",
|
||||
"caption" => "Caption of the video",
|
||||
"has_caption" => "Presence of a legend",
|
||||
"landscape" => "Landscape",
|
||||
"portrait" => "Portrait",
|
||||
"square" => "Square",
|
||||
"align-left" => "Align left",
|
||||
"align-center" => "Align center",
|
||||
"align-right" => "Align right",
|
||||
"wrap-left" => "Wrap left",
|
||||
"wrap-right" => "Wrap right",
|
||||
"youtube" => "Youtube",
|
||||
"vimeo" => "Vimeo",
|
||||
"tooltip" => [
|
||||
"title" => "video",
|
||||
"delete" => "delete the video",
|
||||
"reset" => "reset to original state",
|
||||
"insert" => "add this video",
|
||||
"edit_cancel" => "Cancel updating the video attributes",
|
||||
"edit_apply" => "Update the video attributes",
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -31,47 +31,7 @@ return [
|
|||
'model' => 'Model',
|
||||
'staticroute' => "Static route",
|
||||
'position' => 'Element position',
|
||||
],
|
||||
'contactFormType' => [
|
||||
'collaborator' => [
|
||||
'label' => 'join us as an employee',
|
||||
'slug' => 'join-us-as-an-employee'
|
||||
],
|
||||
'breeder' => [
|
||||
'label' => 'join us as a farmer',
|
||||
'slug' => 'join-us-as-a-farmer'
|
||||
],
|
||||
'professional' => [
|
||||
'label' => 'a request as a professional'
|
||||
],
|
||||
'restauration' => [
|
||||
'label' => 'foodservice, Industry, Retailer France & Overseas',
|
||||
'slug' => 'foodservice-industry-retailer-distributor',
|
||||
'subtitle' => 'a request as a customer / retailer / distributor'
|
||||
],
|
||||
'export' => [
|
||||
'label' => 'other countries / Export',
|
||||
'slug' => 'export'
|
||||
],
|
||||
'finance' => [
|
||||
'label' => 'finance',
|
||||
'slug' => 'finance-2' #FIXME: revoir le slug
|
||||
],
|
||||
'commitments' => [
|
||||
'label' => 'our commitments',
|
||||
'slug' => 'our-commitments-2' #FIXME: revoir le slug
|
||||
],
|
||||
'product' => [
|
||||
'label' => 'product information',
|
||||
'slug' => 'product-information'
|
||||
],
|
||||
'info' => [
|
||||
'label' => 'other general information',
|
||||
'slug' => 'other-general-information'
|
||||
],
|
||||
'press' => [
|
||||
'label' => 'a press request'
|
||||
],
|
||||
'contactform' => 'Contact form type',
|
||||
],
|
||||
'breedType' => [
|
||||
'chicken' => 'Chickens / turkeys / guinea fowl',
|
||||
|
|
@ -128,4 +88,25 @@ return [
|
|||
'asc' => 'From oldest to newest.',
|
||||
'desc' => 'From the most recent to the oldest.'
|
||||
],
|
||||
'exportAskingType' => [
|
||||
"customer" => "Customer",
|
||||
"provider" => "Supplier",
|
||||
"other" => "Other"
|
||||
],
|
||||
'contactFormType' => [
|
||||
'collaborator' => 'join us as an employee',
|
||||
'breeder' => 'join us as a farmer',
|
||||
'professional' => 'a request as a professional',
|
||||
'restauration' => 'foodservice, Industry, Retailer France & Overseas',
|
||||
'export' => 'other countries / Export',
|
||||
'finance' => 'finance',
|
||||
'commitments' => 'our commitments',
|
||||
'product' => 'product information',
|
||||
'info' => 'other general information',
|
||||
'press' => 'a press request',
|
||||
],
|
||||
"videoDisplayFormat" => [
|
||||
"landscape" => "landscape",
|
||||
"portrait" => "portrait"
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -145,5 +145,11 @@ return [
|
|||
'subtitle' => 'jobs, internships and apprenticeships',
|
||||
'link_text' => 'access our recruitment portal'
|
||||
],
|
||||
'discover_our_jobs' => 'discover our broad range of jobs'
|
||||
'discover_our_jobs' => 'discover our broad range of jobs',
|
||||
'newsletter' => [
|
||||
'title' => 'Subscribe to our newsletter !',
|
||||
'placeholder' => 'Enter your email',
|
||||
'validate' => 'Confirm',
|
||||
'policy_message' => 'For any additional information regarding your personal data and your rights, please consult our '
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
"classes" => [
|
||||
"user" => "user",
|
||||
"user" => "user|users",
|
||||
"dynamic-page" => "dynamic page|dynamic pages",
|
||||
"page-config" => "page configuration|page configurations",
|
||||
"activity-log" => "activity log",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'contact' => 'contact-us/{slug}',
|
||||
'contact_index' => 'contact-us',
|
||||
'news' => 'news/{news}',
|
||||
'search' => 'search',
|
||||
'front' => [
|
||||
'news' => 'news/{news}',
|
||||
'search' => 'search'
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -271,6 +271,15 @@ return [
|
|||
"value" => "value",
|
||||
"path" => "path",
|
||||
"issuu_path" => "link of the Issuu preview",
|
||||
"is_issuu" => "is an Issuu preview ?"
|
||||
"is_issuu" => "is an Issuu preview ?",
|
||||
"video" => "vidéo",
|
||||
"video_" => [
|
||||
"isMuted" => "muted",
|
||||
"hasControls" => "controls enabled",
|
||||
"canFullScreen" => "full screen allowed",
|
||||
"alt" => "alternative text",
|
||||
"title" => "title",
|
||||
"caption" => "caption"
|
||||
]
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -372,4 +372,45 @@ return [
|
|||
"select_value" => "sélectionnez une valeur dans la liste",
|
||||
],
|
||||
"associated_model" => ":model associés|:model associées",
|
||||
"input_video" => [
|
||||
"placeholder" => "Cliquer pour ajouter une nouvelle vidéo",
|
||||
"title" => "Insérer une vidéo",
|
||||
"subtitle" => "Ajouter une nouvelle vidéo à votre contenu.",
|
||||
"insert" => "Insérer",
|
||||
"cancel" => "Annuler",
|
||||
"edit_title" => "Mise à jour d'une vidéo",
|
||||
"edit_subtitle" => "Modifier les attributs associés à la vidéo.",
|
||||
"edit_apply" => "Appliquer",
|
||||
"edit_cancel" => "Annuler",
|
||||
"position" => "Position de la vidéo",
|
||||
"format" => "Format de la vidéo",
|
||||
"image" => "Vidéo",
|
||||
"alt" => "Texte alternatif de la vidéo",
|
||||
"caption" => "Légende de la vidéo",
|
||||
"has_caption" => "Présence d'une légende",
|
||||
"landscape" => "Paysage",
|
||||
"portrait" => "Portrait",
|
||||
"align-left" => "Aligner à gauche",
|
||||
"align-center" => "Aligner au centre",
|
||||
"align-right" => "Aligner à droite",
|
||||
"wrap-left" => "Flottant à gauche",
|
||||
"wrap-right" => "Flottant à droite",
|
||||
"youtube" => "Youtube",
|
||||
"vimeo" => "Viméo",
|
||||
"is_muted" => "muter la vidéo ?",
|
||||
"has_controls" => "permettre de controler la vidéo ?",
|
||||
"can_fullScreen" => "autoriser le plein écran ?",
|
||||
"provider" => "hébergeur de la vidéo",
|
||||
'invalid_youtube_id' => "vidéo YOUTUBE invalide",
|
||||
"invalid_vimeo_id" => "vidéo VIMEO invalide",
|
||||
"tooltip" => [
|
||||
"title" => "vidéo",
|
||||
"delete" => "supprimer la vidéo",
|
||||
"reset" => "remettre à l'état d'origine",
|
||||
"insert" => "Ajouter cette vidéo",
|
||||
"cancel" => "Annuler l'ajout de cette vidéo",
|
||||
"edit_apply" => "Mettre à jour les attributs de la vidéo",
|
||||
"edit_cancel" => "Annuler la mise à jour des attributs de la vidéo",
|
||||
]
|
||||
]
|
||||
];
|
||||
|
|
|
|||
|
|
@ -31,47 +31,7 @@ return [
|
|||
'model' => 'Modèle',
|
||||
'staticroute' => "Route statique",
|
||||
'position' => 'Positionnement de l\'élement',
|
||||
],
|
||||
'contactFormType' => [
|
||||
'collaborator' => [
|
||||
'label' => 'nous rejoindre en tant que collaborateur',
|
||||
'slug' => 'nous-rejoindre-en-tant-que-collaborateur'
|
||||
],
|
||||
'breeder' => [
|
||||
'label' => 'nous rejoindre en tant qu’éleveur',
|
||||
'slug' => 'nous-rejoindre-en-tant-queleveur'
|
||||
],
|
||||
'professional' => [
|
||||
'label' => 'une demande en tant que professionnel'
|
||||
],
|
||||
'restauration' => [
|
||||
'label' => 'restaurations, Distributeurs et Détaillants France',
|
||||
'subtitle' => 'une demande en tant que client / détaillant / distributeur',
|
||||
'slug' => 'une-demande-en-tant-que-client-detaillant-distributeur'
|
||||
],
|
||||
'export' => [
|
||||
'label' => 'l’export',
|
||||
'slug' => 'lexport'
|
||||
],
|
||||
'finance' => [
|
||||
'label' => 'la finance',
|
||||
'slug' => 'la-finance'
|
||||
],
|
||||
'commitments' => [
|
||||
'label' => 'nos engagements',
|
||||
'slug' => 'nos-engagements'
|
||||
],
|
||||
'product' => [
|
||||
'label' => 'une information produit',
|
||||
'slug' => 'une-information-produit'
|
||||
],
|
||||
'info' => [
|
||||
'label' => 'une autre information générale',
|
||||
'slug' => 'une-autre-information-generale'
|
||||
],
|
||||
'press' => [
|
||||
'label' => 'une demande presse'
|
||||
],
|
||||
'contactform' => 'Type de formulaire de contact',
|
||||
],
|
||||
'breedType' => [
|
||||
'chicken' => 'Poulets/dindes/pintades',
|
||||
|
|
@ -128,4 +88,25 @@ return [
|
|||
'asc' => 'du plus ancien au plus récent.',
|
||||
'desc' => 'du plus récent au plus ancien.'
|
||||
],
|
||||
'exportAskingType' => [
|
||||
"customer" => "Client",
|
||||
"provider" => "Fournisseur",
|
||||
"other" => "Autres"
|
||||
],
|
||||
'contactFormType' => [
|
||||
'collaborator' => 'nous rejoindre en tant que collaborateur',
|
||||
'breeder' => 'nous rejoindre en tant qu’éleveur',
|
||||
'professional' => 'une demande en tant que professionnel',
|
||||
'restauration' => 'restaurations, Distributeurs et Détaillants France',
|
||||
'export' => 'l’export',
|
||||
'finance' => 'la finance',
|
||||
'commitments' => 'nos engagements',
|
||||
'product' => 'une information produit',
|
||||
'info' => 'une autre information générale',
|
||||
'press' => 'une demande presse',
|
||||
],
|
||||
"videoDisplayFormat" => [
|
||||
"landscape" => "paysage",
|
||||
"portrait" => "portrait"
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -146,5 +146,11 @@ return [
|
|||
'subtitle' => 'd\'emploi, de stage et d\'alternance',
|
||||
'link_text' => 'accédez à notre portail de recrutement'
|
||||
],
|
||||
'discover_our_jobs' => 'découvrez nos métiers'
|
||||
'discover_our_jobs' => 'découvrez nos métiers',
|
||||
'newsletter' => [
|
||||
'title' => 'Inscrivez-vous à notre newsletter !',
|
||||
'placeholder' => 'Renseignez votre email',
|
||||
'validate' => 'Valider',
|
||||
'policy_message' => 'Pour toute information complémentaire concernant vos données personnelles et vos droits, merci de consulter notre'
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
return [
|
||||
"classes" => [
|
||||
"user" => "utilisateur",
|
||||
"user" => "utilisateur|utilisateurs",
|
||||
"dynamic-page" => "page dynamique|pages dynamiques",
|
||||
"page-config" => "configuration de page|configurations de page",
|
||||
"activity-log" => "journal d'activité",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'contact' => 'nous-contacter/{slug}',
|
||||
'contact_index' => 'nous-contacter',
|
||||
'news' => 'actualites/{news}',
|
||||
'search' => 'recherche',
|
||||
'front' => [
|
||||
'news' => 'actualites/{news}',
|
||||
'search' => 'recherche'
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -270,6 +270,17 @@ return [
|
|||
"value" => "valeur",
|
||||
"path" => "chemin d'accès",
|
||||
"issuu_path" => "lien du fichier Issuu",
|
||||
"is_issuu" => "est-ce un fichier Issuu ?"
|
||||
"is_issuu" => "est-ce un fichier Issuu ?",
|
||||
"video" => "vidéo",
|
||||
"video_" => [
|
||||
"isMuted" => "désactiver le son",
|
||||
"hasControls" => "activer les contrôles",
|
||||
"canFullScreen" => "autoriser le plein écran",
|
||||
"alt" => "texte alernatif",
|
||||
"title" => "titre",
|
||||
"caption" => "légende",
|
||||
"provider" => "hébergeur",
|
||||
"videoId" => "ID de la vidéo"
|
||||
]
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ data:
|
|||
APP_ENV: "${APP_APP_ENV}"
|
||||
APP_KEY: "${APP_APP_KEY}"
|
||||
APP_DEBUG: "${APP_APP_DEBUG}"
|
||||
APP_URL: "https://${APP_DOMAIN}"
|
||||
APP_URL: "https://${REDIRECT}${APP_DOMAIN}"
|
||||
LOG_CHANNEL: "k8s"
|
||||
CACHE_STORE: "${APP_CACHE_STORE}"
|
||||
DB_HOST: "${APP_DB_HOST}"
|
||||
|
|
@ -23,6 +23,13 @@ data:
|
|||
MAIL_ENCRYPTION: "${APP_MAIL_ENCRYPTION}"
|
||||
MAIL_FROM_ADDRESS: "${APP_MAIL_FROM_ADDRESS}"
|
||||
MAIL_FROM_NAME: "${APP_APP_NAME}"
|
||||
MAIL_LIST_BREEDER: "${APP_MAIL_LIST_BREEDER}"
|
||||
MAIL_LIST_COMMITMENTS: "${APP_MAIL_LIST_COMMITMENTS}"
|
||||
MAIL_LIST_EXPORT: "${APP_MAIL_LIST_EXPORT}"
|
||||
MAIL_LIST_FINANCE: "${APP_MAIL_LIST_FINANCE}"
|
||||
MAIL_LIST_INFO: "${APP_MAIL_LIST_INFO}"
|
||||
MAIL_LIST_PRESS: "${APP_MAIL_LIST_PRESS}"
|
||||
MAIL_LIST_RESTAURATION: "${APP_MAIL_LIST_RESTAURATION}"
|
||||
SENTRY_LARAVEL_DSN: "${APP_SENTRY_LARAVEL_DSN}"
|
||||
TELESCOPE_ENABLED: "${APP_TELESCOPE_ENABLED}"
|
||||
CRM_ENDPOINT: "${APP_CRM_ENDPOINT}"
|
||||
|
|
@ -56,6 +63,18 @@ spec:
|
|||
requests:
|
||||
storage: $APP_PVC_SIZE
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: ${CI_PROJECT_PATH_SLUG}-wp-uploads-pvc-storage
|
||||
spec:
|
||||
storageClassName: "csi-cinder-classic"
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: $APP_WP_UPLOADS_PVC_SIZE
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
|
|
@ -86,6 +105,8 @@ spec:
|
|||
volumeMounts:
|
||||
- name: storage
|
||||
mountPath: /app/srcs/storage
|
||||
- name: wp-uploads-storage
|
||||
mountPath: /app/srcs/public/app/uploads
|
||||
- name: ${CI_PROJECT_PATH_SLUG}-queue-worker
|
||||
image: $IMAGE_TAG
|
||||
lifecycle:
|
||||
|
|
@ -120,6 +141,9 @@ spec:
|
|||
- name: storage
|
||||
persistentVolumeClaim:
|
||||
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
|
||||
- name: wp-uploads-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: ${CI_PROJECT_PATH_SLUG}-wp-uploads-pvc-storage
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
|
|
@ -141,14 +165,29 @@ spec:
|
|||
- web
|
||||
- websecure
|
||||
routes:
|
||||
- match: Host(`${APP_DOMAIN}`)
|
||||
- match: Host(`${APP_DOMAIN}`) || Host(`www.${APP_DOMAIN}`)
|
||||
kind: Rule
|
||||
services:
|
||||
- name: ${CI_PROJECT_PATH_SLUG}-service
|
||||
port: 80
|
||||
middlewares:
|
||||
- name: redirect-no-www-to-www
|
||||
- name: redirect-ldc-rgpd-to-rgpd
|
||||
|
||||
tls:
|
||||
secretName: ${CRT_TLS_NAME}
|
||||
---
|
||||
# Redirect with domain replacement
|
||||
apiVersion: traefik.io/v1alpha1
|
||||
kind: Middleware
|
||||
metadata:
|
||||
name: redirect-ldc-rgpd-to-rgpd
|
||||
spec:
|
||||
redirectRegex:
|
||||
regex: ^https://${REDIRECT}${APP_DOMAIN}/rgpd/(professionnels|salaries|consommateurs)(/(en|de))?
|
||||
replacement: https://rgpd.ldc.fr/fr/${1}
|
||||
permanent: true
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
|
|
|
|||
645
package-lock.json
generated
645
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -33,7 +33,6 @@
|
|||
"@typescript-eslint/parser": "^8.39.0",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"baseline-browser-mapping": "^2.9.19",
|
||||
"concurrently": "^9.2.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
|
|
@ -104,6 +103,7 @@
|
|||
"@types/pluralize": "^0.0.33",
|
||||
"@types/react": "^19.1.9",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@uwil/husky-config": "^1.0.1",
|
||||
"axios": "^1.13.2",
|
||||
"chart.js": "^4.5.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ parameters:
|
|||
paths:
|
||||
- app/
|
||||
|
||||
level: 5
|
||||
level: 7
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ metadata:
|
|||
spec:
|
||||
dnsNames:
|
||||
- $APP_DOMAIN
|
||||
- www.${APP_DOMAIN}
|
||||
secretName: $APP_DOMAIN
|
||||
issuerRef:
|
||||
name: $APP_CERT_ISSUER
|
||||
|
|
|
|||
174
public/.htaccess
174
public/.htaccess
|
|
@ -18,8 +18,182 @@
|
|||
RewriteCond %{REQUEST_URI} (.+)/$
|
||||
RewriteRule ^ %1 [L,R=301]
|
||||
|
||||
# Redirect /index.php/anything to /anything
|
||||
RewriteCond %{THE_REQUEST} /index\.php/([^\s?]*) [NC]
|
||||
RewriteRule ^index\.php/(.*)$ /$1 [L,R=301,NC]
|
||||
|
||||
# Send Requests To Front Controller...
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteRule ^ index.php [L]
|
||||
|
||||
# Redirection from old website
|
||||
Redirect 301 "/category/news" "/actualites"
|
||||
Redirect 301 "/100%des-elevages-nature-deleveurs-dici-a-2025" "/actualites/100-des-elevages-nature-deleveurs-dici-a-2025"
|
||||
Redirect 301 "/acquisition-de-la-societe-asia-general-food" "/actualites/acquisition-de-la-societe-asia-general-food"
|
||||
Redirect 301 "/big-bang-de-lemploi-2023-rencontrons-nous-dans-des-conditions-exceptionnelles" "/actualites/big-bang-de-lemploi-2023-rencontrons-nous-dans-des-conditions-exceptionnelles"
|
||||
Redirect 301 "/bonne-annee-2024-a-tous" "/actualites/bonne-annee-2024-a-tous"
|
||||
Redirect 301 "/decouvrez-notre-nouvelle-plaquette-rse" "/actualites/decouvrez-notre-nouvelle-plaquette-rse"
|
||||
Redirect 301 "/decouvrez-notre-rapport-annuel-2020-2021" "/actualites/decouvrez-notre-rapport-annuel-2020-2021"
|
||||
Redirect 301 "/decouvrez-notre-rapport-annuel-2021-2022" "/actualites/decouvrez-notre-rapport-annuel-2021-2022"
|
||||
Redirect 301 "/echanges-entre-les-etudiants-de-lessca-et-le-raf-du-pole-volaille-ldc" "/actualites/echanges-entre-les-etudiants-de-lessca-et-le-raf-du-pole-volaille-ldc"
|
||||
Redirect 301 "/echanges-entre-les-etudiants-unilasalle-et-des-collaborateurs-ldc" "/actualites/echanges-entre-les-etudiants-unilasalle-et-des-collaborateurs-ldc"
|
||||
Redirect 301 "/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-la-societe-indykpol-sa-2" "/actualites/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-la-societe-indykpol-sa"
|
||||
Redirect 301 "/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-la-societe-indykpol-sa-3" "/actualites/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-la-societe-indykpol-sa"
|
||||
Redirect 301 "/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-lusine-konspol-de-nowy-sacz-en-pologne-2" "/actualites/entree-en-negociation-exclusive-en-vue-de-lacquisition-de-lusine-konspol-de-nowy-sacz-en-pologne"
|
||||
Redirect 301 "/entree-en-negociation-exclusive-en-vue-de-lacquisition-potentielle-de-100-du-capital-du-groupe-pierre-martinet-2" "/actualites/entree-en-negociation-exclusive-en-vue-de-lacquisition-potentielle-de-100-du-capital-du-groupe-pierre-martinet"
|
||||
Redirect 301 "/entree-en-negociation-exclusive-en-vue-de-lacquisition-potentielle-de-100-du-capital-du-groupe-pierre-martinet-3" "/actualites/entree-en-negociation-exclusive-en-vue-de-lacquisition-potentielle-de-100-du-capital-du-groupe-pierre-martinet"
|
||||
Redirect 301 "/entree-en-partenariat-avec-la-famille-buchanan-au-royaume-uni-2" "/actualites/entree-en-partenariat-avec-la-famille-buchanan-au-royaume-uni"
|
||||
Redirect 301 "/feu-vert-de-lautorite-de-la-concurrence-au-rachat-du-groupe-ronsard-2" "/actualites/feu-vert-de-lautorite-de-la-concurrence-au-rachat-du-groupe-ronsard"
|
||||
Redirect 301 "/feu-vert-de-lautorite-de-la-concurrence-au-rachat-du-groupe-ronsard-3" "/actualites/feu-vert-de-lautorite-de-la-concurrence-au-rachat-du-groupe-ronsard"
|
||||
Redirect 301 "/interview-philippe-gelin-sur-la-chaine-youtube-meilleurtaux-placement" "/actualites/interview-philippe-gelin-sur-la-chaine-youtube-meilleurtaux-placement"
|
||||
Redirect 301 "/journee-mondiale-de-leau" "/actualites/journee-mondiale-de-leau"
|
||||
Redirect 301 "/journee-mondiale-du-recyclage-%e2%99%bb" "/actualites/journee-mondiale-du-recyclage"
|
||||
Redirect 301 "/journee-portes-ouvertes-ldc-sable-le-1er-avril-2023" "/actualites/journee-portes-ouvertes-ldc-sable-le-1er-avril-2023"
|
||||
Redirect 301 "/joyeux-nouvel-an-chinois" "/actualites/joyeux-nouvel-an-chinois"
|
||||
Redirect 301 "/lancement-campagne-alternance-2024" "/actualites/lancement-campagne-alternance-2024"
|
||||
Redirect 301 "/ldc-a-la-reconquete-du-marche-national" "/actualites/ldc-a-la-reconquete-du-marche-national"
|
||||
Redirect 301 "/ldc-partage-sa-politique-alimentation-des-volailles" "/actualites/ldc-partage-sa-politique-dalimentation-durable-des-volailles"
|
||||
Redirect 301 "/ldc-participe-a-la-semaine-nationale-de-lemploi-agroalimentaire-100-digital" "/actualites/ldc-participe-a-la-semaine-nationale-de-lemploi-agroalimentaire-100-digital"
|
||||
Redirect 301 "/ldc-participe-au-campus-des-metiers-et-des-qualifications" "/actualites/ldc-participe-au-campus-des-metiers-et-des-qualifications"
|
||||
Redirect 301 "/ldc-sable-lance-sa-premiere-formation-qualifiante-post-bac" "/actualites/ldc-sable-lance-sa-premiere-formation-qualifiante-post-bac"
|
||||
Redirect 301 "/ldc-sable-le-gaulois-signe-un-partenariat-avec-coolplanet-pour-optimiser-ses-consommations-denergie-et-accelerer-sa-feuille-de-route-en-matiere-de-decarbonation" "/actualites/ldc-sable-le-gaulois-signe-un-partenariat-avec-coolplanet-pour-optimiser-ses-consommations-denergie-et-accelerer-sa-feuille-de-route-en-matiere-de-decarbonation"
|
||||
Redirect 301 "/le-cavol-a-la-reconquete-de-la-rhd" "/actualites/le-cavol-a-la-reconquete-de-la-rhd"
|
||||
Redirect 301 "/le-gaulois-maitre-coq-loue-il-est-le-champion-du-poulet-toutes-categories" "/actualites/le-gaulois-maitre-coq-loue-il-est-le-champion-du-poulet-toutes-categories"
|
||||
Redirect 301 "/le-gaulois-professionnel-lance-son-site-internet" "/actualites/le-gaulois-professionnel-lance-son-site-internet"
|
||||
Redirect 301 "/le-gaulois-signe-un-partenariat-avec-programme-malin" "/actualites/le-gaulois-signe-un-partenariat-avec-programme-malin"
|
||||
Redirect 301 "/le-groupe-ldc-a-participe-au-forum-uni-la-salle-de-beauvais-le-7-octobre-2021" "/actualites/le-groupe-ldc-a-participe-au-forum-uni-la-salle-de-beauvais-le-7-octobre-2021"
|
||||
Redirect 301 "/le-groupe-ldc-cree-un-partenariat-avec-citeo-pour-le-developpement-dune-filiere-de-recyclage-du-polystyrene-en-france" "/actualites/le-groupe-ldc-cree-un-partenariat-avec-citeo-pour-le-developpement-dune-filiere-de-recyclage-du-polystyrene-en-france"
|
||||
Redirect 301 "/le-groupe-ldc-officialise-sa-strategie-climat-biodiversite-2030-%f0%9f%8c%8d" "/actualites/le-groupe-ldc-officialise-sa-strategie-climat-biodiversite-2030"
|
||||
Redirect 301 "/le-groupe-ldc-participe-a-la-semaine-de-lagro-grand-ouest-du-12-au-20-avril" "/actualites/le-groupe-ldc-participe-a-la-semaine-de-lagro-grand-ouest-du-12-au-20-avril"
|
||||
Redirect 301 "/le-groupe-ldc-participe-a-la-semaine-nationale-de-lemploi-agroalimentaire" "/actualites/le-groupe-ldc-participe-a-la-semaine-nationale-de-lemploi-agroalimentaire"
|
||||
Redirect 301 "/anuga-2023" "/actualites/le-groupe-ldc-present-a-lanuga-2023"
|
||||
Redirect 301 "/le-groupe-ldc-present-au-forum-oniris-le-18-novembre-2021" "/actualites/le-groupe-ldc-present-au-forum-oniris-le-18-novembre-2021"
|
||||
Redirect 301 "/le-groupe-ldc-present-au-jobfest-du-mans-le-30-mars-2023" "/actualites/le-groupe-ldc-present-au-jobfest-du-mans-le-30-mars-2023"
|
||||
Redirect 301 "/le-groupe-ldc-present-aux-forums-ecoles-de-lesa-angers-et-agrosup-dijon" "/actualites/le-groupe-ldc-present-aux-forums-ecoles-de-lesa-angers-et-agrosup-dijon"
|
||||
Redirect 301 "/le-groupe-ldc-present-aux-forums-ecoles-de-linstitut-agro-dijon-esa-angers-oniris-nantes-esaip-angers" "/actualites/le-groupe-ldc-present-aux-forums-ecoles-de-linstitut-agro-dijon-esa-angers-oniris-nantes-esaip-angers"
|
||||
Redirect 301 "/le-groupe-ldc-present-aux-forums-polytech-angers-et-agro-campus-rennes-le-18-octobre-2022" "/actualites/le-groupe-ldc-present-aux-forums-polytech-angers-et-agro-campus-rennes-le-18-octobre-2022"
|
||||
Redirect 301 "/le-groupe-ldc-reagit-face-aux-nouvelles-attaques-de-l214" "/actualites/le-groupe-ldc-reagit-face-aux-nouvelles-attaques-de-l214"
|
||||
Redirect 301 "/le-groupe-ldc-renforce-ses-engagements-et-son-objectif-soja-zero-deforestation" "/actualites/le-groupe-ldc-reagit-face-aux-nouvelles-attaques-de-l214"
|
||||
Redirect 301 "/le-groupe-ldc-renforce-ses-engagements-et-son-objectif-soja-zero-deforestation-2" "/actualites/le-groupe-ldc-renforce-ses-engagements-et-son-objectif-soja-zero-deforestation"
|
||||
Redirect 301 "/les-industries-de-la-transformation-des-volailles-offrent-une-diversite-de-metiers-et-de-nombreuses-opportunites-demploi" "/actualites/les-industries-de-la-transformation-des-volailles-offrent-une-diversite-de-metiers-et-de-nombreuses-opportunites-demploi"
|
||||
Redirect 301 "/les-talents-de-la-sarthe-2019" "/actualites/les-talents-de-la-sarthe-2019"
|
||||
Redirect 301 "/les-talents-de-la-sarthe-2019-2" "/actualites/les-talents-de-la-sarthe-2019"
|
||||
Redirect 301 "/maitre-coq-devient-partenaire-officiel-dangers-sco-handball" "/actualites/maitre-coq-devient-partenaire-officiel-dangers-sco-handball"
|
||||
Redirect 301 "/marie-surgeles-organise-ses-job-dating-en-entreprise" "/actualites/marie-surgeles-organise-ses-job-dating-en-entreprise"
|
||||
Redirect 301 "/nos-equipes-volena-et-sbv-etaient-presentes-au-food-talent-du-cfia-expo-rennes-ce-14-mars" "/actualites/nos-equipes-volena-et-sbv-etaient-presentes-au-food-talent-du-cfia-expo-rennes-ce-14-mars"
|
||||
Redirect 301 "/note-dinformation-deces-de-monsieur-chancereul-gerard" "/actualites/note-dinformation-deces-de-monsieur-chancereul-gerard"
|
||||
Redirect 301 "/partenariat-ecole-les-etudiants-uni-la-salle-en-visite-chez-ldc-groupe" "/actualites/partenariat-ecole-les-etudiants-unilasalle-en-visite-chez-ldc-groupe"
|
||||
Redirect 301 "/partenariat-ecole-visite-de-lesa-sur-lunite-dinde-ldc-sable" "/actualites/partenariat-ecole-visite-de-lesa-sur-lunite-dinde-ldc-sable"
|
||||
Redirect 301 "/publication-de-notre-rapport-annuel-2022-2023" "/actualites/publication-de-notre-rapport-annuel-2022-2023"
|
||||
Redirect 301 "/recrutement-en-alternance-plus-de-100-opportunites-dans-tous-les-metiers" "/actualites/recrutement-en-alternance-plus-de-100-opportunites-dans-tous-les-metiers"
|
||||
Redirect 301 "/reprise-de-kiplama-en-belgique" "/actualites/reprise-de-kiplama-en-belgique"
|
||||
Redirect 301 "/reprise-de-luche-tradition-volailles-a-luche-pringe-sarthe" "/actualites/reprise-de-luche-tradition-volailles-a-luche-pringe-sarthe"
|
||||
Redirect 301 "/reprise-de-marnevall-en-hongrie" "/actualites/reprise-de-marnevall-en-hongrie"
|
||||
Redirect 301 "/resultats-annuels-2021-2022-2" "/actualites/resultats-annuels-2021-2022"
|
||||
Redirect 301 "/resultats-semestriels-2020-2021-3" "/actualites/resultats-semestriels-2020-2021"
|
||||
Redirect 301 "/resultats-semestriels-2023-2024-2" "/actualites/resultats-semestriels-20232024"
|
||||
Redirect 301 "/retour-en-video-sur-le-space-rennes-2024" "/actualites/retour-en-video-sur-le-space-rennes-2024"
|
||||
Redirect 301 "/retour-en-video-sur-notre-evenement-interne-challenge-remy-lambert" "/actualites/retour-en-video-sur-notre-evenement-interne-challenge-remy-lambert"
|
||||
Redirect 301 "/retrospective-2024-retrouvez-les-entreprises-qui-ont-rejoint-le-groupe-en-2024" "/actualites/retrospective-2024-retrouvez-les-entreprises-qui-ont-rejoint-le-groupe-en-2024"
|
||||
Redirect 301 "/retrouvez-nous-aux-forums-ecoles-en-fevrier-2025" "/actualites/retrouvez-nous-aux-forums-ecoles-en-fevrier-2025"
|
||||
Redirect 301 "/retrouvez-nous-aux-forums-ecoles-en-mars-2025" "/actualites/retrouvez-nous-aux-forums-ecoles-en-mars-2025"
|
||||
Redirect 301 "/signature-de-conventions-avec-les-banques-alimentaires" "/actualites/signature-de-conventions-avec-les-banques-alimentaires"
|
||||
Redirect 301 "/sirha-lyon-2025-le-groupe-ldc-et-ses-marques-restauration-seront-presents-du-23-au-27-janvier-2025" "/actualites/sirha-lyon-2025-le-groupe-ldc-et-ses-marques-restauration-seront-presents-du-23-au-27-janvier-2025"
|
||||
Redirect 301 "/sponsoring-sportif-maitre-coq-et-yannick-bestaven" "/actualites/sponsoring-sportif-maitre-coq-et-yannick-bestaven"
|
||||
Redirect 301 "/synthese-rse-du-groupe-ldc-2020" "/actualites/synthese-rse-du-groupe-ldc-2020"
|
||||
Redirect 301 "/teasing-retrouvez-le-pole-amont-au-space-du-17-au-19-septembre-2024" "/actualites/teasing-retrouvez-le-pole-amont-au-space-du-17-au-19-septembre-2024"
|
||||
Redirect 301 "/top-depart-pour-le-vendee-globe-avec-maitre-coq" "/actualites/top-depart-pour-le-vendee-globe-avec-maitre-coq"
|
||||
Redirect 301 "/tous-derriere-yannick-et-julien-pour-la-transat-jacques-vabre" "/actualites/tous-derriere-yannick-et-julien-pour-la-transat-jacques-vabre"
|
||||
Redirect 301 "/une-annee-de-plus-ou-nos-produits-ont-ete-recompenses-par-le-concours-general-agricole-2024" "/actualites/une-annee-de-plus-ou-nos-produits-ont-ete-recompenses-par-le-concours-general-agricole-2024"
|
||||
Redirect 301 "/venez-nous-rencontrer-au-salon-work-in-agro-2023" "/actualites/venez-nous-rencontrer-au-salon-work-in-agro-2023"
|
||||
Redirect 301 "/victoire-de-yannick-bestaven-et-son-maitre-coq-iv-de-la-9eme-edition-du-vendee-globe" "/actualites/victoire-de-yannick-bestaven-et-son-maitre-coq-iv-de-la-9eme-edition-du-vendee-globe"
|
||||
Redirect 301 "/victoire-de-yannick-bestaven-et-son-maitre-coq-iv-de-la-9eme-edition-du-vendee-globe-2" "/actualites/victoire-de-yannick-bestaven-et-son-maitre-coq-iv-de-la-9eme-edition-du-vendee-globe"
|
||||
Redirect 301 "/ldc-sable-lance-sa-premiere-formation-qualifiante-post-bac-pour-devenir-conducteur-de-ligne" "/actualites/video-ldc-sable-lance-sa-premiere-formation-qualifiante-post-bac-pour-devenir-conducteur-de-ligne"
|
||||
Redirect 301 "/volena-volaillers-engages-pour-lavenir-le-nouveau-nom-du-pole-normandie" "/actualites/volena-volaillers-engages-pour-lavenir-le-nouveau-nom-du-pole-normandie"
|
||||
Redirect 301 "/webinaire-entre-les-etudiants-doniris-et-des-collaborateurs-ldc" "/actualites/webinaire-entre-les-etudiants-doniris-et-des-collaborateurs-ldc"
|
||||
Redirect 301 "/en/100-of-nature-deleveurs-farms-by-2025" "/en/news/100-of-nature-deleveurs-farms-by-2025"
|
||||
Redirect 301 "/en/acquisition-of-asia-general-food" "/en/news/acquisition-of-asia-general-food"
|
||||
Redirect 301 "/en/cavol-to-re-conquer-hrd" "/en/news/cavol-to-re-conquer-hrd"
|
||||
Redirect 301 "/en/entry-into-partnership-with-the-buchanan-family-2" "/en/news/entry-into-partnership-with-the-buchanan-family"
|
||||
Redirect 301 "/en/ldc-participates-in-the-trades-and-skills-campus" "/en/news/ldc-participates-in-the-trades-and-skills-campus"
|
||||
Redirect 301 "/en/ldc-to-regain-the-national-market" "/en/news/ldc-to-regain-the-national-market"
|
||||
Redirect 301 "/en/le-gaulois-maitre-coq-loue-the-chicken-champion-across-the-categories" "/en/news/le-gaulois-maitre-coq-loue-the-chicken-champion-across-the-categories"
|
||||
Redirect 301 "/en/le-gaulois-signs-a-partnership-with-programme-malin" "/en/news/le-gaulois-signs-a-partnership-with-programme-malin"
|
||||
Redirect 301 "/en/maitre-coq-becomes-an-official-partner-of-angers-sco-handball" "/en/news/maitre-coq-becomes-an-official-partner-of-angers-sco-handball"
|
||||
Redirect 301 "/en/signature-of-agreements-with-food-banks" "/en/news/signature-of-agreements-with-food-banks"
|
||||
Redirect 301 "/en/take-over-of-kiplama-in-belgium" "/en/news/take-over-of-kiplama-in-belgium"
|
||||
Redirect 301 "/en/takeover-of-luche-tradition-volailles-in-luche-pringe-sarthe-2" "/en/news/takeover-of-luche-tradition-volailles-in-luche-pringe-sarthe"
|
||||
Redirect 301 "/en/takeover-of-marnevall-in-hungary" "/en/news/takeover-of-marnevall-in-hungary"
|
||||
Redirect 301 "/en/les-talents-de-la-sarthe-2019-2" "/en/news/talents-in-the-sarthe-2019"
|
||||
Redirect 301 "/en/talents-in-the-sarthe-2019" "/en/news/talents-in-the-sarthe-2019"
|
||||
Redirect 301 "/en/the-ldc-group-creates-a-partnership-with-citeo-for-the-development-of-a-polystyrene-recycling-industry-in-france" "/en/news/the-ldc-group-creates-a-partnership-with-citeo-for-the-development-of-a-polystyrene-recycling-industry-in-france"
|
||||
Redirect 301 "/en/top-start-for-the-vendee-globe-with-maitre-coq" "/en/news/top-start-for-the-vendee-globe-with-maitre-coq"
|
||||
Redirect 301 "/search" "/recherche"
|
||||
Redirect 301 "/nous-contacter/nos-engagements" "/nous-contacter/engagements"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
|
||||
# Images
|
||||
ExpiresByType image/jpg "access plus 1 year"
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/gif "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
|
||||
# CSS / JS
|
||||
ExpiresByType text/css "access plus 1 year"
|
||||
ExpiresByType text/javascript "access plus 1 year"
|
||||
ExpiresByType application/javascript "access plus 1 year"
|
||||
ExpiresByType application/x-javascript "access plus 1 year"
|
||||
|
||||
# WOFF / WOFF2 / Fonts
|
||||
ExpiresByType application/font-woff "access plus 1 year"
|
||||
ExpiresByType font/woff "access plus 1 year"
|
||||
ExpiresByType application/font-woff2 "access plus 1 year"
|
||||
ExpiresByType font/woff2 "access plus 1 year"
|
||||
|
||||
# Favicon
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "\.(jpe?g|png|gif|webp|css|js|woff|woff2|ico)$">
|
||||
Header set Cache-Control "public, max-age=31536000, immutable"
|
||||
</FilesMatch>
|
||||
|
||||
<IfModule mod_brotli.c>
|
||||
AddOutputFilterByType BROTLI_COMPRESS text/html text/css text/plain text/xml text/javascript application/javascript application/x-javascript
|
||||
AddType text/plain .br
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_deflate.c>
|
||||
AddOutputFilterByType DEFLATE text/html
|
||||
AddOutputFilterByType DEFLATE text/plain
|
||||
AddOutputFilterByType DEFLATE text/xml
|
||||
AddOutputFilterByType DEFLATE text/css
|
||||
AddOutputFilterByType DEFLATE text/javascript
|
||||
AddOutputFilterByType DEFLATE application/javascript
|
||||
AddOutputFilterByType DEFLATE application/x-javascript
|
||||
AddOutputFilterByType DEFLATE application/xml
|
||||
AddOutputFilterByType DEFLATE application/xhtml+xml
|
||||
AddOutputFilterByType DEFLATE application/rss+xml
|
||||
AddOutputFilterByType DEFLATE image/svg+xml
|
||||
</IfModule>
|
||||
|
||||
<IfModule mod_mime.c>
|
||||
AddType application/javascript .js
|
||||
AddType font/woff2 .woff2
|
||||
AddType application/font-woff2 .woff2
|
||||
AddType application/font-woff .woff
|
||||
AddType font/woff .woff
|
||||
AddType image/svg+xml .svg
|
||||
</IfModule>
|
||||
|
||||
<FilesMatch "\.(css|js|jpg|jpeg|gif|png|ico|svg|woff|woff2)$">
|
||||
FileETag None
|
||||
Header unset ETag
|
||||
Header unset Set-Cookie
|
||||
</FilesMatch>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,21 @@
|
|||
.image-border_bottom {
|
||||
top: auto;
|
||||
}
|
||||
.textblock__container_swap > .image-content-size img {
|
||||
width: 100% !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
.textblock__container_row > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: 300px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
.textblock__container_side > .image-content-size img {
|
||||
width: 100% !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1600px) {
|
||||
.wrapper {
|
||||
|
|
@ -88,6 +103,11 @@
|
|||
.textblock__container_swap .textblock__image {
|
||||
margin-left: 70px;
|
||||
}
|
||||
.textblock__container_row > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: 300px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
.breadcrumbs {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
|
@ -246,10 +266,35 @@
|
|||
width: 310px;
|
||||
margin: 0px 11px 35px;
|
||||
}
|
||||
|
||||
.image-border-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.textblock__container_side > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: 425px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
.textblock__container_swap_left > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: 425px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
.textblock__container_row > .image-content-size {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.textblock__container_row > .image-content-size img {
|
||||
height: 210px !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
|
||||
|
||||
.values {
|
||||
padding-bottom: 55px;
|
||||
}
|
||||
|
|
@ -403,7 +448,6 @@
|
|||
padding-left: 10px;
|
||||
}
|
||||
[data-menu="submenu-2951"] .mobile-menu__level {
|
||||
padding-top: 0;
|
||||
min-height: calc(100vh - 80px);
|
||||
}
|
||||
.offer-form {
|
||||
|
|
@ -918,7 +962,7 @@
|
|||
font-size: 34px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
|
||||
.engagements__wrapper {
|
||||
text-align: center;
|
||||
}
|
||||
|
|
@ -979,8 +1023,30 @@
|
|||
border: 1px solid #a0a0a0;
|
||||
}
|
||||
.image-border-wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.textblock__container_swap > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
.textblock__container_side > .image-content-size img {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
.textblock__container_row > .image-content-size {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: 210px !important;
|
||||
}
|
||||
|
||||
.image-content-size img {
|
||||
height: 100% !important;
|
||||
object-fit: cover !important;
|
||||
}
|
||||
.list-item__title {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 4.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 368 KiB |
22464
public/news/26_06_07/assets/LDC_LAA_8_PRINT.pdf
Normal file
22464
public/news/26_06_07/assets/LDC_LAA_8_PRINT.pdf
Normal file
File diff suppressed because one or more lines are too long
BIN
public/news/26_06_07/assets/header.jpg
Normal file
BIN
public/news/26_06_07/assets/header.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 730 KiB |
|
|
@ -1,2 +0,0 @@
|
|||
User-agent: *
|
||||
Disallow:
|
||||
BIN
resources/assets/fonts/Poppins/Poppins-Black.ttf
Normal file
BIN
resources/assets/fonts/Poppins/Poppins-Black.ttf
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue