Compare commits

..

112 commits

Author SHA1 Message Date
Noah Lancien
6315c452cb ci: 👷 Replaced user 33 with 82 2026-10-26 12:02:54 +01:00
Noix
7b7b456ef2 Merge branch 'bump' into 'develop'
chore: ⬆️ bump php 8.5 + laravel 12 + meow

See merge request ldc-web/projets/agis!50
2026-10-22 15:41:56 +00:00
Corentin Huet
8d3d646403 chore: ⬆️ bump php 8.5 + laravel 12 + meow 2026-10-22 17:35:06 +02:00
karl.muller
666d79b32f Merge branch 'fix-86' into 'develop'
Closes #86 wording change

See merge request ldc-web/projets/agis!48
2026-06-18 12:50:00 +00:00
Jeremy Munsch
3ed4e6a99f
refactor: Closes #86 wording change in order to be law compliant on page explorer and gastro.. 2026-06-18 14:43:01 +02:00
Jeremy Munsch
80e21dabb7
fix: UserFactory was not upto date 2026-06-18 14:30:38 +02:00
Noah Lancien
f6b5dcb42c ci: migrate to mysql + limit cronjobs 2026-01-23 14:40:06 +01:00
Noah Lancien
df39b81e40 feat: updated admin-secure 2026-01-12 12:40:48 +01:00
Noah Lancien
e86852de81 ci: updated image version + target port 80 2026-01-09 14:39:44 +01:00
Noah Lancien
d7c69379c1 ci: added staging-resources.yaml 2026-01-08 14:38:51 +01:00
Noah Lancien
9b2e54a6a3 ci: added ci variables to define pod resources 2026-01-08 12:59:15 +01:00
noah.lancien
9bf67ce627 Merge branch 'issue-84' into 'develop'
feat: ✨ add desired location in joins us formulary #84

See merge request ldc-web/projets/agis!46
2025-06-16 13:13:27 +00:00
Alexis Gousseau
83cd90096e fix: 🚨 resolve errors for php 8.4 2025-06-16 12:26:32 +02:00
Alexis Gousseau
4656dd598e feat: ✨ add desired location in joins us formulary #84 2025-06-16 12:24:01 +02:00
Vincent BRIDIER
6bca2d4a13 feat: Add br in text on explorer page - Close #82 2025-05-02 08:41:05 +00:00
Karl Muller
aefaf9a0b1 CI: 👷 add multistage Docker build 2025-04-29 07:44:26 +00:00
Vincent BRIDIER
5f16591b85
feat: Add text on explorer page - Close #82 2025-04-29 08:59:34 +02:00
Noah Lancien
aa7715c19c ci: Updated the docker image version to 8.3 2025-03-06 16:50:24 +01:00
noah.lancien
53fa87fa17 Merge branch 'bump-and-saml' into 'develop'
Bump and saml

See merge request ldc-web/projets/agis!43
2025-03-06 15:16:36 +00:00
Noah Lancien
ecaff7a068 feat: Added SAML authentication 2025-03-06 16:06:54 +01:00
Noah Lancien
ab85b33052 chore: Bump Laravel-11 + sitemap-generator v2 -> v4 2025-03-06 14:28:42 +01:00
Noah Lancien
cb8eae4a18 fix: composer update to fix CVE 2025-01-13 11:14:52 +01:00
Noah Lancien
d13760ce4f ci: Added 'concurrencyPolicy: Forbid' in cron jobs 2025-01-13 11:08:27 +01:00
noah.lancien
e418f01712 Merge branch 'issue-22' into 'develop'
fix: 🐛 add condition in news, product and range controller in filtered...

See merge request ldc-web/projets/agis!42
2024-11-06 08:49:47 +00:00
Nicolas Demangeat
5eb1df950a fix: 🐛 add condition in news, product and range controller in filtered method for the paginatemanually 2024-11-06 09:18:13 +01:00
Vincent BRIDIER
a56e9a3e63 Merge branch 'fix-issue-78' into 'develop'
fix: 💄 center recaptcha btn

See merge request ldc-web/projets/agis!41
2024-10-08 07:46:29 +00:00
Sebastien Hattry
3b6801dc35 fix: 💄 center recaptcha btn 2024-10-08 09:40:26 +02:00
Vincent BRIDIER
84c28399ab
feat: Add customer service email for contactForm - Close #81 2024-10-07 17:28:44 +02:00
Vincent BRIDIER
1b0ec6a3d8
feat: Bump php deps for PHP8.3 2024-10-07 16:48:53 +02:00
Vincent BRIDIER
4338023e59 Merge branch 'issue-79' into 'develop'
Issue 79

See merge request ldc-web/projets/agis!40
2024-10-07 14:44:28 +00:00
Sebastien Hattry
7b9e6cc094 feat: change contact page according to cn 6.0.3 2024-09-10 12:20:38 +02:00
Sebastien Hattry
0cbf60cf03 chore: bump CN to 6.0.3 2024-09-10 11:59:20 +02:00
Sebastien Hattry
94156c7db0 chore: npm audit fix 2024-09-09 15:57:36 +02:00
Noah Lancien
59752b3b2e Updated manifest.yaml 2024-07-31 09:42:36 +02:00
Louis Rossignol
29108f8e28 Merge branch 'lor-migration' into 'develop'
Preparation migration

See merge request ldc-web/projets/agis!37
2024-07-03 14:00:20 +00:00
LOR
bc96ef44b0 Preparation migration 2024-07-03 15:59:18 +02:00
Jérémy Munsch
b4159f0c83 Merge branch 'issue-74' into 'develop'
Fixes #74

See merge request ldc-web/projets/agis!36
2024-04-24 13:30:31 +00:00
Jérémy Munsch
eed1ce86d5
Fixes #74 2024-04-24 15:29:28 +02:00
Jérémy Munsch
6e3f65b9ea Merge branch 'issue-76' into 'develop'
Closes #76

See merge request ldc-web/projets/agis!34
2024-04-24 12:53:20 +00:00
Jérémy Munsch
72c0652434 Merge branch '77-typo-header' into 'develop'
Issue 77

See merge request ldc-web/projets/agis!35
2024-04-24 12:51:16 +00:00
lucasschreiner
6f33b697e6 Fix FO typo into the header logo 2024-04-18 13:43:24 +02:00
Jérémy Munsch
4e596fb2b9
Closes #76 2024-03-20 13:54:37 +01:00
Jérémy Munsch
3fef79fa72
Ajout de redirections post MEP oubliés par Sylvain 2024-03-14 15:42:15 +01:00
Jérémy Munsch
45e298f2a9
Fix wrong throttles applied 2024-03-14 15:25:40 +01:00
Jérémy Munsch
ec39bb4f6f
Closes #72 2024-03-13 09:46:45 +01:00
Jérémy Munsch
0b8b1bff0a
#Closes 71 2024-03-13 09:30:34 +01:00
Jérémy Munsch
b0508da89d
Closes #66 2024-03-05 11:53:25 +01:00
Jérémy Munsch
fb94fcb1f6
Closes #67 2024-03-05 11:47:23 +01:00
Jérémy Munsch
8de072695b
Closes #69, #68 2024-03-05 11:46:26 +01:00
Jérémy Munsch
3db48686ee
Closes #70 2024-03-05 11:36:09 +01:00
Vincent BRIDIER
28625f2bf2 Merge branch 'issue-32' into 'develop'
Fixes #32 Add Home video vimeo possibility playing as loop except for youtube (need playlist)

See merge request ldc-web/projets/agis!33
2024-02-09 12:01:42 +00:00
Jérémy Munsch
b658db5bf7 Fixes #32 Add Home video vimeo possibility playing as loop except for youtube (need playlist) 2024-02-09 12:01:42 +00:00
Vincent BRIDIER
95d552c429 Merge branch 'issue-63' into 'develop'
clean blades - #63

See merge request ldc-web/projets/agis!32
2024-02-08 15:00:43 +00:00
Alexis Gousseau
76b8b61288 clean blades - #63 2024-02-07 12:15:42 +01:00
Vincent BRIDIER
e8871467c0 Merge branch 'products-images' into 'develop'
fix product images dimensions - #62

See merge request ldc-web/projets/agis!31
2024-02-02 10:43:43 +00:00
Alexis Gousseau
77ad125ad2 fix product images dimensions - #62 2024-02-02 09:50:06 +01:00
Vincent BRIDIER
dcf995bdc6 Merge branch 'issue-38' into 'develop'
bump cookies notice to v4.0.1 - #38

See merge request ldc-web/projets/agis!29
2024-01-30 10:49:14 +00:00
Alexis Gousseau
29016ad3ba bump cookies notice to v4.0.1 - #38 2024-01-30 09:46:31 +01:00
Jérémy Munsch
e1961fab93
disable executeJavascript for sitemap as no puppeteer 2024-01-29 17:08:57 +01:00
Vincent BRIDIER
fa7d44d03d Merge branch 'mulitple-issues' into 'develop'
Mulitple issues

See merge request ldc-web/projets/agis!28
2024-01-29 11:58:53 +00:00
Jérémy Munsch
4d363f8fac
Fix navlink active on ranges 2024-01-29 12:36:34 +01:00
Jérémy Munsch
80a394691c
Closes #48 2024-01-29 12:33:12 +01:00
Jérémy Munsch
1648636bfa
npm update 2024-01-29 12:25:46 +01:00
Jérémy Munsch
159cbbebe7
Closes #56 Fix phone number 2024-01-29 12:13:48 +01:00
Vincent BRIDIER
a53cfc7d55 Merge branch 'issue-60-nutrionnal-values-labels' into 'develop'
Updated nutrionnal values labels

See merge request ldc-web/projets/agis!27
2024-01-29 11:11:31 +00:00
Noah Lancien
c498e298d8 Updated nutrionnal values labels 2024-01-29 11:24:12 +01:00
Vincent BRIDIER
e2d3d7ec28 Merge branch 'php-stan-issues-48-57' into 'develop'
Php stan issues 48 57

See merge request ldc-web/projets/agis!26
2024-01-29 08:40:19 +00:00
Noah Lancien
bed9d9dde6 phpstan 2024-01-26 16:08:12 +01:00
Noah Lancien
19f6454ea3 Merge branch 'issue-57' into php-stan-issues-48-57 2024-01-26 13:09:38 +01:00
Noah Lancien
ffdd886ddc Merge remote-tracking branch 'origin/issue-48' into php-stan-issues-48-57 2024-01-26 13:03:07 +01:00
Vincent BRIDIER
0d17174052 Merge branch 'issue-51' into 'develop'
Added a BackToTop button

See merge request ldc-web/projets/agis!25
2024-01-26 11:14:50 +00:00
Vincent BRIDIER
50a99c7344 Merge branch 'issue-58' into 'develop'
paËlla

See merge request ldc-web/projets/agis!23
2024-01-26 11:13:50 +00:00
Vincent BRIDIER
41dba737cb Merge branch 'issue-59' into 'develop'
Added youtube channel link in join-us page

See merge request ldc-web/projets/agis!22
2024-01-26 11:13:27 +00:00
Vincent BRIDIER
ae4ab562dc Merge branch 'issue-52' into 'develop'
Issue 52, 53, 54, 55, 56 - Typos + footer link

See merge request ldc-web/projets/agis!21
2024-01-26 11:12:29 +00:00
Vincent BRIDIER
a04a65c232 Merge branch 'issue-36-seo-package' into 'develop'
Added the SEO package

See merge request ldc-web/projets/agis!20
2024-01-26 11:11:44 +00:00
Vincent BRIDIER
fd987180bb Merge branch 'fix-news-show' into 'develop'
apply text-center for news subtitle in news show blade

See merge request ldc-web/projets/agis!19
2024-01-26 11:10:21 +00:00
Vincent BRIDIER
58d359155f Merge branch 'issue-44' into 'develop'
Decorative picture is now aligned with the cooking advice part in produt.show

See merge request ldc-web/projets/agis!17
2024-01-26 11:06:57 +00:00
Vincent BRIDIER
1532e3a31d Merge branch 'issue-41' into 'develop'
Ranges title in homepage are now with different colors

See merge request ldc-web/projets/agis!16
2024-01-26 11:05:41 +00:00
Vincent BRIDIER
8e9f7f8419 Merge branch 'issue-32' into 'develop'
Issue 32 Fix Trait HasPicture (Blame pas possible à cause du squash), les parametres...

See merge request ldc-web/projets/agis!15
2024-01-26 11:04:33 +00:00
Vincent BRIDIER
d5c0c06294 Merge branch 'issue-46' into 'develop'
The width of black strip is now the same as title in about-us page

See merge request ldc-web/projets/agis!14
2024-01-26 11:02:59 +00:00
Vincent BRIDIER
0333649f48 Merge branch 'issue-35' into 'develop'
Issue 35

See merge request ldc-web/projets/agis!13
2024-01-26 11:02:19 +00:00
Vincent BRIDIER
6d482c4631 Merge branch 'develop' into issue-35 2024-01-26 12:01:21 +01:00
Noah Lancien
14b0382cdb Added a BackToTop button 2024-01-23 11:53:45 +01:00
Noah Lancien
54e501cd03 In contact form, dates are now dates inpiuts instead of string 2024-01-22 09:56:19 +00:00
Noah Lancien
d162f87f3e Some wording in the contact form 2024-01-22 09:55:27 +00:00
Noah Lancien
1f1d691cbe paËlla 2024-01-22 09:01:28 +01:00
Noah Lancien
be80ca6c6a Added youtube channel link in join-us page 2024-01-22 08:59:16 +01:00
Noah Lancien
bf365eadca Fix #53, added 'convictions' link in footer 2024-01-19 09:51:43 +01:00
Noah Lancien
28a8390c41 Fix #55, wording on ranges page 2024-01-19 09:48:29 +01:00
Noah Lancien
6ee0986b48 Fix #56, renamed civilities 2024-01-19 09:47:34 +01:00
Noah Lancien
239307c1b1 Fixed footer typo 2024-01-19 09:46:41 +01:00
Noah Lancien
81089b8e12 Fixed header typo 2024-01-19 09:45:25 +01:00
Noah Lancien
30ee51d9d1 Added the SEO package 2024-01-08 10:54:20 +01:00
Nicolas Demangeat
398c762b33 apply text-center for news subtitle in news show blade 2023-12-29 15:18:09 +01:00
Jérémy Munsch
a952ea6ac6
Fixes #48 Add style to ck-content + fix all front WYSIWYG taht where missing ck-content 2023-12-22 11:46:21 +01:00
Jérémy Munsch
8318e83d0b
Update secure header with vite config form base project 2023-12-22 11:23:25 +01:00
Jérémy Munsch
25d9e1cac6
Fix commitments responsive on products.show 2023-12-22 11:05:14 +01:00
Jérémy Munsch
cffa0f34a8
Closes #37 add sitemap 2023-12-22 09:49:02 +01:00
Noah Lancien
9a8e0f8f9a Decorative picture is now aligned with the cooking advice part in produt.show 2023-12-21 16:10:31 +01:00
Noah Lancien
5854fc274b Ranges title in homepage are now with different colors 2023-12-21 15:51:00 +01:00
Jérémy Munsch
608629307f
Closes #32 Add youtube video on Homepage
+ remove Unused video Frame
+ FIX  Video Inlaid in Cookie Notice
2023-12-21 15:42:36 +01:00
Noah Lancien
b32f5161b3 The width of black strip is now the same as title in about-us page 2023-12-21 15:22:05 +01:00
Jérémy Munsch
f06e87daf5
Update HasPicture 2023-12-21 15:18:27 +01:00
Jérémy Munsch
13d998ac9c
Fix Trait HasPicture (Blame pas possible à cause du squash), les parametres ajoutés doivent être à la fin pour ne pas niquer le reste du code 2023-12-21 14:41:27 +01:00
Jérémy Munsch
fed7a112c2
Bump inputs form base project 2023-12-21 13:56:34 +01:00
Jérémy Munsch
f34bb70504
cookie notice bump to 4.0.0 2023-12-21 12:09:47 +01:00
Jérémy Munsch
53ccbb5af8
Closes #35 Change civility labels 2023-12-21 10:59:50 +01:00
Jérémy Munsch
8949c3b427
bump front stack + add favicon Fixes #40 2023-12-21 10:45:43 +01:00
Jérémy Munsch
1963b827d8
composer update 2023-12-21 09:28:30 +01:00
Vincent BRIDIER
256a8b5542 Add foreach for contactformé 2023-12-19 16:50:48 +01:00
Vincent BRIDIER
6c63d52996 Merge branch '2.0.0' into 'develop'
2.0.0

See merge request ldc-web/projets/agis!1
2023-12-19 15:09:12 +00:00
noah.lancien
b557533b72 2.0.0 with Laravel 2023-12-19 15:09:12 +00:00
237 changed files with 10309 additions and 9335 deletions

View file

@ -3,7 +3,6 @@ APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
ASSET_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
@ -23,12 +22,6 @@ QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
@ -37,26 +30,8 @@ MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME=https
PUSHER_APP_CLUSTER=mt1
VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
VITE_PUSHER_HOST="${PUSHER_HOST}"
VITE_PUSHER_PORT="${PUSHER_PORT}"
VITE_PUSHER_SCHEME="${PUSHER_SCHEME}"
VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MAIL_CONTACTFORM_RECEIVER=
MAIL_CONTACTFORM_CUSTOMER_SERVICE_RECEIVER=
# Sentry
SENTRY_LARAVEL_DSN=
@ -69,3 +44,17 @@ MATOMO_SITE_ID=
GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
# UWIL ADMIN SECURE
# - LIST SEPARATED WITH ',' (Required)
UWIL_ADMIN_SECURE_CIDR_LIST=""
# - LIST SEPARATED WITH ',' (Optional)
UWIL_ADMIN_SECURE_CIDR_PROJECT_ALLOWED_LIST=""
# SAML
SAML2_DEFAULT_TENANT_UUID=
SAML2_SP_CERT_x509=
SAML2_SP_CERT_PRIVATEKEY=
SAML2_CONTACT_TECHNICAL_EMAIL=
SAML2_CONTACT_SUPPORT_EMAIL=

13
.gitignore vendored
View file

@ -23,3 +23,16 @@ Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc
# Ignore public favicon
/public/android-chrome-192x192.png
/public/android-chrome-512x512.png
/public/apple-touch-icon.png
/public/browserconfig.xml
/public/browserconfig.xml
/public/favicon-16x16.png
/public/favicon-32x32.png
/public/favicon.ico
/public/mstile-150x150.png
/public/safari-pinned-tab.svg
/public/site.webmanifest

View file

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

45
Dockerfile Normal file
View file

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

View file

@ -1 +1,7 @@
# Agis
## CRON
Put every min in PROD :
```
php artisan schedule:run
```

View file

@ -28,35 +28,28 @@ class UserCreate extends Command
/**
* The first name of the new user.
*
* @var string
* @var string|null
*/
protected $first_name;
/**
* The last name of the new user.
*
* @var string
* @var string|null
*/
protected $last_name;
/**
* The email of the new user.
*
* @var string
* @var string|null
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* The role of the new user.
*
* @var string
* @var string|null
*/
protected $role;
@ -76,9 +69,6 @@ class UserCreate extends Command
$this->email = null;
$this->addEmail();
$this->password = null;
$this->addPassword();
$this->role = null;
$this->addRole();
@ -86,7 +76,6 @@ class UserCreate extends Command
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'password' => $this->password,
'role' => $this->role,
'published' => true,
'published_at' => Carbon::now(),
@ -173,22 +162,6 @@ class UserCreate extends Command
}; //end while
}
/**
* Add a password to the user.
*
* @return void
*/
private function addPassword(): void
{
while (
($this->password = $this->secret(
(!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password'
)) !== $this->secret('Confirm the password')
) {
continue;
}; //end while
}
/**
* Add a role to the user.
*
@ -198,12 +171,10 @@ class UserCreate extends Command
{
while (is_null($this->role)) {
try {
$tmp = $this->choice(
$tmp = $this->choice(
'Select his role',
[RoleEnum::conceptor->label(), RoleEnum::admin->label(), RoleEnum::visitor->label()],
RoleEnum::visitor->label(),
$maxAttempts = null,
$allowMultipleSelections = false
);
if ($tmp === RoleEnum::conceptor->label()) {
$tmp = RoleEnum::conceptor;
@ -219,7 +190,7 @@ class UserCreate extends Command
$this->role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please choose from the selection',
'Please choose from the selection %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;

View file

@ -15,6 +15,8 @@ enum ActivityLogsEventEnum: int
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::created->name => 'Création',

View file

@ -13,9 +13,11 @@ enum CivilityEnum: int
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::man->name => 'Homme',
self::woman->name => 'Femme',
self::man->name => 'Monsieur',
self::woman->name => 'Madame',
];
}

View file

@ -15,6 +15,8 @@ enum ObjectEnum: int
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::customerService->name => 'Service consommateurs',

View file

@ -260,6 +260,8 @@ enum Country: int
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::afghanistan->name => 'Afghanistan',

View file

@ -0,0 +1,27 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum DesiredLocation: int
{
use BaseEnum;
case avignon = 1;
case herbignac = 2;
case tarare = 3;
case none = 4;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::avignon->name => 'Avignon',
self::herbignac->name => 'Herbignac',
self::tarare->name => 'Tarare',
self::none->name => 'Indifférent',
];
}

View file

@ -16,6 +16,8 @@ enum CategoryEnum: int
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::enterprise->name => 'La vie d\'Entreprise',

View file

@ -16,6 +16,8 @@ enum CookingModeEnum: int
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::microwave->name => 'Micro-ondes',

View file

@ -14,6 +14,8 @@ enum BackgroundEnum: int
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::candlemas->name => 'Chandeleur',

View file

@ -21,10 +21,12 @@ enum StaticPageType: int
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::contact->name => 'Contact',
self::legals->name => 'Mentions légales',
self::legals->name => 'Mentions légales et CGU',
self::confidentiality->name => 'Confidentialité',
self::home->name => 'Accueil',
self::conviction->name => 'Nos Convictions',

View file

@ -13,6 +13,8 @@ enum BootstrapThemeEnum: int
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::light->name => 'Light',

View file

@ -14,6 +14,8 @@ enum RoleEnum: int
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::conceptor->name => 'Concepteur',

View file

@ -51,7 +51,7 @@ class ActivityLogsController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\ActivityLog $activity_log
* @param \App\Models\ActivityLog $activity_log
* @return \Illuminate\Contracts\View\View
*/
public function show(ActivityLog $activity_log): \Illuminate\Contracts\View\View

View file

@ -98,7 +98,7 @@ class CommitmentController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Commitment $commitment
* @param \App\Models\Commitment $commitment
* @return \Illuminate\Contracts\View\View
*/
public function edit(Commitment $commitment): \Illuminate\Contracts\View\View
@ -139,7 +139,7 @@ class CommitmentController extends Controller
{
$commitment->products()->detach();
if ($commitment->explorerpage()->count() > 3) {
if ($commitment->explorerpage()->count() === 0) {
if ($commitment->deleteOrFail()) {
return redirect()->route('bo.commitments.index')
->with('success', __('crud.messages.has_been_deleted', [

View file

@ -28,7 +28,7 @@ class JoinuspageController extends Controller
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Joinuspages\UpdateJoinuspageRequest $request
* @param \App\Http\Requests\Bo\Joinuspage\UpdateJoinuspageRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateJoinuspageRequest $request): \Illuminate\Http\RedirectResponse

View file

@ -104,7 +104,7 @@ class NewsController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\News $news
* @param \App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function edit(News $news): \Illuminate\Contracts\View\View

View file

@ -115,7 +115,7 @@ class ProductController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Product $product
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function edit(Product $product): \Illuminate\Contracts\View\View

View file

@ -104,7 +104,7 @@ class RangeController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Range $range
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/
public function edit(Range $range): \Illuminate\Contracts\View\View
@ -143,7 +143,9 @@ class RangeController extends Controller
*/
public function destroy(Range $range): \Illuminate\Http\RedirectResponse
{
if (!$range->products->isEmpty()) {
/** @var \Illuminate\Database\Eloquent\Collection $products*/
$products = $range->products;
if (!$products->isEmpty()) {
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted_relation', [
'model' => Str::ucfirst(__('models.classes.range')),

View file

@ -98,7 +98,7 @@ class SeasonalSectionController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\SeasonalSection $seasonalSection
* @param \App\Models\SeasonalSection $seasonalSection
* @return \Illuminate\Contracts\View\View
*/
public function edit(SeasonalSection $seasonalSection): \Illuminate\Contracts\View\View

View file

@ -98,7 +98,7 @@ class StampController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Stamp $stamp
* @param \App\Models\Stamp $stamp
* @return \Illuminate\Contracts\View\View
*/
public function edit(Stamp $stamp): \Illuminate\Contracts\View\View

View file

@ -56,7 +56,7 @@ class StaticPageController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\StaticPage $static_page
* @param \App\Models\StaticPage $static_page
* @return \Illuminate\Contracts\View\View
*/
public function edit(StaticPage $static_page): \Illuminate\Contracts\View\View

View file

@ -100,7 +100,7 @@ class TestimonialController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Testimonial $testimonial
* @param \App\Models\Testimonial $testimonial
* @return \Illuminate\Contracts\View\View
*/
public function edit(Testimonial $testimonial): \Illuminate\Contracts\View\View

View file

@ -102,7 +102,7 @@ class UsersController extends Controller
/**
* Show the form for editing the specified resource.
*
* @param App\Models\User $user
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function edit(User $user): \Illuminate\Contracts\View\View

View file

@ -26,7 +26,7 @@ class Controller extends BaseController
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable $searchQuery The query string.
* @param callable|null $searchQuery The query string.
* @param string ...$fields Either a simple array or a string
* in case of a string the two fields
* would be concatenate to make the search.
@ -35,7 +35,7 @@ class Controller extends BaseController
protected function searchQuery(
Builder $query,
string $search,
callable $searchQuery = null,
callable|null $searchQuery = null,
string ...$fields
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
@ -94,7 +94,7 @@ class Controller extends BaseController
return $query;
}
// Get models with relation's name/label.
if (Str::endsWith($field, '_id') && $query->getModel()->has(Str::remove('_id', $field))) {
if (Str::endsWith($field, '_id')) {
$query->orWhereHas(Str::remove('_id', $field), function ($queryRelation) use ($field, $search) {
if (Schema::hasColumn(Str::remove('_id', $field) . 's', 'name')) {
$queryRelation->where('name', 'like', '%' . $search . '%');
@ -122,12 +122,15 @@ class Controller extends BaseController
*/
protected function getSearchValue(Builder $query, string $search, string $field): string|array
{
$cast = $query->getModel()->getCasts()[$field] ?? null;
if (
count($query->getModel()->getCasts()) and // Check if the model has casts.
isset($query->getModel()->getCasts()[$field]) and // Check if there is cast about specific field.
enum_exists($query->getModel()->getCasts()[$field]) // Check if the cast is an enum.
$cast and // Check if there is cast about specific field.
enum_exists($cast) // Check if the cast is an enum.
) {
$cases = collect($query->getModel()->getCasts()[$field]::toArray());
/** @var \App\Traits\Enums $cast */
// @phpstan-ignore-next-line
$cases = collect($cast::toArray());
$enum = $cases->filter(function ($item) use ($search) {
return preg_match('/' . Str::lower($search) . '/', Str::lower($item->label));
});
@ -148,12 +151,12 @@ class Controller extends BaseController
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->sort_col) {
Session::put("$rName.sort_col", request()->sort_col);
if (request()->input('sort_col')) {
Session::put("$rName.sort_col", request()->input('sort_col'));
Session::put("$rName.sorted", true);
}
if (request()->sort_way) {
Session::put("$rName.sort_way", request()->sort_way);
if (request()->input('sort_way')) {
Session::put("$rName.sort_way", request()->input('sort_way'));
Session::put("$rName.sorted", true);
}
if (request()->rst) {
@ -161,12 +164,16 @@ class Controller extends BaseController
Session::remove("$rName.sort_col");
Session::remove("$rName.sort_way");
}
if (request()->sort_col and request()->sort_way and Schema::hasColumn($table, request()->sort_col)) {
if (
!isset($ignore) or
(isset($ignore) and !in_array(request()->sort_col, $ignore))
) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
if (
request()->input('sort_col') and
request()->input('sort_way') and
Schema::hasColumn($table, request()->input('sort_col'))
) {
if (!in_array(request()->input('sort_col'), $ignore)) {
$query = $query->orderBy(
\sprintf("$table.%s", request()->input('sort_col')),
request()->input('sort_way')
);
}
} elseif (Schema::hasColumn($table, 'order')) {
$query = $query->orderBy('order', 'ASC');
@ -193,7 +200,7 @@ class Controller extends BaseController
*/
protected function paginate(
Builder $query,
ItemsPerPaginationEnum $pagination = null
ItemsPerPaginationEnum|null $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";

View file

@ -24,6 +24,7 @@ class NewsController extends Controller
/** @var \App\Models\News $newsModels */
$newsModels = News::query()->where('published', true)->orderBy('publish_at', 'desc')->paginate(12);
return view('fo.pages.news.index', \compact('staticPageModel', 'newsModels'));
}
@ -62,7 +63,7 @@ class NewsController extends Controller
})
->orderBy('publish_at', 'desc')
->get();
$newsModels = CollectionHelper::paginateManually($newsModels, 12, request()->page);
$newsModels = CollectionHelper::paginateManually($newsModels, 12, request()->page ?? 1);
return response()->json($newsModels);
}
}

View file

@ -3,6 +3,7 @@
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Lib\Helpers\CollectionHelper;
use App\Models\Product;
class ProductController extends Controller
@ -10,16 +11,35 @@ class ProductController extends Controller
/**
* Display the product page.
*
* @param \App\Models\Product $product
* @param string $slug
* @return \Illuminate\Contracts\View\View
*/
public function show(Product $product): \Illuminate\Contracts\View\View
public function show(string $slug): \Illuminate\Contracts\View\View
{
$otherProductModels = $product->range->products()
->where('id', '!=', $product->id)
$productModel = Product::query()->where('slug', $slug)->firstOrFail();
$otherProductModels = $productModel->range->products()
->where('id', '!=', $productModel->id)
->where('published', true)
->inRandomOrder()
->get();
return view('fo.pages.products.show', compact('product', 'otherProductModels'));
return view('fo.pages.products.show', compact('productModel', 'otherProductModels'));
}
/**
* Get filtered products.
*
* @return \Illuminate\Http\JsonResponse
*/
public function productsFiltered(): \Illuminate\Http\JsonResponse
{
$productModels = Product::query()
->where('published', true)
->where('range_id', request()->range_id)
->orderBy('order', 'asc')
->get();
$productModels = CollectionHelper::paginateManually($productModels, 12, request()->page ?? 1);
return response()->json($productModels);
}
}

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Lib\Helpers\CollectionHelper;
use App\Models\Product;
use App\Models\Range;
use App\Models\StaticPage;
@ -17,12 +18,12 @@ class RangeController extends Controller
*/
public function index(): \Illuminate\Contracts\View\View
{
/** @var \App\Models\Range $rangeModels */
$rangeModels = Range::query()->where('published', true)->orderBy('order')->get();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = StaticPage::query()->where('type', StaticPageType::ranges)->firstOrFail();
/** @var \App\Models\Range $rangeModels */
$rangeModels = Range::query()->where('published', true)->orderBy('order')->paginate(12);
return view('fo.pages.ranges.index', compact('rangeModels', 'staticPageModel'));
}
@ -38,8 +39,27 @@ class RangeController extends Controller
$rangeModel = Range::query()->where('published', true)->where('slug', $slug)->firstOrFail();
/** @var \App\Models\Product $productModels */
$productModels = Product::query()->where('range_id', $rangeModel->id)->where('published', true)->get();
$productModels = Product::query()
->where('range_id', $rangeModel->id)
->where('published', true)
->orderBy('order')
->paginate(12);
return view('fo.pages.ranges.show', compact('rangeModel', 'productModels'));
}
/**
* Get filtered ranges.
*
* @return \Illuminate\Http\JsonResponse
*/
public function rangesFiltered(): \Illuminate\Http\JsonResponse
{
$productModels = Range::query()
->where('published', true)
->orderBy('order', 'asc')
->get();
$productModels = CollectionHelper::paginateManually($productModels, 12, request()->page ?? 1);
return response()->json($productModels);
}
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Controllers\Fo;
use App\Enums\Contact\ObjectEnum;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\ContactRequest;
@ -27,7 +28,7 @@ class StaticPageController extends Controller
* @param \App\Models\StaticPage $aboutUsPage
* @return \Illuminate\Contracts\View\View
*/
public function aboutUs(StaticPage $aboutUsPage)
public function aboutUs(StaticPage $aboutUsPage): \Illuminate\Contracts\View\View
{
$aboutUsPage = StaticPage::query()->where('type', StaticPageType::aboutus)
->get();
@ -40,7 +41,7 @@ class StaticPageController extends Controller
* @param \App\Models\StaticPage $legalPage
* @return \Illuminate\Contracts\View\View
*/
public function legals(StaticPage $legalPage)
public function legals(StaticPage $legalPage): \Illuminate\Contracts\View\View
{
$legalPage = StaticPage::query()->where('type', StaticPageType::legals)->get();
return view('fo.pages.legals', ['staticPageModel' => $legalPage[0]]);
@ -52,7 +53,7 @@ class StaticPageController extends Controller
* @param \App\Models\StaticPage $policiesPage
* @return \Illuminate\Contracts\View\View
*/
public function policies(StaticPage $policiesPage)
public function policies(StaticPage $policiesPage): \Illuminate\Contracts\View\View
{
$policiesPage = StaticPage::query()->where('type', StaticPageType::confidentiality)->get();
return view('fo.pages.legals', ['staticPageModel' => $policiesPage[0]]);
@ -103,14 +104,22 @@ class StaticPageController extends Controller
}
/**
* Display a listing of the resource.
* Handle contactForm Request
*
* @param \App\Http\Requests\Fo\ContactRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function contactForm(ContactRequest $request): \Illuminate\Http\RedirectResponse
{
Mail::to(\config('mail.from.address'))->send(new SendContactMail((object)$request->validated()));
if ($request->object === strval(ObjectEnum::customerService->value())) {
$receivers = \config('mail.contactform.customerService');
} else {
$receivers = \config('mail.contactform.receiver');
}
foreach ($receivers as $email) {
Mail::to($email)->send(new SendContactMail((object)$request->validated()));
}
return redirect()->route('fo.contact.show')
->with(
@ -177,14 +186,17 @@ class StaticPageController extends Controller
}
/**
* Display a listing of the resource.
* Handle joinusForm Request
*
* @param \App\Http\Requests\Fo\JoinusRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function joinusForm(JoinusRequest $request): \Illuminate\Http\RedirectResponse
{
Mail::to(\config('mail.from.address'))->send(new SendJoinusMail((object)$request->validated()));
$receivers = \config('mail.contactform.receiver');
foreach ($receivers as $email) {
Mail::to($email)->send(new SendJoinusMail((object)$request->validated()));
}
return redirect()->route('fo.joinus.show')
->with(

View file

@ -40,6 +40,12 @@ class Kernel extends HttpKernel
\App\Http\Middleware\SecureHeaders::class
],
'saml' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
@ -65,5 +71,6 @@ class Kernel extends HttpKernel
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
'local-development' => \App\Http\Middleware\LocalDevelopment::class,
];
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class LocalDevelopment
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
if (app()->environment() != 'local') {
return abort(403, 'You are not authorized to access this');
}
return $next($request);
}
}

View file

@ -59,12 +59,17 @@ class SecureHeaders
config('app.debug') ? "https://{$host}:4443" : '',
'https://yl.s-web.fr',
]) . ';',
"img-src * https://yl.s-web.fr data: blob:;",
"img-src 'self' data: blob: " . implode(' ', [
'https://yl.s-web.fr',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : ''
]) . ';',
'frame-src ' . implode(' ', [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/',
'https://www.youtube.com',
'https://player.vimeo.com',
]) . ';',
"font-src 'self' data: " . \implode(' ', [
// * VITE DEV
@ -93,6 +98,7 @@ class SecureHeaders
"$scheme://www.google-analytics.com",
'https://www.google.com/recaptcha/',
'https://www.gstatic.com/recaptcha/',
'https://yl.s-web.fr',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
]) . ';'
@ -103,8 +109,11 @@ class SecureHeaders
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', \implode(',', [
'fullscreen', 'accelerometer', 'encrypted-media',
'gyroscope', 'picture-in-picture'
'fullscreen',
'accelerometer',
'encrypted-media',
'gyroscope',
'picture-in-picture'
]) . ';');
return $response;
}

View file

@ -27,14 +27,14 @@ class UpdateConvictionpageRequest extends FormRequest
public function rules()
{
return [
'key_feed' => 'required|numeric',
'key_earth_1' => 'required|numeric',
'key_earth_2' => 'required|numeric',
'key_together_1' => 'required|numeric',
'key_together_2' => 'required|numeric',
'key_together_3' => 'required|numeric',
'key_together_4' => 'required|numeric',
'key_territory' => 'required|numeric',
'key_feed' => 'required|numeric|max:2147483647',
'key_earth_1' => 'required|numeric|max:2147483647',
'key_earth_2' => 'required|numeric|max:2147483647',
'key_together_1' => 'required|numeric|max:2147483647',
'key_together_2' => 'required|numeric|max:2147483647',
'key_together_3' => 'required|numeric|max:2147483647',
'key_together_4' => 'required|numeric|max:2147483647',
'key_territory' => 'required|numeric|max:2147483647',
];
}

View file

@ -2,6 +2,8 @@
namespace App\Http\Requests\Bo\Homepage;
use App\Rules\IsValidVimeoId;
use App\Rules\IsValidYoutubeId;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
@ -26,6 +28,9 @@ class UpdateHomepageRequest extends FormRequest
*/
protected function prepareForValidation()
{
$this->merge([
'display_video' => $this->boolean('display_video')
]);
$this->mergePicture('picture');
}
@ -44,10 +49,20 @@ class UpdateHomepageRequest extends FormRequest
'picture_title' => 'required|string|between:3,255',
'presentation_title' => 'required|string|between:3,255',
'presentation_content' => 'required|string',
'display_video' => 'present|boolean',
'video_id' => [
'nullable', 'required_if:display_video,true',
function (string $attribute, mixed $value, \Closure $fail) {
if (!IsValidYoutubeId::validates($value) and !IsValidVimeoId::validates($value)) {
(new IsValidYoutubeId())->validate($attribute, $value, $fail);
(new IsValidVimeoId())->validate($attribute, $value, $fail);
}
}
],
];
return \array_merge(
$rules,
$this->pictureRules('picture', 640, 390, 640, 390, false),
$this->pictureRules('picture', 640, 390, 640, 390, true, requiredIf: 'display_video,false'),
);
}
@ -59,6 +74,7 @@ class UpdateHomepageRequest extends FormRequest
public function attributes(): array
{
return [
'video_id' => trans('validation.custom.video_id'),
'title_part1' => trans('validation.custom.title_part1'),
'title_part2' => trans('validation.custom.title_part2'),
'title_part3' => trans('validation.custom.title_part3'),

View file

@ -2,6 +2,8 @@
namespace App\Http\Requests\Bo\Joinuspage;
use App\Rules\IsValidVimeoId;
use App\Rules\IsValidYoutubeId;
use Illuminate\Foundation\Http\FormRequest;
class UpdateJoinuspageRequest extends FormRequest
@ -24,7 +26,15 @@ class UpdateJoinuspageRequest extends FormRequest
public function rules()
{
return [
'video_id' => 'required|string|regex:%([^"&?\/\s]{11})%i',
'video_id' => [
'required', 'string',
function (string $attribute, mixed $value, \Closure $fail) {
if (!IsValidYoutubeId::validates($value) and !IsValidVimeoId::validates($value)) {
(new IsValidYoutubeId())->validate($attribute, $value, $fail);
(new IsValidVimeoId())->validate($attribute, $value, $fail);
}
}
],
];
}

View file

@ -54,7 +54,7 @@ class StoreNewsRequest extends FormRequest
'subtitle' => 'required|string|between:3,255',
'content' => 'required|string|min:15',
'published' => 'required|boolean',
'publish_at' => 'nullable|date',
'publish_at' => 'required|date',
'unpublish_at' => 'nullable|date|after:publish_at',
];

View file

@ -68,8 +68,8 @@ class StoreProductRequest extends FormRequest
return \array_merge(
$rules,
$this->pictureRules('picture_thumbnail', 332, 252, 332, 252, false),
$this->pictureRules('picture_ambiance', 774, 515, 774, 515, false),
$this->pictureRules('picture_packshot', 327, 252, 327, 252, false),
$this->pictureRules('picture_ambiance', 1115, 790, 1115, 790, false),
$this->pictureRules('picture_packshot', 560, 480, 560, 480, false),
);
}
@ -89,35 +89,35 @@ class StoreProductRequest extends FormRequest
// phpcs:enable
$nutritionnal_values = json_encode([
"energyKj" => [
"label" => "Energie en Kj",
"label" => "ÉNERGIE (kJ)",
"value" => $this->nutritionnal_values['energy_kj'],
],
"energyKcal" => [
"label" => "Energie en kcal",
"label" => "ÉNERGIE (kcal)",
"value" => $this->nutritionnal_values['energy_kcal'],
],
"fat" => [
"label" => "Matières grasses",
"label" => "MATIÈRES GRASSES (g)",
"value" => $this->nutritionnal_values['fat'],
],
"saturated-fat" => [
"label" => "dont acides gras saturés",
"label" => "ACIDES GRAS SATURÉS (g)",
"value" => $this->nutritionnal_values['saturated_fat'],
],
"carbohydrate" => [
"label" => "Glucides",
"label" => "GLUCIDES (g)",
"value" => $this->nutritionnal_values['carbohydrate'],
],
"sugar" => [
"label" => "dont sucres",
"label" => "SUCRES (g)",
"value" => $this->nutritionnal_values['sugar'],
],
"proteins" => [
"label" => "Protéines",
"label" => "PROTÉINES (g)",
"value" => $this->nutritionnal_values['proteins'],
],
"salt" => [
"label" => "Sel",
"label" => "SEL (g)",
"value" => $this->nutritionnal_values['salt'],
]
]);

View file

@ -59,7 +59,7 @@ class StoreRangeRequest extends FormRequest
return \array_merge(
$rules,
$this->pictureRules('picture', 466, 440, 466, 440, false),
$this->pictureRules('picture_productsheet', 332, 442, 332, 442, false),
$this->pictureRules('picture_productsheet', 680, 300, 680, 300, false),
$this->pictureRules('picture_thumbnail', 246, 337, 246, 337, false),
);
}

View file

@ -24,8 +24,10 @@ class UpdateStaticPageRequest extends FormRequest
*/
public function prepareForValidation(): void
{
/** @var \App\Models\StaticPage $staticPageModel */
$staticPageModel = $this->route('static_page');
if (
\in_array($this->route('static_page')->type, [
\in_array($staticPageModel->type, [
\App\Enums\StaticPageType::confidentiality,
\App\Enums\StaticPageType::legals
])

View file

@ -1,229 +0,0 @@
<?php
namespace App\Http\Requests\Bo;
use App\Enums\InputType;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\ValidationException;
abstract class UpdateMiniCrudRequest extends FormRequest
{
/**
* The model parameter name
*
* @var string
*/
protected $modelName = null;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
* @phpcs:disable Generic.Metrics.NestingLevel.TooHigh
*/
public function rules()
{
// phpcs:enable
$rules = [];
foreach ($this->{$this->modelName}->data as $iptName => $input) {
$input = \json_decode(\json_encode($input), true);
switch ($input['inputType']) {
case InputType::number->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('required|numeric', $input)
]);
break;
case InputType::videoYoutube->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('regex:%([^"&?\/\s]{11})%i', $input)
]);
break;
case InputType::videoVimeo->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('regex:%([0-9]{8,10})%i', $input)
]);
break;
case InputType::video->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate([
'regex:%([0-9]{8,10}|[^"&?\/\s]{11})%i'
], $input)
]);
break;
case InputType::string->value:
$rules = array_merge($rules, [
"data.$iptName.value" => $this->compileValidate('required|string|min:3', $input)
]);
break;
case InputType::text->value:
$mergeRules = $this->compileValidate('required|string|min:15', $input);
$mergeRules = collect($mergeRules)
->map(function ($rule) {
if (\strpos($rule, 'max:') !== false) {
$max = \intval(str_replace('max:', '', $rule));
return function ($attribute, $value, $fail) use ($max) {
if (\strlen(\strip_tags($value)) > $max) {
$fail("$attribute ne doit pas excéder $max caractères");
}
};
}
return $rule;
});
$rules = array_merge($rules, [
"data.$iptName.value" => $mergeRules
]);
break;
case InputType::image->value:
$required = $this->isUpdate() ? 'sometimes' : 'required';
$ipt = $this->data[$iptName];
$max = isset($ipt['dimensionsMax']);
$dim = \is_string($input['dimensions']) ? collect(explode(
',',
(isset($ipt['dimensionsMax']) and is_string($ipt['dimensionsMax'])) ?
$ipt['dimensionsMax'] : $input['dimensions']
))->mapWithKeys(function ($v) {
$v = explode(' ', $v);
return [$v[0] => $v[1]];
}) : $input['dimensions'];
// * Adapt dimensions
$sizes = (object)[
'minHeight' => $dim['height'],
'maxHeight' => $dim['height'],
'minWith' => $dim['width'],
'maxWith' => $dim['width'],
];
$rules = array_merge($rules, [
"data.$iptName.value" => [
function (string $attribute, $value, callable $fail) use ($sizes) {
return ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
],
$this->compileValidate(
implode('|', [
$required,
'image',
'mimes:png,jpg,jpeg',
sprintf(
'dimensions:%s=%d,%s=%d',
$max ? 'max_height' : 'height',
$dim['height'],
$max ? 'max_width' : 'width',
$dim['width']
)
]),
$input
),
"data.$iptName.alt" => $this->compileValidate("$required|string|min:5", $input),
"data.$iptName.title" => $this->compileValidate("$required|string|min:5", $input)
]);
break;
case InputType::url->value:
$rules = array_merge($rules, [
"data.$iptName.value" => [
'required', 'url',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
try {
ToolboxHelper::assertNoInternalUrls($attribute, $value);
ToolboxHelper::assertNoInternalBoUrls($attribute, $value);
} catch (ValidationException $e) {
$fail(collect($e->errors())->first()[0]);
}
}
]
]);
break;
case InputType::boolean->value:
$all = $this->all();
$nested = ToolboxHelper::dotStringToNestedArray(
"data.$iptName.value",
$this->input("data.$iptName.value") ? true : false
);
$all = ToolboxHelper::arrayMergeRecursiveDistinct($all, $nested);
$this->merge($all);
$rules = array_merge($rules, [
"data.$iptName.value" => 'boolean'
]);
break;
} //end switch
} //end foreach
return $rules;
}
/**
* Undocumented function
*
* @param string|array $inputTypeValidate
* @param array $input
* @return array
*/
public function compileValidate(string|array $inputTypeValidate, array $input): array
{
if (\is_string($inputTypeValidate)) {
$inputTypeValidate = explode('|', $inputTypeValidate);
}
/** @var array|string */
$inputValidate = \collect($input)->get('validate', $input);
$inputValidate = \is_string($inputValidate) ? explode('|', $inputValidate) : $inputValidate;
foreach ($inputValidate as $validate) {
if (!\in_array($validate, $inputTypeValidate)) {
$inputTypeValidate[] = $validate;
}
}
$test = \collect($inputTypeValidate)->all();
$test = \collect($inputTypeValidate)->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->unique()->all();
return \collect($inputTypeValidate)->unique()->sort(function ($valueA, $valueB) {
$valueA = $this->ruleHasToComeFirst($valueA) ? 0 : 2;
$valueB = $this->ruleHasToComeFirst($valueB) ? 1 : 2;
return $valueA - $valueB;
})->all();
}
/**
* Does the rulle need to come first ?
*
* @param string $rule
* @return boolean
*/
private function ruleHasToComeFirst(string $rule): bool
{
$rule = \collect(\explode(':', $rule, 1))->filter()->first(null, '');
return \collect([
'nullable', 'present', 'required', 'required_if',
'required_unless', 'required_with', 'required_with_all',
'required_without', 'required_without_all', 'required_with_all',
'missing', 'missing_if', 'missing_unless',
'missing_with', 'missing_with_all',
])->first(fn (string $firstRule) => \strpos($rule, $firstRule) === 0) ? true : false;
}
/**
* Is this request an update request ?
*
* @return boolean
*/
private function isUpdate(): bool
{
return $this->isMethod('PUT');
}
}

View file

@ -3,9 +3,10 @@
namespace App\Http\Requests\Bo\Users;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
@ -16,7 +17,7 @@ class StoreUserRequest extends FormRequest
*/
public function authorize(): bool
{
return auth('backend')->check();
return Gate::check('create', User::class);
}
/**
@ -24,10 +25,10 @@ class StoreUserRequest extends FormRequest
*
* @return void
*/
protected function prepareForValidation()
protected function prepareForValidation(): void
{
$this->merge([
'published' => $this->published ? true : false
'published' => $this->boolean('published')
]);
}
@ -42,14 +43,6 @@ class StoreUserRequest extends FormRequest
'first_name' => 'required|string|min:3|max:255',
'last_name' => 'required|string|min:3|max:255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email',
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
],
'role' => ['required', new Enum(RoleEnum::class)],
'published' => 'required|boolean',
];
@ -66,7 +59,6 @@ class StoreUserRequest extends FormRequest
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'password' => trans('validation.attributes.password'),
'role' => trans('validation.attributes.role'),
'published' => trans('validation.custom.publication')
];

View file

@ -2,23 +2,30 @@
namespace App\Http\Requests\Bo\Users;
use Illuminate\Validation\Rules\Password;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', $this->route('user'));
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
protected function prepareForValidation(): void
{
$this->merge([
'published' => $this->published ? true : false,
'password' => $this->password ? $this->password : $this->route('user')->password,
'password_confirmation' => $this->password_confirmation ?
$this->password_confirmation :
$this->route('user')->password
'published' => $this->boolean('published'),
]);
}
@ -30,18 +37,10 @@ class UpdateUserRequest extends StoreUserRequest
public function rules(): array
{
return array_merge(parent::rules(), [
'email' => [
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
'unique:users,email,' . $this->route('user')->getKey() . ',id',
],
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
Rule::unique('users', 'email')->ignore(\optional($this->route('user'))->getRouteKey())
],
]);
}

View file

@ -20,10 +20,6 @@ class ContactRequest extends FormRequest
*/
protected function prepareForValidation()
{
$this->merge([
'accept_rules' => $this->accept_rules ? true : false,
'send_data' => $this->send_data ? true : false,
]);
$this->mergePicture('file');
}
@ -31,9 +27,11 @@ class ContactRequest extends FormRequest
* Get the validation rules that apply to the request.
*
* @return array
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function rules()
{
// phpcs:enable
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
$rules = [
@ -43,104 +41,104 @@ class ContactRequest extends FormRequest
'society' => 'sometimes|nullable|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'sometimes|nullable|numeric|min:0',
'address' => 'sometimes|nullable|string|between:3,255',
'postal_code' => 'sometimes|nullable|numeric|min:0',
'city' => 'sometimes|nullable|string|between:3,255',
'address' => 'required|string|between:3,255',
'postal_code' => 'required|numeric|min:0',
'city' => 'required|string|between:3,255',
'object' => ['required', new Enum(ObjectEnum::class)],
'product_name' => [
'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_date_limit,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_name'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_limit' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_barcode,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_limit'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_barcode' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_lot,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_barcode'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_lot' => [
'sometimes', 'nullable', 'numeric', 'min:0',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_date_consumption,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_lot'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_consumption' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_location',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_consumption'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_location' => [
'sometimes', 'nullable', 'string', 'between:3,255',
// @phpcs:ignore Generic.Files.LineLength.TooLong
'required_with:product_name,product_date_limit,product_barcode,product_lot,product_date_consumption',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_location'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'message' => 'required|string|between:3,255',
'accept_rules' => 'required|boolean',
'send_data' => 'required|boolean',
'message' => 'required|string|between:3,500',
];
if ($this->object === strval(ObjectEnum::customerService->value())) {
$rules = \array_merge($rules, [
'product_name' => [
'string',
'between:3,255',
'required',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_name'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_limit' => [
'required',
'date',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_limit'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_barcode' => [
'numeric',
'min:0',
'required',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_barcode'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_lot' => [
'numeric',
'min:0',
'required',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_lot'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_date_consumption' => [
'required',
'date',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_date_consumption'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
'product_location' => [
'string',
'between:3,255',
'required',
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->get("object") != ObjectEnum::customerService->value()) {
$fail(\trans('le Champs :field doit être vide lorsque le champs objet
n\'est pas égale à :enumValue.', [
'field' => trans('validation.custom.product_location'),
'enumValue' => ObjectEnum::customerService->label(),
]));
}
}
],
]);
} //end if
if (
isset(request()->file) and
(request()->file->getClientMimeType() === "image/jpeg" ||
@ -152,8 +150,8 @@ class ContactRequest extends FormRequest
0,
1920,
1080,
3000,
true,
3000,
'product_name,product_date_limit,product_barcode,product_lot,product_date_consumption,product_location'
));
} elseif (isset(request()->file) and (request()->file->getClientMimeType() === "application/pdf")) {
@ -161,6 +159,20 @@ class ContactRequest extends FormRequest
//phpcs:ignore Generic.Files.LineLength.TooLong
'file' => 'sometimes|nullable|mimes:pdf|required_with:product_name,product_date_limit,product_barcode,product_lot,product_date_consumption,product_location'
]);
} elseif (
isset(request()->file) and
(request()->file->getClientMimeType() !== "application/pdf") and
(request()->file->getClientMimeType() !== "image/jpeg") and
(request()->file->getClientMimeType() !== "image/png")
) {
$rules = \array_merge($rules, [
'file' => [
// @phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
function ($attribute, $value, $fail) {
$fail(\trans('Le format du fichier doit être .jpg, .png ou .pdf'));
}
],
]);
} //end if
return $rules;
}
@ -190,8 +202,6 @@ class ContactRequest extends FormRequest
'product_date_consumption' => trans('validation.custom.product_date_consumption'),
'product_location' => trans('validation.custom.product_location'),
'message' => trans('validation.attributes.message'),
'accept_rules' => trans('validation.custom.accept_rules'),
'send_data' => trans('validation.custom.send_data'),
'file' => trans('validation.custom.file'),
];
}

View file

@ -2,8 +2,10 @@
namespace App\Http\Requests\Fo;
use App\Enums\DesiredLocation;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class JoinusRequest extends FormRequest
{
@ -12,17 +14,18 @@ class JoinusRequest extends FormRequest
*
* @return array
*/
public function rules()
public function rules(): array
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
return [
'last_name' => 'required|string|between:3,255',
'first_name' => 'required|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'required|numeric|min:0',
'message' => 'required|string|between:3,255',
"file" => "sometimes|nullable|mimes:pdf|max:10000"
'last_name' => 'required|string|between:3,255',
'first_name' => 'required|string|between:3,255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|max:255',
'phone' => 'required|numeric|min:0',
'desired_location' => ['required', new Enum(DesiredLocation::class)],
'message' => 'required|string|between:3,500',
"file" => "sometimes|nullable|mimes:pdf|max:10000"
];
}
@ -34,12 +37,13 @@ class JoinusRequest extends FormRequest
public function attributes(): array
{
return [
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'phone' => trans('validation.attributes.phone'),
'message' => trans('validation.attributes.message'),
'file' => trans('validation.attributes.file'),
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'phone' => trans('validation.attributes.phone'),
'desired_location' => trans('validation.custom.desired_location'),
'message' => trans('validation.attributes.message'),
'file' => trans('validation.attributes.file'),
];
}
}

View file

@ -19,7 +19,7 @@ class SeasonalSectionStatusUpdate
*/
public function handle()
{
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSections> */
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSection> */
$seasonalsectionsListToPublish = SeasonalSection::query()
->where('publish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('publish_at')
@ -30,7 +30,7 @@ class SeasonalSectionStatusUpdate
$seasonalsections->published = true;
$seasonalsections->saveOrFail();
}
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSections> */
/** @var \Illuminate\Database\Eloquent\Collection<array-key, \App\Models\SeasonalSection> */
$seasonalsectionsListToUnPublish = SeasonalSection::query()
->where('unpublish_at', '<=', Carbon::now()->toDateTime())
->whereNotNull('unpublish_at')

View file

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

View file

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

View file

@ -175,11 +175,10 @@ class ToolboxHelper
$url = $match;
try {
$request = Request::create($url);
// If a route is match then it do not throws exception.
if (
\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST) &&
$routes->match($request)
) {
// Assert route matches existing routes.
$routes->match($request);
// ? Is url internal ?
if (\parse_url(config('app.url'), \PHP_URL_HOST) === \parse_url($url, \PHP_URL_HOST)) {
// Removes the matched url.
throw ValidationException::withMessages([
$field => trans(
@ -205,14 +204,14 @@ class ToolboxHelper
* @param array|string $replace
* @param array|string $subject
* @param integer $count
* @return array|string
* @return array|string|false
*/
public static function mbReplace(
array|string $search,
array|string $replace,
array|string $subject,
int &$count = 0
): array|string {
): array|string|false {
if (!is_array($search) && is_array($replace)) {
return false;
}
@ -289,9 +288,12 @@ class ToolboxHelper
public static function getOriginal(Model $model, string $attribute): mixed
{
if (\in_array('Spatie\Translatable\HasTranslations', \class_uses_recursive($model))) {
// @phpstan-ignore-next-line
return $model->getTranslation(
$attribute,
// @phpstan-ignore-next-line
$model->getLocale(),
// @phpstan-ignore-next-line
$model->useFallbackLocale()
);
}
@ -361,47 +363,45 @@ class ToolboxHelper
/**
* Convert deeply an object to array
*
* @param array|object $input
* @param mixed $input
* @return array
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function objectToArray($input): array
public static function objectToArray(mixed $input): array
{
// Only process if it's an object or array being passed to the function.
if (is_object($input) or is_array($input)) {
$ret = (array)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::objectToArray($item) : $item;
}
return $ret;
} else {
if (!(is_object($input) or is_array($input))) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
$ret = (array)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::objectToArray($item) : $item;
}
return $ret;
}
/**
* Convert deeply an array to object
*
* @param array|object $input
* @param mixed $input
* @return object
* @throws \RuntimeException If $input var is a scalar value.
*/
public static function arrayToObject($input): object
public static function arrayToObject(mixed $input): object
{
// Only process if it's an object or array being passed to the function.
if (is_object($input) or is_array($input)) {
$ret = (object)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::arrayToObject($item) : $item;
}
return $ret;
} else {
if (!(is_object($input) or is_array($input))) {
throw new \RuntimeException('Cannot handle $input of type ' . \gettype($input));
}
$ret = (object)$input;
foreach ($ret as &$item) {
// Recursively process EACH element only if not scalar.
$item = (is_object($item) or is_array($item)) ?
self::arrayToObject($item) : $item;
}
return $ret;
}
/**
@ -409,11 +409,11 @@ class ToolboxHelper
*
* @param string $attribute
* @param string|null $value
* @param callback $fail
* @param callable $fail
* @param object $sizes
* @return void
*/
public static function validatePicture(string $attribute, string|null $value, callable $fail, object $sizes)
public static function validatePicture(string $attribute, string|null $value, callable $fail, object $sizes): void
{
if (!($useStorage = Storage::exists($value)) and !File::exists($value)) {
$fail(trans(':attribute n\'existe pas dans le système de fichier', [
@ -432,7 +432,7 @@ class ToolboxHelper
'attribute' => $attribute
]));
}
if (!($dimensions = \getimagesize($fileFullPath)) or !isset($dimensions[0]) or !isset($dimensions[1])) {
if (!($dimensions = \getimagesize($fileFullPath))) {
$fail(trans(':attribute impossible de valider les dimensions', [
'attribute' => $attribute
]));

View file

@ -5,7 +5,6 @@ namespace App\Mail;
use App\Enums\Contact\ObjectEnum;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
@ -17,7 +16,7 @@ class SendContactMail extends Mailable
use SerializesModels;
/**
* @var \App\Mail\Contact
* @var \stdClass
*/
private \stdClass $data;
@ -40,16 +39,11 @@ class SendContactMail extends Mailable
*/
public function envelope(): \Illuminate\Mail\Mailables\Envelope
{
$subject = ObjectEnum::make($this->data->object) ?
ObjectEnum::make($this->data->object)->label() :
__('Nouveau message');
return new Envelope(
from: new Address(
$this->data->email,
($this->data->first_name !== null || $this->data->last_name !== null) ?
(($this->data->first_name ?? '') . " " . ($this->data->last_name ?? '')) :
null
),
to: [new Address(config('mail.from.address'), config('mail.from.name'))],
subject: sprintf('%s - %s', ObjectEnum::make($this->data->object)->label() ??
__('Nouveau message'), config('app.name')),
subject: sprintf('%s - %s', $subject, config('app.name')),
);
}
@ -101,8 +95,6 @@ class SendContactMail extends Mailable
* @property string $product_lot
* @property string $product_date_consumption
* @property string $product_location
* @property boolean $accept_rules
* @property boolean $send_data
* @property \Illuminate\Http\UploadedFile $file
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
*/

View file

@ -2,9 +2,9 @@
namespace App\Mail;
use App\Enums\DesiredLocation;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Attachment;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
@ -16,7 +16,7 @@ class SendJoinusMail extends Mailable
use SerializesModels;
/**
* @var \App\Mail\Joinus
* @var \stdClass
*/
private \stdClass $data;
@ -25,11 +25,11 @@ class SendJoinusMail extends Mailable
*
* @param \stdClass $data
* @return void
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
public function __construct(\stdClass $data)
{
$this->data = clone $data;
$data->desired_location = DesiredLocation::make($data->desired_location)->label();
$this->data = clone $data;
}
/**
@ -40,13 +40,6 @@ class SendJoinusMail extends Mailable
public function envelope(): \Illuminate\Mail\Mailables\Envelope
{
return new Envelope(
from: new Address(
$this->data->email,
($this->data->first_name !== null || $this->data->last_name !== null) ?
(($this->data->first_name ?? '') . " " . ($this->data->last_name ?? '')) :
null
),
to: [new Address(config('mail.from.address'), config('mail.from.name'))],
subject: sprintf('%s - %s', __('Candidature spontanée'), config('app.name')),
);
}
@ -86,6 +79,7 @@ class SendJoinusMail extends Mailable
* @property string $first_name
* @property string $email
* @property string $phone
* @property \App\Enums\DesiredLocation $desired_location
* @property string $message
* @property \Illuminate\Http\UploadedFile $file
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses

View file

@ -3,18 +3,17 @@
namespace App\Models;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Schema;
/**
* @property string $model Target model.
* @property string $model_id Id of the target model.
* @property string $event Event of this activity (ActivityLogsEventEnum).
* @property string $data Changes.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property string $model Target model.
* @property string $model_id Id of the target model.
* @property \App\Enums\ActivityLogs\ActivityLogsEventEnum $event Event of this activity (ActivityLogsEventEnum).
* @property array $data Changes.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
*
* @property-read \App\Models\User $user User BelongsTo relation.
*/
@ -82,19 +81,20 @@ class ActivityLog extends Model
$activity->model_id = $model->getKey();
$activity->event = $eventEnum;
$activity->data = static::getChangedColumns($activity, $model, $eventEnum);
$activity->created_at = Carbon::now();
// @phpstan-ignore-next-line
$activity->created_at = now();
$activity->saveOrFail();
}
/**
* Get old, new columns that has changed.
*
* @param \self $activity
* @param self $activity
* @param \Illuminate\Database\Eloquent\Model $model
* @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum
* @return array|null
* @return array
*/
public static function getChangedColumns(self $activity, Model $model, ActivityLogsEventEnum $eventEnum): array|null
public static function getChangedColumns(self $activity, Model $model, ActivityLogsEventEnum $eventEnum): array
{
$modelClassName = $activity->model_class;
/** Get type of each fields of the target model */
@ -103,16 +103,17 @@ class ActivityLog extends Model
$activity->model_id
)->first();
if ($targetModel != null) {
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
$targetModelTypes = collect($targetModel->toArray())
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed
->map(function ($field, $fieldIndex) use ($targetModel) {
return Schema::getColumnType($targetModel->getTable(), $fieldIndex);
})->toArray();
// phpcs:enable
/** Return old, new and type of values changed */
return ($eventEnum === ActivityLogsEventEnum::updated) ?
array_intersect_key($targetModelTypes, $model->getChanges()) : [];
}
/** Return old, new and type of values changed */
return ($eventEnum === ActivityLogsEventEnum::updated) ?
array_intersect_key($targetModelTypes, $model->getChanges()) : null;
return [];
}
// * RELATIONS
@ -122,7 +123,7 @@ class ActivityLog extends Model
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
{
return $this->belongsTo(User::class);
}

View file

@ -11,9 +11,11 @@ use Illuminate\Database\Eloquent\Model;
* @property string $title_part1 Title part 1.
* @property string $title_part2 Title part 2.
* @property string $title_part3 Title part 3.
* @property string $picture Path of the picture (640px * 390px).
* @property string|null $picture Path of the picture (640px * 390px).
* @property string $picture_alt Picture alt attribute.
* @property string $picture_title Picture title attribute.
* @property string $video_id The youtube or vimeo video Id.
* @property boolean $display_video Wether to display video or picture.
* @property string $presentation_title Presentation title.
* @property string $presentation_content WYSIWYG - Presentation content.
* @property-read \Carbon\Carbon $created_at Created date.
@ -38,6 +40,17 @@ class Homepage extends Model
'picture_title',
'presentation_title',
'presentation_content',
'video_id',
'display_video',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'display_video' => 'bool'
];
/**

View file

@ -9,6 +9,12 @@ use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
use Uwil\Seo\Laravel\Objects\SeoItem;
use Uwil\Seo\Laravel\Traits\SeoTrait;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
/**
* @property string $seo_title SEO title.
@ -31,10 +37,11 @@ use Illuminate\Support\Str;
* @property-read \Carbon\Carbon $created_at Created date.
* @property-read \Carbon\Carbon $updated_at Updated date.
*/
class News extends Model
class News extends Model implements SeoInterface, Sitemapable
{
use ActivityLog;
use HasFactory;
use SeoTrait;
/**
* The attributes that are mass assignable.
@ -198,4 +205,35 @@ class News extends Model
}
return false;
}
// * SEO TRAIT OVERLOAD
/**
* Transform to seoModel
*
* @return \Uwil\Seo\Laravel\Objects\SeoItem
*/
public function toSeoItem(): SeoItem
{
return new SeoItem(
\trans('models.classes.' . Str::singular($this->getTable())),
$this->seo_title,
$this->seo_description,
$this->published ?? false,
\route(\config('seo.bo_route_prefix') . $this->getTable() . '.edit', $this),
$this->published ? \route('fo.' . $this->getTable() . '.show', $this) : ''
);
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this;
$route = 'fo.news.show';
$params = ['slug' => $model->slug];
return SitemapHelper::generateUrl($route, $params, $model);
}
}

View file

@ -9,6 +9,12 @@ use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
use Uwil\Seo\Laravel\Objects\SeoItem;
use Uwil\Seo\Laravel\Traits\SeoTrait;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
/**
* @phpcs:disable Generic.Files.LineLength.TooLong
@ -41,10 +47,11 @@ use Illuminate\Support\Str;
* @property-read \App\Models\Range $range Range Belongs to relationship.
* @phpcs:enable
*/
class Product extends Model
class Product extends Model implements SeoInterface, Sitemapable
{
use ActivityLog;
use HasFactory;
use SeoTrait;
/**
* The attributes that are mass assignable.
@ -189,4 +196,35 @@ class Product extends Model
{
return $this->belongsToMany(Commitment::class);
}
// * SEO TRAIT OVERLOAD
/**
* Transform to seoModel
*
* @return \Uwil\Seo\Laravel\Objects\SeoItem
*/
public function toSeoItem(): SeoItem
{
return new SeoItem(
\trans('models.classes.' . Str::singular($this->getTable())),
$this->seo_title,
$this->seo_description,
$this->published ?? false,
\route(\config('seo.bo_route_prefix') . $this->getTable() . '.edit', $this),
$this->published ? \route('fo.' . $this->getTable() . '.show', $this) : ''
);
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this;
$route = 'fo.products.show';
$params = ['slug' => $model->slug];
return SitemapHelper::generateUrl($route, $params, $model);
}
}

View file

@ -9,6 +9,12 @@ use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Spatie\Sitemap\Contracts\Sitemapable;
use Spatie\Sitemap\Tags\Url;
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
use Uwil\Seo\Laravel\Objects\SeoItem;
use Uwil\Seo\Laravel\Traits\SeoTrait;
use Uwil\SitemapGenerator\Lib\SitemapHelper;
// phpcs:disable Generic.Files.LineLength.TooLong
/**
@ -22,7 +28,7 @@ use Illuminate\Support\Str;
* @property string $picture Path of the picture (466px * 440px).
* @property string $picture_alt Picture alt attribute.
* @property string $picture_title Picture title attribute.
* @property string $picture_productsheet Path of the picture of the product sheet (332px * 442px).
* @property string $picture_productsheet Path of the picture of the product sheet (680px * 300px).
* @property string $picture_productsheet_alt Picture alt attribute of the product sheet.
* @property string $picture_productsheet_title Picture title attribute of the product sheet.
* @property string $picture_thumbnail Path of the thumbnail (246px * 337px).
@ -36,10 +42,11 @@ use Illuminate\Support\Str;
* @property-read \App\Models\Products $products Products Has Many relationship.
*/
// phpcs:enable
class Range extends Model
class Range extends Model implements SeoInterface, Sitemapable
{
use ActivityLog;
use HasFactory;
use SeoTrait;
/**
* The attributes that are mass assignable.
@ -170,4 +177,35 @@ class Range extends Model
{
return $this->hasMany(Product::class);
}
// * SEO TRAIT OVERLOAD
/**
* Transform to seoModel
*
* @return \Uwil\Seo\Laravel\Objects\SeoItem
*/
public function toSeoItem(): SeoItem
{
return new SeoItem(
\trans('models.classes.' . Str::singular($this->getTable())),
$this->seo_title,
$this->seo_description,
$this->published ?? false,
\route(\config('seo.bo_route_prefix') . $this->getTable() . '.edit', $this),
$this->published ? \route('fo.' . $this->getTable() . '.show', $this) : ''
);
}
/**
* toSitemapTag method
*
* @return Url|string|array
*/
public function toSitemapTag(): Url | string | array
{
$model = $this;
$route = 'fo.ranges.show';
$params = ['slug' => $model->slug];
return SitemapHelper::generateUrl($route, $params, $model);
}
}

View file

@ -5,6 +5,7 @@ namespace App\Models;
use App\Lib\Helpers\FileStorageHelper;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
/**
* @property string $picture Path of the picture (120px * 120px).
@ -54,9 +55,14 @@ class Stamp extends Model
});
static::updated(function (self $model) {
FileStorageHelper::removeOldFile($model, 'picture');
Cache::forget('randomStampsFooter');
});
static::deleted(function (self $model) {
FileStorageHelper::removeFile($model, 'picture');
Cache::forget('randomStampsFooter');
});
static::created(function () {
Cache::forget('randomStampsFooter');
});
}
}

View file

@ -8,6 +8,9 @@ use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Uwil\Seo\Laravel\Interfaces\SeoInterface;
use Uwil\Seo\Laravel\Objects\SeoItem;
use Uwil\Seo\Laravel\Traits\SeoTrait;
/**
* @property \App\Enums\StaticPageType $type Page type (StaticPageEnum).
@ -19,10 +22,11 @@ use Illuminate\Support\Str;
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*/
class StaticPage extends Model
class StaticPage extends Model implements SeoInterface
{
use ActivityLog;
use HasFactory;
use SeoTrait;
/**
* The attributes that are mass assignable.
@ -57,7 +61,7 @@ class StaticPage extends Model
});
static::updating(function (self $model) {
// * ASSERTIONS
static::assertTitleIsUnique($model->title, $model->id);
self::assertTitleIsUnique($model->title, $model->id);
// * CHANGES
$model->slug = Str::slug($model->title);
@ -83,4 +87,22 @@ class StaticPage extends Model
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
}
// * SEO TRAIT OVERLOAD
/**
* Transform to seoModel
*
* @return \Uwil\Seo\Laravel\Objects\SeoItem
*/
public function toSeoItem(): SeoItem
{
return new SeoItem(
\trans('models.classes.' . Str::singular($this->getTable())),
$this->seo_title,
$this->seo_description,
true,
\route(\config('seo.bo_route_prefix') . $this->getTable() . '.edit', $this),
''
);
}
}

View file

@ -4,16 +4,15 @@ namespace App\Models;
use App\Enums\Users\RoleEnum;
use App\Traits\Models\ActivityLog;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
/**
* @property string $first_name Firstname.
* @property string $last_name Lastname.
* @property string $email Email.
* @property string $password Password.
* @property \App\Enums\Users\RoleEnum $role Role.
* @property \Illuminate\Support\Carbon $published Publication status.
* @property \Illuminate\Support\Carbon $published_at Publication status update date.
@ -37,7 +36,6 @@ class User extends Authenticatable
'first_name',
'last_name',
'email',
'password',
'role',
'published',
'published_at',
@ -49,7 +47,6 @@ class User extends Authenticatable
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
@ -59,9 +56,9 @@ class User extends Authenticatable
* @var array<string, string>
*/
protected $casts = [
'role' => RoleEnum::class,
'email_verified_at' => 'datetime',
'published' => 'boolean',
'role' => RoleEnum::class,
'last_connection' => 'date',
'published' => 'boolean',
];
/**
@ -72,7 +69,6 @@ class User extends Authenticatable
protected static function booted(): void
{
static::creating(function (self $model) {
static::updatePassword($model);
// * Prevent user elevation privileges
throw_if(
auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(),
@ -80,7 +76,6 @@ class User extends Authenticatable
);
});
static::updating(function (self $model) {
static::updatePassword($model);
if (optional(auth('backend')->user())->getKey() === $model->getKey()) {
\validator(
['published' => $model->published],
@ -95,31 +90,4 @@ class User extends Authenticatable
);
});
}
// * METHODS
/**
* Update password.
*
* @param self $model
* @return void
*/
private static function updatePassword(self $model): void
{
if (Hash::needsRehash($model->password)) {
$model->password = Hash::make($model->password);
}
}
// * RELATIONS
/**
* Activities logs One-to-many relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function activityLogs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ActivityLog::class);
}
}

View file

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

View file

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

View file

@ -4,6 +4,7 @@ namespace App\Providers;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider;
@ -20,6 +21,7 @@ class ViewServiceProvider extends ServiceProvider
$appInfos = Cache::remember('composer', 360, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
// Share composer.json infos.
if (isset($appInfos->name) and isset($appInfos->version)) {
View::share('cpsr_name', $appInfos->name);
@ -28,12 +30,33 @@ class ViewServiceProvider extends ServiceProvider
if (!app()->runningInConsole()) {
// * FORCE BOOTSTRAP PAGINATOR (or custom if in front)
// Early boot, request wont be filled as expected.
if (collect(explode('/', \request()->getPathInfo()))->get(1) === 'bo') {
// For the moment, we exclude the seo page because the new pagination system doesn't work in the package.
if (
collect(explode('/', \request()->getPathInfo()))->get(1) === 'bo' &&
collect(explode('/', \request()->getPathInfo()))->last() !== 'seo'
) {
Paginator::defaultView('bo.modules.pagination');
} else {
Paginator::useBootstrapFive();
}
}
// Get all the stamps that are pubished.
$stamps = Cache::remember('randomStampsFooter', 360, function () {
return DB::table('stamps')
->select('picture', 'picture_alt', 'picture_title')
->where('published', true)
->get();
});
/**
* Share a collection of 1 or 2 random stamps for footer.
* First, check if $stamps is not empty, if not,
* we count if the number of stamp in $stamps is greater than 2.
* Then, pick 1 or 2 stamp(s) depending on the number of stamps.
*/
View::share(
'randomStampsFooter',
$stamps->isNotEmpty() ? $stamps->random($stamps->count() >= 2 ? 2 : 1) : []
);
} //end if
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
/**
* Validate a vimeo ID regex
*
* @package App\Rules
*/
class IsValidVimeoId implements ValidationRule
{
/**
* Run the validation rule.
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string):\Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
// phpcs:enable
if (!self::validates($value)) {
$fail(trans('validation.vimeo_id'));
}
}
/**
* Validate a value
*
* @param mixed $value
* @return boolean
*/
public static function validates(mixed $value): bool
{
return Str::of($value)->isMatch('/^[0-9]{8,10}$/');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Str;
/**
* Validate a youtube ID regex
*
* @url https://webapps.stackexchange.com/questions/54443/format-for-id-of-youtube-video
* @package App\Rules
*/
class IsValidYoutubeId implements ValidationRule
{
/**
* Run the validation rule.
*
* @param string $attribute
* @param mixed $value
* @param \Closure(string):\Illuminate\Translation\PotentiallyTranslatedString $fail
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
// phpcs:enable
if (!self::validates($value)) {
$fail(trans('validation.youtube_id'));
}
}
/**
* Validate a value
*
* @param mixed $value
* @return boolean
*/
public static function validates(mixed $value): bool
{
return Str::of($value)->isMatch('/^[0-9A-Za-z_-]{10}[048AEIMQUYcgkosw]$/');
}
}

View file

@ -37,18 +37,16 @@ trait ChangesModelOrder
return DB::transaction(function () use ($request, $className, $model) {
/** @var \Illuminate\Database\Eloquent\Model|null */
$tmp = $className::where('order', $request->direction ? '<' : '>', $model->order)
$tmp = $className::where('order', $request->direction ? '<' : '>', $model->getOriginal('order'))
->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first();
if (!$tmp) {
return back()->with('error', trans('crud.messages.order_not_changed'));
}
$newOrder = $tmp->order;
$tmp->order = $model->order;
$model->order = $newOrder;
$tmp->saveOrFail();
$model->saveOrFail();
$newOrder = $tmp->getOriginal('order');
$tmp->update(['order' => $model->getOriginal('order')]);
$model->update(['order' => $newOrder]);
Session::flash('success', trans('crud.messages.order_changed'));
return back();

View file

@ -19,8 +19,7 @@ trait UpdateModelPublished
*/
public function changePublished(Model $model)
{
$model->published = !$model->published;
$model->saveOrFail();
$model->update(['published' => !$model->getOriginal('published')]);
return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved')));
}
}

View file

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

View file

@ -2,6 +2,7 @@
namespace App\Traits\Models;
use App\Models\ActivityLog as ModelsActivityLog;
use App\Observers\ModelObserver;
/**
@ -20,4 +21,16 @@ trait ActivityLog
{
static::observe(ModelObserver::class);
}
// * RELATIONS
/**
* Activities logs One-to-many relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function activityLogs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ModelsActivityLog::class);
}
}

View file

@ -10,7 +10,8 @@ use Illuminate\Support\Facades\Cache;
/**
* ! Use this only on limited elements, (not on tags, or products, or posts for ex.)
*
* @property private static array $cacheConditions Eloquent condition to cache elements (to be used in where statement).
* @property array<string,string|integer|float|boolean> $cacheConditions
* Eloquent condition to cache elements (to be used in where statement).
*/
trait IsCachedAsCollection
{

View file

@ -1,180 +0,0 @@
<?php
namespace App\Traits\Models;
use App\Enums\InputType;
use App\Lib\Helpers\FileStorageHelper;
use App\Lib\Helpers\ToolboxHelper;
trait MiniCrudModel
{
/**
* Fill the model with an array of attributes.
*
* @param array $attributes
* @return void
* @throws \Illuminate\Database\Eloquent\MassAssignmentException If fill prop is not allowed.
* @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows
*/
public function fill(array $attributes)
{
// phpcs:enable
parent::fill($attributes);
if (isset($attributes['data'])) {
$this->setData($attributes['data']);
}
}
/**
* Apply function on data typed text while on save event
*
* @return void
*/
public function minicrudCreating()
{
/** @var array<string,array> */
$data = $this->data;
foreach ($data as &$input) {
switch ($input['inputType']) {
case InputType::text->value:
$input['text'] = clean($input['text']);
$input['text'] = FileStorageHelper::storeModelFilesFromHtml($this, $input['text']);
break;
} //end switch
} //end foreach
$this->data = $data;
}
/**
* Apply function on data typed text while on update event
*
* @return void
*/
public function minicrudUpdating()
{
$this->minicrudCreating();
}
/**
* Apply function on data typed text while on updated event
*
* @return void
*/
public function minicrudUpdated()
{
/** @var array */
foreach ($this->data as $index => $input) {
switch ($input['inputType']) {
case InputType::text->value:
$original = ToolboxHelper::getOriginal($this, 'data');
FileStorageHelper::removeOldModelFilesFromHtml(
$input['text'],
$original[$index]['text'] ?? ''
);
break;
} //end switch
} //end foreach
}
/**
* Apply function on data typed text while on deleted event
*
* @return void
*/
public function minicrudDeleted()
{
/** @var array */
foreach ($this->data as $input) {
switch ($input['inputType']) {
case InputType::text->value:
FileStorageHelper::removeModelFilesOfHtml($input['text']);
break;
} //end switch
} //end foreach
}
/**
* Set a specific prop
*
* @param array $data
* @return void
*/
private function setData(array $data): void
{
$attrs = $this->attributes;
if (isset($attrs['data'])) {
unset($attrs['data']);
}
$this->setAttribute('data', $data);
}
/**
* Set a specific prop
*
* @param array $value
* @return void
*/
protected function setDataAttribute(array $value): void
{
$this->attributes['data'] = $this->castAttributeAsJson(
'data',
$this->serializeData($value)
);
}
/**
* Set a specific prop
*
* @param string|null $data
* @return array
*/
protected function getDataAttribute(string|null $data): array
{
return $this->fromJson($data ?? $this->attributes['data'] ?? '[]');
}
/**
* Serialize properties
*
* @param array $data
* @return array
* @throws \ErrorException If input type is no handled.
* @throws \Illuminate\Validation\ValidationException If url is a bo url.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
private function serializeData(array $data): array
{
foreach ($data as $o => $input) {
switch ($input['inputType']) {
case InputType::image->value:
$data[$o]['value'] = \preg_replace(
'/^\/|^/',
'',
FileStorageHelper::storeFile($this, $input['value'], true)
);
break;
case InputType::selectEnum->value:
case InputType::boolean->value:
case InputType::password->value:
case InputType::email->value:
case InputType::number->value:
case InputType::videoVimeo->value:
case InputType::videoYoutube->value:
case InputType::video->value:
break;
case InputType::string->value:
case InputType::text->value:
case InputType::url->value:
ToolboxHelper::assertNoInternalBoUrls('content', $input['value']);
break;
default:
throw new \ErrorException(sprintf(
'Unknown input Type %s',
$input['inputType']
));
} //end switch
} //end foreach
return $data;
}
}

View file

@ -45,9 +45,11 @@ trait HasPicture
* @param integer|null $minHeight
* @param integer|null $maxWith
* @param integer|null $maxHeight
* @param integer|null $size
* @param boolean $nullable
* @param integer|null $size
* @param string|null $requiredWith
* @param string|null $requiredWithout
* @param string|null $requiredIf
* @return array
*/
public function pictureRules(
@ -56,9 +58,11 @@ trait HasPicture
?int $minHeight = null,
?int $maxWith = null,
?int $maxHeight = null,
?int $size = null,
bool $nullable = false,
?int $size = null,
?string $requiredWith = null,
?string $requiredWithout = null,
?string $requiredIf = null,
): array {
$dimensions = (object)[
'minWith' => $minWith ?? $this->minWith,
@ -69,10 +73,12 @@ trait HasPicture
return [
$name => [
!$nullable ? 'required' : 'nullable',
(strlen($requiredWith)) ? 'required_with:' . $requiredWith : '',
($size) ? 'max:' . $size : '',
(!empty($requiredWith)) ? "required_with:{$requiredWith}" : '',
(!empty($requiredWithout)) ? "required_without:{$requiredWithout}" : '',
(!empty($requiredIf)) ? "required_if:{$requiredIf}" : '',
($size) ? "max:{$size}" : '',
function (string $attribute, $value, callable $fail) use ($dimensions) {
return ToolboxHelper::validatePicture($attribute, $value, $fail, $dimensions);
ToolboxHelper::validatePicture($attribute, $value, $fail, $dimensions);
}
]
];
@ -107,7 +113,7 @@ trait HasPicture
"{$name}Validate.*" => [
'required',
function (string $attribute, $value, callable $fail) use ($sizes) {
return $this->validatePicture($attribute, $value, $fail, $sizes);
ToolboxHelper::validatePicture($attribute, $value, $fail, $sizes);
}
]
];

View file

@ -1,31 +1,34 @@
{
"name": "uwil/agis",
"version": "1.0.0",
"version": "2.0.0",
"type": "project",
"description": "Agis website.",
"require": {
"php": "^8.2",
"diglactic/laravel-breadcrumbs": "^8.0",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.0",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"php": "^8.5",
"diglactic/laravel-breadcrumbs": "^10.1",
"guzzlehttp/guzzle": "^7.9",
"laravel/framework": "^12.0",
"laravel/tinker": "^3.0",
"laravel/ui": "^4.6",
"mews/purifier": "^3.4",
"sentry/sentry-laravel": "*"
"scaler-tech/laravel-saml2": "^2.5",
"sentry/sentry-laravel": "^4.25",
"uwil/laravel-admin-secure": "^6.0",
"uwil/k8s-laravel": "^3.0",
"uwil/seo-laravel": "^4.0",
"uwil/sitemap-generator": "^5.0"
},
"require-dev": {
"fakerphp/faker": "^1.9.1",
"kitloong/laravel-migrations-generator": "^6.8",
"krlove/eloquent-model-generator": "dev-l10-compatibility",
"laravel-lang/common": "^4.0",
"fakerphp/faker": "^1.23",
"larastan/larastan": "^3.0",
"laravel-lang/common": "^6.0",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
"nunomaduro/collision": "^7.0",
"phpunit/phpunit": "^10.0",
"laravel/sail": "^1.41",
"mockery/mockery": "^1.6",
"nunomaduro/collision": "^8.6",
"phpunit/phpunit": "^11.5",
"slevomat/coding-standard": "^8.10",
"spatie/laravel-ignition": "^2.0",
"thedoctor0/laravel-factory-generator": "^1.2"
"spatie/laravel-ignition": "^2.9"
},
"autoload": {
"psr-4": {
@ -36,8 +39,8 @@
},
"repositories": [
{
"type": "vcs",
"url": "https://github.com/laravel-shift/eloquent-model-generator.git"
"type": "composer",
"url": "https://lab.s-web.fr/api/v4/group/23/-/packages/composer/packages.json"
}
],
"autoload-dev": {

8185
composer.lock generated

File diff suppressed because it is too large Load diff

View file

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

View file

@ -96,6 +96,11 @@ return [
'name' => env('MAIL_FROM_NAME', 'Example'),
],
'contactform' => [
'receiver' => explode(',', env('MAIL_CONTACTFORM_RECEIVER', 'hello@example.com')),
'customerService' => explode(',', env('MAIL_CONTACTFORM_CUSTOMER_SERVICE_RECEIVER'))
],
/*
|--------------------------------------------------------------------------
| Markdown Mail Settings

397
config/saml2.php Normal file
View file

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

12
config/seo.php Normal file
View file

@ -0,0 +1,12 @@
<?php
return [
'bo_route_prefix' => 'bo.',
'blade' => 'bo.pages.seo.index',
'middlewares' => [
'web',
'admin-secure',
'auth:backend'
],
'guard' => 'backend'
];

View file

@ -38,4 +38,8 @@ return [
'version' => env('GOOGLE_RECAPTCHA_VERSION', "3") === "3" ? true : false
]
],
'matomo' => [
'site_id' => env('MATOMO_SITE_ID')
]
];

View file

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

View file

@ -20,35 +20,35 @@ class ProductFactory extends Factory
$nutritionnal_values = [
'energyKj' => [
'label' => 'Energie en Kj',
'label' => 'ÉNERGIE (kJ)',
'value' => fake()->randomFloat(2, 0, 70)
],
'energyKcal' => [
'label' => 'Energie en kcal',
'label' => 'ÉNERGIE (kcal)',
'value' => fake()->randomFloat(2, 0, 70)
],
'fat' => [
'label' => 'Matières grasses',
'label' => 'MATIÈRES GRASSES (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
'saturated-fat' => [
'label' => 'dont acides gras saturés',
'label' => 'ACIDES GRAS SATURÉS (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
'carbohydrate' => [
'label' => 'Glucides',
'label' => 'GLUCIDES (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
'sugar' => [
'label' => 'dont sucres',
'label' => 'SUCRES (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
'proteins' => [
'label' => 'Protéines',
'label' => 'PROTÉINES (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
'salt' => [
'label' => 'Sel',
'label' => 'SEL (g)',
'value' => fake()->randomFloat(2, 0, 70)
],
];

View file

@ -20,8 +20,6 @@ class UserFactory extends Factory
'first_name' => fake()->firstname(),
'last_name' => fake()->lastname(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
'remember_token' => Str::random(10),
'role' => \collect(RoleEnum::toArray())->random()->value,
'published' => $published,
@ -36,8 +34,8 @@ class UserFactory extends Factory
*/
public function unverified(): static
{
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
return $this->state(fn (array $attributes) => [
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
return $this->state(fn(array $attributes) => [
'email_verified_at' => null,
]);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

View file

@ -15,14 +15,14 @@ return new class extends Migration
{
Schema::create('convictionpages', function (Blueprint $table) {
$table->id();
$table->float('key_feed')->comment('Chiffre clé \'bien nourrir\'');
$table->float('key_earth_1')->comment('Chiffre clé \'respecter la terre\' 1');
$table->float('key_earth_2')->comment('Chiffre clé \'respecter la terre\' 2');
$table->float('key_together_1')->comment('Chiffre clé \'vivre ensemble\' 1');
$table->float('key_together_2')->comment('Chiffre clé \'vivre ensemble\' 2');
$table->float('key_together_3')->comment('Chiffre clé \'vivre ensemble\' 3');
$table->float('key_together_4')->comment('Chiffre clé \'vivre ensemble\' 4');
$table->float('key_territory')->comment('Chiffre clé \'intégrer son territoire\'');
$table->float('key_feed', 12)->comment('Chiffre clé \'bien nourrir\'');
$table->float('key_earth_1', 12)->comment('Chiffre clé \'respecter la terre\' 1');
$table->float('key_earth_2', 12)->comment('Chiffre clé \'respecter la terre\' 2');
$table->float('key_together_1', 12)->comment('Chiffre clé \'vivre ensemble\' 1');
$table->float('key_together_2', 12)->comment('Chiffre clé \'vivre ensemble\' 2');
$table->float('key_together_3', 12)->comment('Chiffre clé \'vivre ensemble\' 3');
$table->float('key_together_4', 12)->comment('Chiffre clé \'vivre ensemble\' 4');
$table->float('key_territory', 12)->comment('Chiffre clé \'intégrer son territoire\'');
$table->timestamps();
});
}

View file

@ -26,7 +26,7 @@ return new class extends Migration
$table->string('picture_alt')->comment('Attribut alt pour l\'image picture');
$table->string('picture_title')->comment('Attribut title pour l\'image picture');
$table->string('picture_productsheet')
->comment('Path de l\'image à afficher sur les fiches produits de cette gamme (332px * 442px)');
->comment('Path de l\'image à afficher sur les fiches produits de cette gamme (680px * 300px)');
$table->string('picture_productsheet_alt')
->comment('Attribut alt de l\'image à afficher sur les fiches produits');
$table->string('picture_productsheet_title')

View file

@ -0,0 +1,43 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->string('picture')->nullable()->change()
->comment('Path de l\'image de la page d\'accueil (640px * 390px)');
$table->string('youtube_video_id')->after('picture_title')
->nullable()
->default(null)
->comment('L\ídentifiant de la vidéo Youtube');
$table->boolean('display_video')->after('youtube_video_id')
->default(false)
->comment('Afficher la video ou l\'image');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->string('picture')->change()
->comment('Path de l\'image de la page d\'accueil (640px * 390px)');
$table->dropColumn('youtube_video_id');
$table->dropColumn('display_video');
});
}
};

View file

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->renameColumn('youtube_video_id', 'video_id');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('homepages', function (Blueprint $table) {
$table->renameColumn('video_id', 'youtube_video_id');
});
}
};

View file

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('email_verified_at');
$table->dropColumn('password');
$table->date('last_connection')->nullable()->after('role');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->timestamp('email_verified_at')->nullable()->after('email');
$table->string('password')->after('email_verified_at');
$table->dropColumn('last_connection');
});
}
};

View file

@ -21,7 +21,7 @@ class RangeSeeder extends Seeder
'../database/factories/assets/ranges/default-picture-466x440.png'
)));
$range->picture_productsheet = FileStorageHelper::storeFile($range, new \SplFileInfo(\resource_path(
'../database/factories/assets/ranges/default-picture-332x442.png'
'../database/factories/assets/ranges/default-picture-680x300.png'
)));
$range->picture_thumbnail = FileStorageHelper::storeFile($range, new \SplFileInfo(\resource_path(
'../database/factories/assets/ranges/default-picture-246x337.png'

View file

@ -36,10 +36,10 @@ class StaticPageSeeder extends Seeder
'type' => StaticPageType::legals,
],
[
'seo_title' => 'Seo title pour la page mentions légales',
'seo_description' => 'Seo description pour la page mentions légales',
'title' => 'Mentions légales',
'slug' => Str::slug('Mentions légales'),
'seo_title' => 'Seo title pour la page mentions légales et CGU',
'seo_description' => 'Seo description pour la page mentions légales et CGU',
'title' => 'Mentions légales et CGU',
'slug' => Str::slug('Mentions légales et CGU'),
'type' => StaticPageType::legals,
'content' => \clean(\fake()->randomHtml(6, 7)),
]

177
manifest.yaml Normal file
View file

@ -0,0 +1,177 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ${CI_PROJECT_PATH_SLUG}-config-map
data:
APP_NAME: "${APP_APP_NAME}"
APP_ENV: "${APP_APP_ENV}"
APP_KEY: "${APP_APP_KEY}"
APP_DEBUG: "${APP_APP_DEBUG}"
APP_URL: "https://${REDIRECT}${APP_DOMAIN}"
LOG_CHANNEL: "k8s"
DB_HOST: "${APP_DB_HOST}"
DB_DATABASE: "${APP_DB_DATABASE}"
DB_USERNAME: "${APP_DB_USERNAME}"
DB_PASSWORD: "${APP_DB_PASSWORD}"
MAIL_HOST: "${APP_MAIL_HOST}"
MAIL_PORT: "${APP_MAIL_PORT}"
MAIL_USERNAME: "${APP_MAIL_USERNAME}"
MAIL_PASSWORD: "${APP_MAIL_PASSWORD}"
MAIL_ENCRYPTION: "${APP_MAIL_ENCRYPTION}"
MAIL_FROM_ADDRESS: "${APP_MAIL_FROM_ADDRESS}"
MAIL_FROM_NAME: "${APP_APP_NAME}"
MAIL_CONTACTFORM_RECEIVER: "${APP_MAIL_CONTACTFORM_RECEIVER}"
MAIL_CONTACTFORM_CUSTOMER_SERVICE_RECEIVER: "${APP_MAIL_CONTACTFORM_CUSTOMER_SERVICE_RECEIVER}"
LDC_WEB_API_KEY: "${APP_LDC_WEB_API_KEY}"
GOOGLE_RECAPTCHA_PRIVATE: "${APP_GOOGLE_RECAPTCHA_PRIVATE}"
GOOGLE_RECAPTCHA_PUBLIC: "${APP_GOOGLE_RECAPTCHA_PUBLIC}"
GOOGLE_RECAPTCHA_VERSION: "${APP_GOOGLE_RECAPTCHA_VERSION}"
SENTRY_LARAVEL_DSN: "${APP_SENTRY_LARAVEL_DSN}"
MATOMO_SITE_ID: "${APP_MATOMO_SITE_ID}"
SAML2_SP_CERT_x509: "${APP_SAML2_SP_CERT_x509}"
SAML2_SP_CERT_PRIVATEKEY: "${APP_SAML2_SP_CERT_PRIVATEKEY}"
SAML2_CONTACT_TECHNICAL_EMAIL: "${APP_SAML2_CONTACT_TECHNICAL_EMAIL}"
SAML2_CONTACT_SUPPORT_EMAIL: "${APP_SAML2_CONTACT_SUPPORT_EMAIL}"
SAML2_DEFAULT_TENANT_UUID: "${APP_SAML2_DEFAULT_TENANT_UUID}"
UWIL_ADMIN_SECURE_CIDR_LIST: "${APP_GLOBAL_UWIL_ADMIN_SECURE_CIDR_LIST}"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: ${CI_PROJECT_PATH_SLUG}-pvc-storage
spec:
storageClassName: "csi-cinder-classic"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: $APP_PVC_SIZE
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${CI_PROJECT_PATH_SLUG}-deployment
spec:
replicas: 1
selector:
matchLabels:
app: ${CI_PROJECT_PATH_SLUG}-app
template:
metadata:
labels:
app: ${CI_PROJECT_PATH_SLUG}-app
spec:
containers:
- name: ${CI_PROJECT_PATH_SLUG}-container
image: $IMAGE_TAG
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
resources:
limits:
cpu: "${APP_CI_CPU_LIMIT}"
memory: "${APP_CI_MEMORY_LIMIT}"
requests:
cpu: "${APP_CI_CPU_REQUEST}"
memory: "${APP_CI_MEMORY_REQUEST}"
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
imagePullSecrets:
- name: docker-registry-credentials
securityContext:
runAsUser: 82
runAsGroup: 82
fsGroup: 82
fsGroupChangePolicy: "OnRootMismatch"
volumes:
- name: storage
persistentVolumeClaim:
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
---
apiVersion: v1
kind: Service
metadata:
name: ${CI_PROJECT_PATH_SLUG}-service
spec:
selector:
app: ${CI_PROJECT_PATH_SLUG}-app
ports:
- port: 80
targetPort: 80
---
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: ${CI_PROJECT_PATH_SLUG}-ingress-route
spec:
entryPoints:
- web
- websecure
routes:
- 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
tls:
secretName: ${CRT_TLS_NAME}
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: ${CI_PROJECT_PATH_SLUG}-cronjob
spec:
schedule: "* * * * *"
successfulJobsHistoryLimit: 1
failedJobsHistoryLimit: 1
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- ${CI_PROJECT_PATH_SLUG}-app
topologyKey: "kubernetes.io/hostname"
namespaces: [$KUBE_NAMESPACE]
containers:
- name: ${CI_PROJECT_PATH_SLUG}-scheduled
image: $IMAGE_TAG
imagePullPolicy: IfNotPresent
command:
- "php"
args:
- "artisan"
- "schedule:run"
envFrom:
- configMapRef:
name: ${CI_PROJECT_PATH_SLUG}-config-map
volumeMounts:
- name: storage
mountPath: /app/srcs/storage
imagePullSecrets:
- name: docker-registry-credentials
volumes:
- name: storage
persistentVolumeClaim:
claimName: ${CI_PROJECT_PATH_SLUG}-pvc-storage
securityContext:
runAsUser: 82
runAsGroup: 82
fsGroup: 82
fsGroupChangePolicy: "OnRootMismatch"
restartPolicy: OnFailure

3226
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
{
"private": true,
"name": "srcs",
"scripts": {
"vite": "vite --clearScreen false",
"prod": "vite build --mode production",
@ -15,12 +16,11 @@
"devDependencies": {
"@babel/core": "^7.22.11",
"@babel/types": "^7.21.2",
"@fortawesome/fontawesome-svg-core": "latest",
"@fortawesome/free-brands-svg-icons": "latest",
"@fortawesome/free-regular-svg-icons": "latest",
"@fortawesome/free-solid-svg-icons": "latest",
"@fortawesome/vue-fontawesome": "^3.0.3",
"@ldc-web/cookie-notice": "^3.0.9",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.5",
"@ldc-web/cookie-notice": "^6.0.3",
"@sipec/vue3-tags-input": "^3.0.4",
"@types/bootstrap": "^5.2.7",
"@types/node": "^20.8.2",
@ -28,11 +28,10 @@
"@typescript-eslint/eslint-plugin": "^5.62.0",
"@typescript-eslint/parser": "^5.36.2",
"@vitejs/plugin-basic-ssl": "^1.0.1",
"@vitejs/plugin-legacy": "latest",
"@vitejs/plugin-vue": "latest",
"@vitejs/plugin-vue": "^4.6.2",
"@vue/compiler-sfc": "latest",
"@vue/eslint-config-typescript": "latest",
"@vue/tsconfig": "latest",
"@vue/tsconfig": "^0.4.0",
"axios": "^1.5.1",
"bootstrap": "^5.3.2",
"chart.js": "^4.4.0",
@ -40,33 +39,37 @@
"eslint": "latest",
"eslint-import-resolver-alias": "^1.1.2",
"eslint-plugin-vue": "latest",
"laravel-vite-plugin": "latest",
"laravel-vite-plugin": "^0.8.1",
"postcss-discard-comments": "^6.0.0",
"prettier": "latest",
"print-js": "^1.6.0",
"resolve-url-loader": "^5.0.0",
"rollup-plugin-copy": "^3.5.0",
"sass": "latest",
"slugify": "^1.6.6",
"stylelint": "latest",
"stylelint": "^15.11.0",
"stylelint-config-idiomatic-order": "github:ream88/stylelint-config-idiomatic-order",
"stylelint-config-sass-guidelines": "latest",
"stylelint-config-standard-scss": "latest",
"stylelint-scss": "latest",
"stylelint-config-sass-guidelines": "^10.0.0",
"stylelint-config-standard-scss": "^11.1.0",
"stylelint-scss": "^5.3.2",
"sweetalert2": "^11.7.31",
"swiper": "^10.2.0",
"swiper": "^11.0.5",
"terser": "latest",
"toastify-js": "^1.12.0",
"ts-loader": "^9.3.1",
"typescript": ">=3.3.1 <5.2.0",
"typescript-eslint": "latest",
"typescript": "latest",
"vite": "^4.5.1",
"vite-plugin-babel": "latest",
"vite-plugin-checker": "latest",
"vite-plugin-eslint": "latest",
"vite-plugin-stylelint": "latest",
"vite": "^4.0.0",
"vue-eslint-parser": "^9.1.0",
"vue-loader": "^16.8.3",
"vue": "latest",
"vue-tsc": "1.6.0"
"vue-eslint-parser": "^9.3.2",
"vue-loader": "^17.3.1",
"vue-tsc": "1.8.11"
},
"dependencies": {
"meow": "^9.0.0"
}
}

10
phpstan.neon Normal file
View file

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

13
production-resources.yaml Normal file
View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 15 KiB

View file

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

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