Compare commits

...
Sign in to create a new pull request.

415 commits

Author SHA1 Message Date
Noah Lancien
a55af08121 chore: bump version to 4.2.1 2026-09-30 11:51:48 +02:00
Noix
05b51a7188 Merge branch 'fix-issue-59' into 'master'
fix: Fixes issue #59 could not delete a user

See merge request ldc-web/projets/gen-game-web!73
2026-09-30 09:49:27 +00:00
Noix
7809f430ef Merge branch 'fix-issie-60' into 'master'
fix: Fixes #60 model user was not properly resolved on status chage

See merge request ldc-web/projets/gen-game-web!72
2026-09-30 09:49:17 +00:00
Noix
b240c614fc Merge branch 'issue-61' into 'master'
feat: ✨ set winning proofs optionnal

See merge request ldc-web/projets/gen-game-web!74
2026-09-29 13:27:02 +00:00
Noah Lancien
0e6b074794 chore: bump version to 4.2.0 2026-09-29 15:26:06 +02:00
Alexis Gousseau
6a6781831b
feat: ✨ set winning proofs optionnal 2026-09-28 16:17:07 +02:00
Jeremy Munsch
956ae8693f
fix: Fixes issue #59 could not delete a user 2026-09-18 16:11:03 +02:00
Jeremy Munsch
79026a20d6
fix: Fixes #60 model user was not properly resolved on status chage 2026-09-18 15:57:56 +02:00
Alexis Gousseau
7f8284e300
fix: 🐛 use node types in ts config 2026-09-09 17:19:49 +02:00
Noix
342d2adced Merge branch 'update' into 'master'
Update

See merge request ldc-web/projets/gen-game-web!71
2026-08-07 06:35:19 +00:00
Alexis Gousseau
d462bc5f1c
chore: 🔖 release v4.1.2 2026-08-06 16:21:58 +02:00
Alexis Gousseau
4e16b2e160
feat: ✨ add commit lint from husky configuration 2026-08-06 16:21:57 +02:00
Alexis Gousseau
0c0cd0d476
fix: 🐛 remove faker in mandatory seeder 2026-08-06 16:21:53 +02:00
Noah Lancien
157bcb491f chore: npm update - 4.1.1 2026-08-05 12:03:55 +02:00
Noix
3c1d4fe84c Merge branch 'vite8-deps-bump-and-fixes' into 'master'
chore: update ESLint and Stylelint configurations

See merge request ldc-web/projets/gen-game-web!70
2026-08-04 14:54:02 +00:00
a27f0bf4d8
feat(ckeditor): integrate CKEditor 5 with custom plugins and styles
- Added CKEditor 5 integration with custom plugins for enhanced functionality.
- Implemented a unique ID generator for component instances.
- Created a dedicated SCSS file for CKEditor styles to ensure proper styling.
- Updated the CkEditorComponent to utilize the new setup and improve data handling.
2026-07-23 16:46:41 +02:00
87dbf384ca
chore(ts): import CookieNotice and add to Window interface
Added CookieNotice import and included it in the global Window interface for better type support.
2026-07-23 16:01:10 +02:00
d3f47bd120
fix(vite.config): update theme path for hot module replacement
Ensure hot module replacement only detects changes in the active theme.
2026-07-23 16:01:04 +02:00
4920902222
chore(tsconfig): update include paths for TypeScript
Added additional paths to include for TypeScript compilation.
2026-07-23 16:00:17 +02:00
d489fa29f8
chore: update ESLint and Stylelint configurations
Refine ESLint and Stylelint commands for better specificity.
2026-07-23 15:59:51 +02:00
0e0854ff3f
refactor: update Vue components and TypeScript modules 2026-07-23 15:03:41 +02:00
c40b80dd79
style: update back-office and base SCSS styles 2026-07-23 15:03:40 +02:00
eaa9303445
fix: update middlewares and front-office routes 2026-07-23 15:03:40 +02:00
ae2c0f146c
build: update Vite config for HMR and favicons, and update dependencies 2026-07-23 15:03:40 +02:00
7d4fdb3011
chore: update ESLint, Stylelint and PHPCS configurations 2026-07-23 15:03:40 +02:00
d693885a90
chore(jobs): disable flawed DeleteOldCustomers job 2026-07-23 15:03:39 +02:00
09ca3ac738
fix(fo): fix undefined variable in SubmitWinningProofRequest and wrong route in CrmRepository 2026-07-23 15:03:39 +02:00
12e6d2adf5
fix(crm): use soft deletes and fix synchronization issues during registration 2026-07-23 15:03:39 +02:00
37278195e6
fix(discount): align propertiesList with Discount::make() parameter order 2026-07-23 14:40:56 +02:00
696132eddc
fix: 🐛 reorder Discount::make() params in SettingsController 2026-07-23 13:50:04 +02:00
Jérémy
379cff88c2 Merge branch 'update' into 'master'
Update v4.0.9

See merge request ldc-web/projets/gen-game-web!68
2026-07-23 11:30:39 +00:00
Jérémy
706577c606 Merge branch 'master' into 'update'
# Conflicts:
#   app/Models/Customer.php
2026-07-23 11:30:07 +00:00
Jérémy
7f9fb9d414 Merge branch '55-runtimeexception-this-cannot-be-done-a-cause-de-mesbr' into 'master'
Resolve "RuntimeException: This cannot be done à cause de MesBr"

See merge request ldc-web/projets/gen-game-web!69
2026-07-23 11:29:10 +00:00
e0b60f65da
refactor: remove unnecessay cast to allow futur strict type and Arr::integer usage 2026-07-23 13:25:41 +02:00
0ec5ab7f8c
fix(customer): allow deleting customers locally if the account is not activated. 2026-07-23 13:18:43 +02:00
Alexis Gousseau
52b183bd9d
chore: 🔖 release v4.0.9 2026-07-22 13:13:58 +02:00
Alexis Gousseau
24199b3ac6
fix: 🐛 reorder params of Discount make() method, its causes type error 2026-07-22 13:13:34 +02:00
Alexis Gousseau
37530c5826
fix: 🌐 update some translations 2026-07-22 13:01:02 +02:00
Jérémy
d5cb4c7d78 Merge branch 'fix-front-compilation' into 'master'
chore: add missing meow dependency

See merge request ldc-web/projets/gen-game-web!67
2026-07-17 13:29:45 +00:00
Jeremy Munsch
9d0d2dc73b
chore: add missing meow dependency 2026-07-17 15:27:49 +02:00
Noah Lancien
2e23da12e1 feat: ✨ Added some countries translations - 4.0.8 2026-05-21 12:43:44 +02:00
Noix
497786de7f Merge branch 'fix-settings' into 'master'
fix 🐛 fix country selection bug

See merge request ldc-web/projets/gen-game-web!66
2026-05-12 08:39:13 +00:00
Corentin Huet
fec87ad399 fix 🐛 fix country selection bug 2026-05-07 15:05:46 +02:00
Noix
01b7f09fac Merge branch 'swiperjs' into 'master'
feat: 📦 install swiperjs

See merge request ldc-web/projets/gen-game-web!65
2026-04-27 15:20:09 +00:00
Corentin Huet
cacc53e8a0 feat: 📦 install swiperjs 2026-04-27 14:58:09 +02:00
Noix
2b333d5c14 Merge branch 'translate-de' into 'master'
feat: ✨ add de translations

See merge request ldc-web/projets/gen-game-web!62
2026-04-17 10:56:27 +00:00
Corentin Huet
5a50f04f4f chore: ⬆️ upgrade ggw version 2026-04-17 12:52:03 +02:00
Corentin Huet
e572743f51 feat: ✨ add missing English translations and translate all user-facing flash messages and translate all mail subjects, greetings and contents 2026-04-17 12:52:00 +02:00
Corentin Huet
c77a587bda feat: ✨ add de translations 2026-04-17 12:50:36 +02:00
Noix
8cb0219c37 Merge branch '52-fix-verification' into 'master'
fix: 🐛 remove auth middleware from email verification

See merge request ldc-web/projets/gen-game-web!64
2026-04-17 10:49:29 +00:00
Noix
cc4e4e72e6 Merge branch 'bump-crm-client' into 'master'
chore: 📦 bump crm client from v2 to v4

See merge request ldc-web/projets/gen-game-web!63
2026-04-17 10:49:19 +00:00
Corentin Huet
08df396de5 chore: 📦 bump crm client from v2 to v4 2026-04-13 12:33:16 +02:00
Corentin Huet
087a2c08fb fix: 🐛 remove auth middleware from email verification 2026-04-10 11:25:09 +02:00
noah.lancien
25ecbca185 Merge branch 'fix-re7-menu' into 'master'
fix: 🐛 replace discount model with discount lib

See merge request ldc-web/projets/gen-game-web!61
2026-01-14 15:16:36 +00:00
Corentin Huet
f8a4fa816c fix: 🐛 replace discount model with discount lib 2026-01-14 16:14:44 +01:00
Noah Lancien
1443080cd3 fix: remove admin secure package 2026-01-13 11:48:19 +01:00
Noah Lancien
5b06bc2c6c fix: remove admin-secure middleware (waiting for multi-tenant update) 2026-01-13 11:02:23 +01:00
noah.lancien
c47cf8d29a Merge branch 'issue-50' into 'master'
fix: add example.* to ignored routes

See merge request ldc-web/projets/gen-game-web!59
2025-11-13 11:09:39 +00:00
noah.lancien
948cc09443 Merge branch 'master' into 'issue-50'
# Conflicts:
#   composer.json
2025-11-13 11:07:22 +00:00
noah.lancien
25cafe1a1c Merge branch 'mesbr-update' into 'master'
feat: update br management

See merge request ldc-web/projets/gen-game-web!60
2025-11-13 11:00:29 +00:00
Noah Lancien
7e02516b65 chore: bump version to 4.0.0 - update br management 2025-11-13 11:57:26 +01:00
Noah Lancien
7bddbdd356 fix: set correct country in the request when updating a customer 2025-11-12 09:18:05 +01:00
Vincent BRIDIER
ac4bb01c26 Du saaaaaale comme never (pas demi) 2025-11-05 19:12:03 +01:00
Noah Lancien
7240c5e35d fix: review feedback 2025-10-29 12:42:18 +01:00
Noah Lancien
6a5f07ae58 feat: update br management 2025-10-27 16:23:02 +01:00
Corentin Huet
af4ac8883e fix: add example.* to ignored routes 2025-09-15 10:22:25 +02:00
noah.lancien
2b936e308e Merge branch 'issue-287' into 'master'
fix: use ternary operator to resolve bug on winner list page

See merge request ldc-web/projets/gen-game-web!58
2025-09-09 14:50:53 +00:00
Corentin Huet
cf41ecb748 fix: use ternary operator to resolve bug on winner list page 2025-09-08 13:51:45 +02:00
karl.muller
6bb7e926fa Merge branch 'intToSerial' into 'master'
fix: 🐛 displaying the value of the unique code instead of its ID #48

See merge request ldc-web/projets/gen-game-web!56
2025-09-04 13:05:28 +00:00
Alexis Gousseau
fcc2d5b519
chore: 🔖 release v3.1.4 2025-09-04 10:32:46 +02:00
Alexis Gousseau
65f4ce155f
fix: 🐛 displaying the value of the unique code instead of its ID #48 2025-09-04 10:32:13 +02:00
karl.muller
6ae26c6a23 Merge branch 'error-email' into 'master'
fix: the unique_code variable is now optional in admin emails

See merge request ldc-web/projets/gen-game-web!55
2025-09-01 09:58:43 +00:00
Corentin Huet
144c74e422 fix: the unique_code variable is now optional in admin emails 2025-09-01 09:48:06 +02:00
Vincent BRIDIER
290f9aa570 Merge branch 'error-sentry' into 'master'
fix: undefined variable settingsGlobal

See merge request ldc-web/projets/gen-game-web!54
2025-08-26 06:51:16 +00:00
Corentin HUET
8b6943df28 fix: undefined variable settingsGlobal on mail 2025-08-26 06:51:16 +00:00
Vincent BRIDIER
2c1c3eb2e6 Merge branch 'strtolower-email' into 'master'
fix: 🐛 save customer email attribute in lower case #46

See merge request ldc-web/projets/gen-game-web!50
2025-08-18 15:18:03 +00:00
Alexis Gousseau
688565c1ea fix: 🐛 save customer email attribute in lower case #46 2025-08-18 15:18:03 +00:00
Vincent BRIDIER
4bdb75f866 Merge branch 'countries-issue-42' into 'master'
feat: ✨ set authorized countries in settings #42

See merge request ldc-web/projets/gen-game-web!49
2025-08-18 15:13:12 +00:00
Vincent BRIDIER
317c7d95d0
bump version 2025-08-14 17:02:42 +02:00
Vincent BRIDIER
d5d60f98e9
Merge branch 'master' into codes-issue-43 2025-08-14 16:50:02 +02:00
Vincent BRIDIER
419573145c Merge branch 'codes-issue-43' into 'master'
feat: ✨ update codes types (unique and ean) #43

See merge request ldc-web/projets/gen-game-web!48
2025-08-14 14:12:13 +00:00
Alexis Gousseau
1a4d0a03ec feat: ✨ update codes types (unique and ean) #43 2025-08-14 14:12:13 +00:00
Vincent BRIDIER
2339684802 Merge branch 'participation-exhausted' into 'master'
feat: added route and link in bo for participation-exhausted

See merge request ldc-web/projets/gen-game-web!51
2025-08-07 14:53:28 +00:00
Corentin Huet
bf92d8542b Merge branch 'master' of lab.s-web.fr:ldc-web/projets/gen-game-web into participation-exhausted 2025-08-07 16:37:11 +02:00
Corentin Huet
5a361da75c feat: added route and link in bo for participation-exhausted 2025-08-07 16:27:40 +02:00
Vincent BRIDIER
48260952a4
feat: Update EAN Code type 2025-08-07 14:45:16 +02:00
Alexis Gousseau
dd97b1d6dc
chore: 🔖 release v2.6.0 2025-08-06 12:28:17 +02:00
Alexis Gousseau
2b8fd6bbe4
fix: 🚨 resolve phpcs/phpstan errors 2025-08-06 12:28:16 +02:00
Alexis Gousseau
e6839a71c8
feat: ✨ set authorized countries in settings #42 2025-08-06 12:28:00 +02:00
Vincent BRIDIER
3ee3b5c69a Merge branch 'edit-gitignore' into 'master'
chore: update .gitignore

See merge request ldc-web/projets/gen-game-web!47
2025-08-01 16:21:03 +00:00
Corentin Huet
616593cdb8 chore: update .gitignore 2025-08-01 18:15:41 +02:00
Vincent BRIDIER
8144bc9c62 Merge branch 'matines-40ans' into 'master'
feat: ✨ add participation route to web-re7

See merge request ldc-web/projets/gen-game-web!46
2025-07-30 14:21:03 +00:00
Nicolas Demangeat
ee22082f59 fix: 🔖 update version tag 2025-07-30 16:19:25 +02:00
Nicolas Demangeat
e719a96705 feat: ✨ add button in submenu Re7 in BO to see the participate page 2025-07-30 16:15:20 +02:00
Nicolas Demangeat
3ae492f193 feat: ✨ add participation route to the re7 route 2025-07-28 14:31:08 +02:00
noah.lancien
ff86605bc8 Merge branch '39-belgium-issues' into 'master'
Resolve "Retour récla export - problème sur la soumission des informations complémentaires"

See merge request ldc-web/projets/gen-game-web!43
2025-07-16 10:28:38 +00:00
Noah Lancien
1f1f95afcf chore: bump version to 2.4.9 + compsoer update to fix many CVEs 2025-07-16 12:05:09 +02:00
Noah Lancien
52b5132021 fix: use max instead of count in CodeController 2025-07-16 12:04:21 +02:00
Noah Lancien
0f1c71272b fix: country code was broken when a user submitted a code so the validation rules were always in FR 2025-07-16 12:03:54 +02:00
noah.lancien
37a75e8680 Merge branch 'dont-update-crm-email' into 'master'
fix: don't send email and password when updating a customer

See merge request ldc-web/projets/gen-game-web!42
2025-07-10 07:36:32 +00:00
Noah Lancien
bbfba0bda1 fix: don't send email and password when updating a customer 2025-07-10 08:17:43 +02:00
noah.lancien
a5bbbfdcef Merge branch 'migrate-to-mysql' into 'master'
fix: update migrations to make it compatible with mysql databases

See merge request ldc-web/projets/gen-game-web!41
2025-06-20 08:34:47 +00:00
Noah Lancien
2edb4df669 fix: update migrations to make it compatible with mysql databases 2025-06-19 17:08:36 +02:00
Vincent BRIDIER
d649bc1eb1 Merge branch 'issue-38' into 'master'
feat/fix: ✨ 🐛 change Cache::get to Cache::remember in Middleware...

See merge request ldc-web/projets/gen-game-web!40
2025-06-04 08:33:54 +00:00
Nicolas Demangeat
633c33eead fix: increase cache save time to 84600sec 2025-06-04 10:32:32 +02:00
Nicolas Demangeat
25147ac27e feat/fix: ✨🐛 change Cache::get to Cache::remember in Middleware CheckThemeVersion and increase the cache time 360sec to 3600sec 2025-06-04 10:12:09 +02:00
Vincent BRIDIER
fe99d75513 Merge branch 'issue-29' into 'master'
feat: 🗃️ add a migration to remove the model_ranges table in database

See merge request ldc-web/projets/gen-game-web!39
2025-05-27 08:19:19 +00:00
Nicolas Demangeat
7b08d0e1c8 feat: 🗃️ add a migration to remove the model_ranges table in database 2025-05-27 10:14:21 +02:00
karl.muller
1dd81a7d30 Merge branch 'issue-34-without-array' into 'master'
fix: 🐛 error with postal and phone code in create account request

See merge request ldc-web/projets/gen-game-web!38
2025-05-23 10:22:35 +00:00
Karl
741aabed60 fix: 🐛 error with postal and phone code in create account request 2025-05-23 12:16:18 +02:00
Vincent BRIDIER
37a767c591 Merge branch 'fix-mesbr-hashcode' into 'master'
fix: use hashcode instead of customer_id to update mesbr basket

See merge request ldc-web/projets/gen-game-web!37
2025-04-01 10:12:36 +00:00
Noah Lancien
a28885f74e fix: use hashcode instead of customer_id to update mesbr basket 2025-04-01 11:45:01 +02:00
Vincent BRIDIER
dc559dc1e7
Add Les Fermiers SNV Thème 2024-08-19 17:30:17 +02:00
Vincent BRIDIER
62e28d1f3e
Bump theme version 2024-07-03 13:37:30 +02:00
Vincent BRIDIER
6445e36c67
Bump Cookie Notice 6.0.3 2024-07-03 13:36:44 +02:00
Vincent BRIDIER
6a4fa79cc4 Merge branch 'fix-activity-logs' into 'master'
fix: updated activity-logs from le-gaulois

See merge request ldc-web/projets/gen-game-web!35
2024-07-02 12:32:24 +00:00
Noah Lancien
c8e0f71593 fix: updated activity-logs from le-gaulois 2024-07-02 12:56:11 +02:00
Vincent BRIDIER
515e36ce06 Merge branch 'init-jeu-galettes-2024' into 'master'
feat: customer feedback 1

See merge request ldc-web/projets/gen-game-web!31
2024-06-26 16:09:13 +00:00
Sebastien Hattry
d440842524 Bump v2.4.1 2024-06-26 17:52:10 +02:00
Sebastien Hattry
30007dbf1f fix: phpcs warning 2024-06-26 17:51:20 +02:00
Sebastien Hattry
52d5c2908f Add min dimensions 2024-06-26 17:51:20 +02:00
Sebastien Hattry
010aeb0221 feat: customer feedback 1 2024-06-26 17:51:20 +02:00
Vincent BRIDIER
2945692413 Merge branch 'fix-winning-proof-mail' into 'master'
fix: 🐛 WinningProofAdminMail was sending wrong EAN code

See merge request ldc-web/projets/gen-game-web!28
2024-06-26 14:39:36 +00:00
Vincent BRIDIER
4223691742 Merge branch 'issue-28' into 'master'
add activitylog on each models

See merge request ldc-web/projets/gen-game-web!26
2024-06-26 14:39:00 +00:00
Vincent BRIDIER
dcf2ad6079 Merge branch 'conditionnal-postal-code' into 'master'
fix: Added a condition to validate the postal code according to the country

Closes ldc-web/projets/gen-game-web-instances/le-gaulois-tdf-2024#1

See merge request ldc-web/projets/gen-game-web!34
2024-06-25 16:09:15 +00:00
Vincent BRIDIER
cbd674eaa1 Merge branch 'cookie-notice-v6' into 'master'
feat: Bumped cookie-notice to v6

See merge request ldc-web/projets/gen-game-web!33
2024-06-25 16:08:58 +00:00
Noah Lancien
854a2c7c47 fix: cleaned postal code condition 2024-06-25 17:39:20 +02:00
Noah Lancien
f4c09329a1 fix: Added a condition to validate the postal code according to the country 2024-06-25 16:50:56 +02:00
Noah Lancien
e8ea255626 feat: Bumped cookie-notice to v6 2024-06-25 16:24:59 +02:00
Vincent BRIDIER
58fd71ed09 Merge branch 'postal_code' into 'master'
fix: BE and LU postal codes were not allowed

See merge request ldc-web/projets/gen-game-web!32
2024-06-25 11:03:26 +00:00
Noah Lancien
a0027a7747 fix: BE and LU postal codes were not allowed 2024-06-25 11:44:24 +02:00
Vincent BRIDIER
6a4fae3552 fix version 2024-06-13 14:48:54 +00:00
Vincent BRIDIER
4d98327f10 Merge branch 'init-jeu-galettes-2024' into 'master'
feat: Add latest package of cookie notice

See merge request ldc-web/projets/gen-game-web!30
2024-06-13 14:47:39 +00:00
sebastien.hattry
3453b4f44c feat: Add latest package of cookie notice 2024-06-13 14:47:39 +00:00
Vincent BRIDIER
9fc5c19cec Merge branch 'init-jeu-galettes-2024' into 'master'
Add new theme

See merge request ldc-web/projets/gen-game-web!29
2024-06-04 10:14:35 +00:00
Sebastien Hattry
a3406b03b9 Add new theme 2024-06-04 12:08:33 +02:00
Jérémy Munsch
cf42dfd311
fix: 🐛 WinningProofAdminMail was sending wrong EAN code 2024-05-28 15:40:06 +02:00
Jérémy Munsch
0b3ff7628c
chore: Bump to 2.2.12 2024-05-17 11:18:57 +02:00
Jérémy Munsch
618863ee30
fix: 🐛 Product attribute and title are now correctly nullable 2024-05-17 11:18:12 +02:00
Jérémy Munsch
7ab7a99a1f
chore: Bump to 2.2.11 version 2024-05-17 10:58:17 +02:00
Jérémy Munsch
ecdcf81334
fix: 🚑 Fix fromSubmit deported button event handler 2024-05-17 10:57:29 +02:00
Jérémy Munsch
04d4e66918
fix: 🚑 Fix eanCodes request in Dashboard Controller 2024-05-17 10:53:39 +02:00
Jérémy Munsch
b0b89669ef Merge branch 'newsletter' into 'master'
Added the possibility to subscribe to a newsletter

See merge request ldc-web/projets/gen-game-web!27
2024-05-07 07:25:36 +00:00
Noah Lancien
3a9b8e4530 Added the possibility to submit to a newsletter 2024-05-06 15:50:37 +02:00
Jérémy Munsch
31af560983
bump version to 2.2.9 2024-05-02 12:05:40 +02:00
Jérémy Munsch
8763a7633d
Merge branch 'fixes-bo-seo' 2024-05-02 11:59:39 +02:00
Jérémy Munsch
2b00e56b26
npm update and audit fix 2024-05-02 11:59:01 +02:00
Jérémy Munsch
6767750b42
ProcessExportCodesUniques extends timeout 2024-03-20 11:34:55 +01:00
Jérémy Munsch
1084f1b68c
Closes #30 Disallow seo bot index bo prefix 2024-03-12 13:43:35 +01:00
Nicolas Demangeat
53e70cd9a0 add activitylog on each models 2024-02-15 15:53:36 +01:00
Vincent BRIDIER
37544c5632 Merge branch 'phpstan' into 'master'
phpstan

See merge request ldc-web/projets/gen-game-web!25
2024-01-31 09:03:18 +00:00
noah.lancien
218a3a7891 phpstan 2024-01-31 09:03:18 +00:00
Vincent BRIDIER
500c841ac6 Merge branch 'fix-participations' into 'master'
fix participations limit - #24

See merge request ldc-web/projets/gen-game-web!24
2024-01-29 11:28:52 +00:00
Alexis Gousseau
7e0159bdd0 fix participations limit - #24 2024-01-29 11:28:52 +00:00
Vincent BRIDIER
31d84c337a Merge branch 'fix-prize-cache-clean' into 'master'
Clear prize cache when updating instant win

See merge request ldc-web/projets/gen-game-web!22
2024-01-23 10:10:21 +00:00
Jérémy Munsch
ec8b4ffba5 Clear prize cache when updating instant win 2024-01-23 10:10:21 +00:00
Vincent BRIDIER
816bab3cb3 Merge branch 'sentry-fixes' into 'master'
Sentry fixes

See merge request ldc-web/projets/gen-game-web!21
2024-01-23 10:07:32 +00:00
Vincent BRIDIER
6f480721ea Merge branch 'check-reset-pw-signature' into 'master'
Added the signed middleware to the reset password route

See merge request ldc-web/projets/gen-game-web!20
2024-01-23 10:06:55 +00:00
Vincent BRIDIER
5fce1d7241 Merge branch 'update-theme' into 'master'
update theme

See merge request ldc-web/projets/gen-game-web!23
2024-01-19 14:31:06 +00:00
Alexis Gousseau
c06d0a80cb update theme 2024-01-19 15:29:21 +01:00
Jérémy Munsch
527237d551
Attemp to fix notifications
https://ldc-services-web.sentry.io/issues/4806040997/?environment=production&project=4506512363159552&query=is%3Aunresolved&referrer=issue-stream&statsPeriod=14d&stream_index=0
2024-01-17 14:57:47 +01:00
Jérémy Munsch
382d0d5470
CheckThemeVersion wrap into app()->runningInConsole since it depends on cached composer file
Fixes https://ldc-services-web.sentry.io/issues/4849094228/?environment=production&project=4506512363159552&query=is%3Aunresolved&referrer=issue-stream&statsPeriod=14d&stream_index=3
2024-01-17 14:44:42 +01:00
Jérémy Munsch
3be9f378a9
Fixes https://ldc-services-web.sentry.io/issues/4809722719/?project=4506512363159552 2024-01-17 14:40:02 +01:00
Noah Lancien
528ef0ff0a Added the signed middleware to the reset password route 2024-01-05 15:37:52 +01:00
Vincent BRIDIER
d05a516172 Merge branch 'trust-proxies' into 'master'
Fixed TrustProxies issue

See merge request ldc-web/projets/gen-game-web!19
2024-01-05 14:23:53 +00:00
Noah Lancien
a8bbeeba2a Fixed TrustProxies issue 2024-01-05 15:20:14 +01:00
Vincent BRIDIER
28ac2d3314 Merge branch 'issue-22-max-code-usage' into 'master'
Fixed the way we counted how many times a code has been used

See merge request ldc-web/projets/gen-game-web!18
2024-01-05 11:33:08 +00:00
Vincent BRIDIER
c4e571153f
Merge master + bump version 2024-01-05 12:32:45 +01:00
Vincent BRIDIER
968bceefac
Merge branch 'master' into issue-22-max-code-usage 2024-01-05 12:32:11 +01:00
Noah Lancien
532f294a77 Reverse query to get participations with the current code 2024-01-05 12:29:13 +01:00
Vincent BRIDIER
de768737d8 Merge branch 'issue-21-matomo' into 'master'
Added MAtomo

See merge request ldc-web/projets/gen-game-web!17
2024-01-05 11:15:29 +00:00
Noah Lancien
e7d3e2e8da Fixed manifest.yaml tabulations 2024-01-05 12:13:57 +01:00
Noah Lancien
4739b48a28 Fixed the way we counted how many times a code has been used 2024-01-05 10:49:38 +01:00
Noah Lancien
d251607bc4 Updated version 2024-01-04 17:52:22 +01:00
Noah Lancien
a06b0a9947 Updated manifest.yaml 2024-01-04 17:51:47 +01:00
Noah Lancien
482eb9e603 Added manifest.yaml 2024-01-04 17:44:51 +01:00
Noah Lancien
64fe3a41d9 Added MAtomo 2024-01-04 17:33:02 +01:00
Vincent BRIDIER
52960b5b72 Merge branch 'issue-19-admin-secure' into 'master'
Added admin-secure

See merge request ldc-web/projets/gen-game-web!16
2024-01-04 14:19:47 +00:00
noah.lancien
369e9c9fd3 Added admin-secure #19 2024-01-04 14:19:47 +00:00
Vincent BRIDIER
f349ed3c1e
Add deployment folder 2024-01-04 15:11:38 +01:00
Vincent BRIDIER
3515cd7253 Merge branch 'issue-20' into 'master'
Fixed condition to display if a prize has been won or not

See merge request ldc-web/projets/gen-game-web!15
2024-01-04 14:04:46 +00:00
noah.lancien
ba418c5edf Fixed condition to display if a prize has been won or not 2024-01-04 14:04:46 +00:00
Vincent BRIDIER
311dda7b6a Merge branch 'k8s' into 'master'
Added k8s package

See merge request ldc-web/projets/gen-game-web!14
2024-01-04 11:47:55 +00:00
noah.lancien
6e7be9050e Added k8s package 2024-01-04 11:47:55 +00:00
Vincent BRIDIER
32e47c8d9c Merge branch 'trunk' 2023-12-21 16:41:57 +01:00
Vincent BRIDIER
a1e612d74e Merge branch 'develop' into trunk 2023-12-21 16:41:23 +01:00
Vincent BRIDIER
3a1485c91e Merge branch 'issue-16' into 'develop'
2.0.0

See merge request ldc-web/projets/gen-game-web!13
2023-12-21 15:39:21 +00:00
Jérémy Munsch
85a8bde47d
bump cookie notice to 4.0.0 + update readme 2023-12-21 12:55:02 +01:00
Jérémy Munsch
5fa986ba0c
bump front packages 2023-12-21 10:54:29 +01:00
Jérémy Munsch
2f36dd221d
update readme 2023-12-20 17:11:46 +01:00
Jérémy Munsch
9242161f19
update theme 2023-12-20 17:11:06 +01:00
Jérémy Munsch
ff922a4127
add scratchcard-js 2023-12-20 17:03:44 +01:00
Jérémy Munsch
d8ab0146e0
PArticipationPolicy fix viewWonList pour recettage 2023-12-20 16:11:05 +01:00
Jérémy Munsch
2ef26bc270
ParticipationPolicy fix viewWonList 2023-12-20 13:12:54 +01:00
Jérémy Munsch
de748bb9bb
ProcessExportCodesUniques remove writed file in case of export failure 2023-12-20 12:58:40 +01:00
Jérémy Munsch
ee8b0a6a41
CustomerController fix profilVerifySubmit 2023-12-20 12:44:48 +01:00
Jérémy Munsch
b278bae91e
update readme 2023-12-19 13:50:24 +01:00
Jérémy Munsch
44de0136e4
Set prize sub title as nullable 2023-12-19 13:50:18 +01:00
Jérémy Munsch
28e54a81f2
Fix Prize->pdf_winning_proof was not set on null if no file 2023-12-19 13:28:35 +01:00
Jérémy Munsch
1d3f6138dd
HomeController add warning if game cannot be enabled 2023-12-19 13:08:20 +01:00
Jérémy Munsch
89e0aa0fdc
CheckGameStatus middleware add use case game will begun but has no teaser as default hanlding redirect to page 2023-12-19 12:55:37 +01:00
Jérémy Munsch
c113c51800
breacrumd trans lots 2023-12-19 12:46:42 +01:00
Jérémy Munsch
50a4ccc958
__('validation.custom.unique_code') trans 2023-12-19 12:31:29 +01:00
Jérémy Munsch
d3575fff52
add trans('validation.custom.comment') 2023-12-19 12:27:26 +01:00
Jérémy Munsch
f5bb6baba2
bo title ajout indication Bo pour faciliter le travail 2023-12-19 11:13:37 +01:00
Jérémy Munsch
d6d70a9509
Fix for BO html title 2023-12-19 10:43:18 +01:00
Jérémy Munsch
78ad816e7c
Fix composer PSR4 load 2023-12-19 09:09:09 +01:00
Jérémy Munsch
35eae0b542
Theme swith command prefix with uwil: 2023-12-18 18:41:58 +01:00
Jérémy Munsch
b4a7b2000a
ToolboxHelper add missing runConsoleCommand method 2023-12-18 18:40:28 +01:00
Jérémy Munsch
b469549d53
Fix config theme include if mopunted folder does not exists 2023-12-18 18:38:25 +01:00
Jérémy Munsch
dc0e6d9481
Bo favicon fix 2023-12-18 14:15:12 +01:00
Jérémy Munsch
7779feaf88
UserSeeder Update 2023-12-18 12:25:02 +01:00
Jérémy Munsch
a001cb83fc
Fo ResetPasswordController typehint fix 2023-12-15 17:06:14 +01:00
Jérémy Munsch
55907fadb0
Version 2.0.0 GGW theme Handler update 2023-12-14 10:35:49 +01:00
Jérémy Munsch
7b699597d1
Move theme version check to a dedicaced middleware as it would prevent flash to flush 2023-12-14 10:24:08 +01:00
Jérémy Munsch
5b73723f03
SYNC FINAL theme add comments and version 2023-12-14 10:11:53 +01:00
Jérémy Munsch
3262b2553c
Add sitemap generator 2023-12-14 10:01:23 +01:00
Jérémy Munsch
f8528c4ec8
Fi UI elements + add condition on re7 prize sent mail 2023-12-14 09:48:29 +01:00
Jérémy Munsch
a5fcfa15e7
Add notify_prize_sent setting 2023-12-14 09:41:19 +01:00
Jérémy Munsch
e10acc5756
SYNC 25 Winning participatiosn list FO 2023-12-13 18:45:43 +01:00
Jérémy Munsch
5b05cf7c59
Fix participation save 2023-12-13 18:10:37 +01:00
Jérémy Munsch
0f3bf5e2ca
Add re7 prize sent 2023-12-13 18:08:05 +01:00
Jérémy Munsch
b8bb0f2392
Add missing policies to Bo/ParticipationController + fix 2023-12-13 17:12:11 +01:00
Jérémy Munsch
e29c5580b8
Add Winner add comment feature 2023-12-13 16:47:28 +01:00
Jérémy Munsch
8fab343dde
SYNC 23 Add Winner set prize sent feature 2023-12-13 15:48:18 +01:00
Jérémy Munsch
573ac1a127
Fix winner list view 2023-12-13 12:47:36 +01:00
Jérémy Munsch
9a7fc876de
Refactored DashboardController 2023-12-13 12:45:15 +01:00
Jérémy Munsch
b68b6c4aec
Re7Controller fix prize page display 2023-12-13 11:36:10 +01:00
Jérémy Munsch
247660f4dc
re7 add received proof mail 2023-12-13 11:29:56 +01:00
Jérémy Munsch
b127390971
update readme 2023-12-13 11:22:44 +01:00
Jérémy Munsch
57f9ddad28
Fixup Verification (set active on crm also) 2023-12-13 11:22:37 +01:00
Jérémy Munsch
3301dd8de1
Fix regression on participate 2023-12-13 11:22:21 +01:00
Jérémy Munsch
5258824967
fix postal code usage on participate and winningproof controller 2023-12-13 10:25:38 +01:00
Jérémy Munsch
9dda536e9e
Fix register controller 2023-12-13 10:25:08 +01:00
Jérémy Munsch
8ab8c7a0cc
SYNC 22 Submit winning proof + notifications ok 2023-12-13 10:01:52 +01:00
Jérémy Munsch
c12cf20050
Refactor ParticipateController 2023-12-12 16:12:50 +01:00
Jérémy Munsch
3bf55161f9
PrizeWonAdminMail fix 2023-12-12 16:05:41 +01:00
Jérémy Munsch
9567c77ae9
Update Settings layout + fix game counters (participation by account, IP) and Code times use 2023-12-12 16:05:29 +01:00
Jérémy Munsch
94ac4f8d40
Fix Participation policy 2023-12-12 15:00:54 +01:00
Jérémy Munsch
9b55bf09c4
SYNC 21 Coupon win ok 2023-12-12 14:56:12 +01:00
Jérémy Munsch
3f383af05d
Fixed Participate and win InstantWin 2023-12-12 12:55:05 +01:00
Jérémy Munsch
cf0b1a54f0
SYNC 20 participate InstantWin Ok 2023-12-12 12:25:45 +01:00
Jérémy Munsch
9f73d89e5c
Fix InstantWinComponent 2023-12-12 09:20:09 +01:00
Jérémy Munsch
48046fdbba
Settings nb_unique_code_use set minimum 1 as value 2023-12-11 17:27:23 +01:00
Jérémy Munsch
15a75bc8ee
Fix Bo Gate usage when already logged using frontend guard 2023-12-11 17:21:55 +01:00
Jérémy Munsch
20fb1fea8e
Fixes wrong policy (deny modifying while game is oppened) set only on prizes + add prize show 2023-12-11 13:26:37 +01:00
Jérémy Munsch
c2421e5015
Deny updating or deleting Ranges and Product while game is oppened
+ add product and range show on BO
2023-12-11 13:07:28 +01:00
Jérémy Munsch
9328fe0b37
Add game status on header and refactor game status +
Refactor GameStatus
2023-12-11 12:37:58 +01:00
Jérémy Munsch
1ecc4d60d2
update readme 2023-12-11 11:34:40 +01:00
Jérémy Munsch
ca0c6484d0
Sync 19 Fix participate view 2023-12-11 11:30:25 +01:00
Jérémy Munsch
d191b6ad56
Sync 18 Add VerifyController + Fixes on CRM register 2023-12-11 11:21:06 +01:00
Jérémy Munsch
b3abfbe66c
Add first part to account verify 2023-12-08 18:55:55 +01:00
Jérémy Munsch
47fc3e250a
Register add country to account creation 2023-12-08 18:55:31 +01:00
Jérémy Munsch
8986d93b11
fix bo auth check on header 2023-12-08 18:54:10 +01:00
Jérémy Munsch
090336b33e
Bo code overhaul code search display and add how many times a code was used 2023-12-08 16:04:15 +01:00
Jérémy Munsch
4659173811
Re7 add home page 2023-12-08 15:49:20 +01:00
Jérémy Munsch
1b388e7265
Big freaking update TMP refactor Participate 2023-12-08 15:39:13 +01:00
Jérémy Munsch
1c4a7d915e
Some phpcs fixes 2023-12-07 17:16:23 +01:00
Jérémy Munsch
58623e9207
re7 pages display BR page and mail only if activated in settings 2023-12-07 17:05:55 +01:00
Jérémy Munsch
cae2ad7754
SYNC 17 RE7 Participate mail preview 2023-12-07 17:00:14 +01:00
Jérémy Munsch
6683365cb1
Sync 16 re7EndGame 2023-12-07 15:27:48 +01:00
Jérémy Munsch
8efa232036
SYNC 15 RE7 pages ok win 2023-12-07 15:15:08 +01:00
Jérémy Munsch
723892063a
Setup Webcoupon administration 2023-12-07 14:28:29 +01:00
Jérémy Munsch
57517b1772
partials inputs fixes 2023-12-06 16:39:21 +01:00
Jérémy Munsch
f81026caff
error page text fix 2023-12-06 16:00:51 +01:00
Jérémy Munsch
f8b1b7ab76
Sync 15 FIX prizes with image resizer 2023-12-06 12:11:46 +01:00
Jérémy Munsch
d66e34f5b8
SYNC 14 Setup prizes add picture + normalize + make usage of all fields + add image/resizer 2023-12-06 11:19:39 +01:00
Jérémy Munsch
880d722e5f
Sync 12 Re7 prize ok avec Roulette 2023-12-05 18:41:58 +01:00
Jérémy Munsch
f89688b3ec
More bo fixes 2023-12-05 14:41:16 +01:00
Jérémy Munsch
6761e040b6
Fix marketing policies bo 2023-12-05 12:43:46 +01:00
Jérémy Munsch
72aa0297f1
Remove userless is-admin, is-super-admin middlewares 2023-12-05 12:38:27 +01:00
Jérémy Munsch
edf18b2fb8
Fix policies for StaticPage 2023-12-05 12:35:34 +01:00
Jérémy Munsch
a34176419c
Tons of fucking fix + Setup policies 2023-12-05 12:33:25 +01:00
Jérémy Munsch
ee429bc2e0
add project color 2023-12-04 11:16:25 +01:00
Jérémy Munsch
71c55a34be
Add code config view on settings 2023-12-01 17:43:41 +01:00
Jérémy Munsch
bfc72f09af
SYNC 12 theme indication on BO 2023-12-01 17:04:32 +01:00
Jérémy Munsch
5ba294b496
Add missing parts for delete customer (jobs) but deny removing customers (on Model) 2023-12-01 16:55:25 +01:00
Jérémy Munsch
95bb4e3b09
Remove code length from settings (keep on config like always) 2023-12-01 16:49:10 +01:00
Jérémy Munsch
79ee185e1c
SYNC 11 Settings company + name usage 2023-12-01 16:13:56 +01:00
Jérémy Munsch
5374c26859
settings as singleton 2023-12-01 15:43:00 +01:00
Jérémy Munsch
67c5eb07f3
REmove Teaser end date (useless) 2023-12-01 15:21:25 +01:00
Jérémy Munsch
f1b90801da
SYNC 11 Start Re7 Teaser Page ok 2023-12-01 11:45:12 +01:00
Jérémy Munsch
da31943f11
Remove society from settings and fix Settings bo 2023-11-30 18:06:10 +01:00
Jérémy Munsch
560c0875d6
StaticPageSeeder add truncate before seed 2023-11-30 17:07:03 +01:00
Jérémy Munsch
a41a3916fb
update readme 2023-11-30 17:06:35 +01:00
Jérémy Munsch
712c000ff2
Bo request attributes translate 2023-11-30 16:58:36 +01:00
Jérémy Munsch
dc3d0642dc
Remove forgotten GainView requests 2023-11-30 16:31:31 +01:00
Jérémy Munsch
efca142995
StorePrizeRequest attributes fix 2023-11-30 16:30:57 +01:00
Jérémy Munsch
298c787935
Sync 10 Remove boolean need preuve de gains from Lot 2023-11-30 16:20:45 +01:00
Jérémy Munsch
50cbb4b7ea
Role enum add marketing + Upstream fix UserCreate command 2023-11-30 15:30:23 +01:00
Jérémy Munsch
dbbe8468c7
upstream add bs-demo 2023-11-30 15:27:03 +01:00
Jérémy Munsch
44883c1d7d
Banned ip using ip Lib (handle ipv4 or ip6) + ip validation from every where 2023-11-30 14:46:08 +01:00
Jérémy Munsch
1774e34acd
Bo fixes 2023-11-30 11:37:17 +01:00
Jérémy Munsch
4f8b257d17
Remove Gain View (gagné lot, gagné BR, et perdu on chaqu'un leur page) 2023-11-30 11:22:08 +01:00
Jérémy Munsch
d803889960
Sync 9 FAQ ok 2023-11-30 11:10:02 +01:00
Jérémy Munsch
c10c4abc6a
CrmUserProvider set user activated if it is activated on CRM 2023-11-29 17:07:51 +01:00
Jérémy Munsch
d2f44f9688
Fix for gate fo.showProducts 2023-11-29 17:00:54 +01:00
Jérémy Munsch
53ab1814aa
Sync 8 produit porteurs 2023-11-29 16:52:33 +01:00
Jérémy Munsch
232bc2f459
Tons of Bo fixes related to product and ranges,
add picture alt and titles + seeders + correct config
picture sizes
2023-11-29 15:33:23 +01:00
Jérémy Munsch
3e326ba3ca
SYNC 7 Register + activate + reset + mail customisation 2023-11-29 10:57:48 +01:00
Jérémy Munsch
2f12ab0c1c
SYNC 6 Fully responsive home layout + static pages 2023-11-27 12:04:19 +01:00
Jérémy Munsch
93caefb196
Sync 5 Fix front loading after bo Overhaul 2023-11-24 10:03:42 +01:00
Jérémy Munsch
b79b7dc151
Fixes on Bo 2023-11-24 09:52:56 +01:00
Jérémy Munsch
d208854fb0
Tmp back overhaul with base project 2023-11-23 17:31:28 +01:00
Jérémy Munsch
5dda42e9da
Sync 3 Favicon System
Sync 4 Layout + homepage ok
2023-11-23 09:45:52 +01:00
Jérémy Munsch
d3f2942c68
Sync 2 Html loading ok 2023-11-21 12:28:09 +01:00
Jérémy Munsch
e6a16ff965
Setup view aliases 2023-11-20 17:22:24 +01:00
Jérémy Munsch
47aaf436c6
Sync 1 Front compilation ok 2023-11-20 17:06:16 +01:00
Jérémy Munsch
6c34db1f2a
remove basic_example theme 2023-11-20 17:04:20 +01:00
Jérémy Munsch
80cf81bc4b
Fix Bo compile 2023-11-20 16:03:55 +01:00
Jérémy Munsch
6a5468df53
Add theme switch command 2023-11-20 15:42:16 +01:00
Jérémy Munsch
9011378232
Add theme-merger as submodule 2023-11-20 12:20:13 +01:00
Jérémy Munsch
0fe116851a
npm audit fix force 2023-11-20 11:30:22 +01:00
Jérémy Munsch
d3d0daa167
phpcs file rule fix 2023-11-20 11:20:09 +01:00
Jérémy Munsch
05fa20765a
PSR-4 fix 2023-11-20 11:19:30 +01:00
Jérémy Munsch
247952ae5f
composer update (audit fix) 2023-11-20 11:18:05 +01:00
Vincent BRIDIER
c2421dfc57
Merge branch 'trunk' 2023-08-28 17:10:48 +02:00
Vincent BRIDIER
a8ab9af6dc
Merge branch 'develop' into trunk 2023-08-28 17:10:10 +02:00
Noah Lancien
949b7ca13e phpcs 2023-08-24 12:59:51 +00:00
Noah Lancien
4113e2254a Some fixes, docblocks, style, cleaning ... Added a footer 2023-08-24 13:00:43 +02:00
Noah Lancien
ec7f20cf4d Updated Codes Controller/views 2023-08-23 14:24:51 +00:00
Noah Lancien
bc477627b3 Updated InstantWin Component/Controller 2023-08-23 13:48:25 +00:00
Noah Lancien
82df7497ee Fixed Dashboard 2023-08-23 13:29:54 +00:00
Noah Lancien
89c5f19dfa Updated Settings Controller/Request/Blade and winnersList 2023-08-23 10:38:11 +00:00
Noah Lancien
c928f9a5c4 Products crud 2023-08-22 14:25:31 +02:00
Noah Lancien
1f672491ca Ranges crud 2023-08-22 13:38:38 +02:00
Noah Lancien
da8e008147 Prizes crud 2023-08-22 12:57:02 +02:00
Noah Lancien
92c81ab937 GainViews crud 2023-08-22 10:48:31 +02:00
Noah Lancien
7654b078c5 EAN Codes crud 2023-08-22 10:13:35 +02:00
Noah Lancien
db4db55fe9 FAQ CRUDs 2023-08-22 09:37:17 +02:00
Noah Lancien
d604cf64b6 Content CRUD 2023-08-21 15:10:57 +02:00
Noah Lancien
333d34d17a BannedIps CRUD + missing files 2023-08-21 10:15:14 +00:00
Noah Lancien
a529367fba Bump to laravel-10 2023-08-21 10:22:07 +02:00
Alexis Gousseau
afa735f0b8 split admin users and random users to customers - issue #10 2023-08-17 12:46:15 +02:00
Vincent BRIDIER
e1d1238919
bump kwaadpepper/serial-int-caster 2023-08-10 13:15:45 +02:00
Jérémy Munsch
6d48e30cc0
Merge branch 'trunk' 2023-05-17 17:00:57 +02:00
Jérémy Munsch
6cc7ffa0ba
Merge branch 'develop' into trunk 2023-05-17 17:00:18 +02:00
Jérémy Munsch
c189ac6f18
update config.code to be customized from env 2023-05-17 16:59:41 +02:00
Vincent BRIDIER
0bc2a03ade
Merge branch 'trunk' 2022-08-31 11:27:02 +02:00
Vincent BRIDIER
386efc9be6
Merge branch 'develop' into trunk 2022-08-31 11:26:49 +02:00
Vincent BRIDIER
7ceccdf7b0
Merge branch 'fix_mail' into develop 2022-08-31 11:26:33 +02:00
Vincent BRIDIER
0e2f589251
Update Basic Example mails 2022-08-31 11:25:12 +02:00
Alexis Gousseau
31f75bdf0d fix code ean in email and in instant win page 2022-08-31 11:05:14 +02:00
Vincent BRIDIER
b641ba5238
Merge branch 'trunk' 2022-08-31 10:33:59 +02:00
Vincent BRIDIER
b15d2a86ab
Merge branch 'develop' into trunk 2022-08-31 10:33:46 +02:00
Vincent BRIDIER
47661c3ceb Merge branch 'fix_mail' into 'develop'
fix code emails

See merge request service-web-lab/gen-game-web!7
2022-08-31 08:33:06 +00:00
Alexis Gousseau
d2c2503438 fix code emails 2022-08-31 09:41:59 +02:00
Vincent BRIDIER
c25d447ca3
Merge branch 'trunk' 2022-08-19 17:39:03 +02:00
Vincent BRIDIER
eec1adc588
Merge branch 'develop' into trunk 2022-08-19 17:38:48 +02:00
Vincent BRIDIER
8502fe7538 Merge branch 'fix_analytics_sh01' into 'develop'
Fix analytics sh01

See merge request service-web-lab/gen-game-web!6
2022-08-19 15:37:56 +00:00
S. Hattry
589f733601 Fix GA lenght 2022-08-19 15:32:33 +00:00
Vincent BRIDIER
a52fa1d08a
Merge branch 'trunk' 2022-08-11 11:26:29 +02:00
Vincent BRIDIER
bcadeab1d3
Merge branch 'develop' into trunk 2022-08-11 11:25:50 +02:00
Vincent BRIDIER
22f5e5b0f9
Add CI 2022-08-11 10:27:10 +02:00
Vincent BRIDIER
683a6824bc
Some PHPCS fixes 2022-08-11 10:26:29 +02:00
Vincent BRIDIER
4846a6b888
PHPCBF 2022-08-11 10:03:16 +02:00
Vincent BRIDIER
8658088d9e
Remove Useless files 2022-08-11 10:02:00 +02:00
Vincent BRIDIER
b1108974d9
Update Create Admin command 2022-08-11 09:57:36 +02:00
Vincent BRIDIER
6613f5b8b9
Update README 2022-08-11 09:57:22 +02:00
Vincent BRIDIER
bd98b50512
Update .env.example 2022-08-11 09:57:02 +02:00
Vincent BRIDIER
30033e2ed8 Merge branch 'bug-plus-evo_sh01' into 'develop'
Bug plus evo sh01

See merge request service-web-lab/gen-game-web!5
2022-08-11 07:33:30 +00:00
Sebastien Hattry
ac55e4ca69 Ticket 908
Ticket 952
2022-08-11 07:33:30 +00:00
Olivier Legat
3ec4993ae8 Merge branch 'trunk' 2022-06-08 09:11:11 +02:00
Olivier Legat
e303611223 Merge branch 'develop' into trunk 2022-06-08 09:10:20 +02:00
Vincent BRIDIER
2bb85255a4
Fix arcadia message regression 2022-06-07 11:53:10 +02:00
Vincent BRIDIER
b77b0452ba
Untrack public files 2022-06-07 11:27:13 +02:00
Vincent BRIDIER
fb8e0cd066
Merge branch 'trunk' 2022-05-23 14:29:05 +02:00
Olivier Legat
f208463532 Merge branch 'develop' into 'trunk'
Stack update

See merge request service-web-lab/gen-game-web!3
2022-05-13 07:09:00 +00:00
Vincent BRIDIER
2e72dda5ec
Stack update 2022-05-02 14:30:59 +02:00
Olivier Legat
9d2152d549 Merge branch 'trunk' 2022-02-24 14:13:42 +01:00
Olivier Legat
081d4ca8f2 Merge branch 'develop' into trunk 2022-02-24 14:13:24 +01:00
Olivier Legat
d63dfd34c1 Merge branch 'trunk' 2022-02-17 18:05:05 +01:00
Olivier Legat
bdcbf4f082 Merge branch 'develop' into trunk 2022-02-11 16:59:49 +01:00
Olivier Legat
155116b1d1 Merge branch 'develop' into trunk 2022-02-11 11:11:32 +01:00
Olivier Legat
0dea317174 Merge branch 'develop' into trunk 2022-02-11 10:12:26 +01:00
Vincent BRIDIER
50166c28e1 Merge branch 'trunk' 2022-01-10 18:05:23 +01:00
Vincent BRIDIER
6868b153be Merge branch 'trunk' of https://voight001.lesidc.com/gen-game-web into trunk 2022-01-10 18:04:22 +01:00
Vincent BRIDIER
4a4902ae7e Merge branch 'develop' into trunk 2022-01-10 18:04:09 +01:00
Olivier Legat
8d4bbfbd98 Merge branch 'trunk' 2021-09-30 12:00:56 +02:00
Olivier Legat
f337413438 Merge branch 'develop' into trunk 2021-09-30 12:00:34 +02:00
Olivier Legat
c81160a4dd Merge branch 'trunk' 2021-09-30 09:45:36 +02:00
Olivier Legat
079968f207 Merge branch 'develop' into trunk 2021-09-30 09:44:18 +02:00
Olivier Legat
ed3b6c2e43 Merge branch 'trunk' 2021-09-29 17:37:59 +02:00
Olivier Legat
796e4436d9 Merge branch 'develop' into trunk 2021-09-29 17:37:44 +02:00
Olivier Legat
59769cbff0 Merge branch 'trunk' 2021-09-23 09:03:05 +02:00
Olivier Legat
7de8fbd8bf Merge branch 'develop' into trunk 2021-09-23 09:02:52 +02:00
Olivier Legat
3f8b85b6de Merge branch 'trunk' 2021-09-16 11:01:12 +02:00
Olivier Legat
3063e9fd93 Merge branch 'develop' into trunk 2021-09-16 11:00:59 +02:00
Olivier Legat
91955f6f9e Merge branch 'trunk' 2021-09-06 13:37:34 +02:00
Olivier Legat
765cc236ef Merge branch 'develop' into trunk 2021-09-06 13:37:08 +02:00
Olivier Legat
49390dbf7a Merge branch 'trunk' 2021-09-06 12:22:07 +02:00
Olivier Legat
24a1687a42 Merge branch 'develop' into trunk 2021-09-06 12:21:52 +02:00
Olivier Legat
115a4de280 Merge branch 'trunk' 2021-08-30 16:13:20 +02:00
Olivier Legat
56a68bf234 Merge branch 'develop' into trunk 2021-08-30 16:12:58 +02:00
Olivier Legat
07c430cb5c Merge branch 'trunk' 2021-08-23 14:17:38 +02:00
Olivier Legat
54a4808829 Merge branch 'develop' into trunk 2021-08-23 14:17:15 +02:00
Vincent BRIDIER
11fb17d63b Merge branch 'trunk' 2021-08-19 10:11:38 +02:00
Vincent BRIDIER
64643d90f1 Merge branch 'develop' into trunk 2021-08-19 10:10:57 +02:00
Vincent BRIDIER
0e8c121f8f Merge branch 'develop' into trunk 2021-08-19 10:06:21 +02:00
Vincent BRIDIER
bf8a70f47b Merge branch 'develop' into trunk 2021-08-19 09:43:58 +02:00
Vincent BRIDIER
500219ca89 Merge branch 'trunk' 2021-07-28 14:02:25 +02:00
Vincent BRIDIER
7fa1b84cb5 Merge branch 'trunk' of https://voight001.lesidc.com/gen-game-web into trunk 2021-07-28 14:01:55 +02:00
Vincent BRIDIER
0546ab81a7 Merge branch 'develop' into trunk 2021-07-28 14:01:45 +02:00
Olivier Legat
f4e6b41328 Merge branch 'trunk' 2021-07-22 10:33:44 +02:00
Olivier Legat
ec325a27f3 Merge branch 'develop' into trunk 2021-07-22 10:33:02 +02:00
Olivier Legat
ffbe8ac531 Merge branch 'trunk' 2021-07-20 09:05:28 +02:00
Olivier Legat
f8e50b259b Merge branch 'develop' into trunk 2021-07-20 09:04:30 +02:00
Olivier Legat
43f0d6aef2 Merge branch 'develop' into trunk 2021-07-16 12:53:40 +02:00
Olivier Legat
7e3c225aaf Merge branch 'develop' into trunk 2021-07-16 12:46:56 +02:00
Olivier Legat
55b5dd438d Merge branch 'develop' into trunk 2021-07-15 16:08:37 +02:00
Olivier Legat
5e2fb194c1 Merge branch 'develop' into trunk 2021-07-13 17:29:57 +02:00
Vincent BRIDIER
9f6a9db628 Merge branch 'trunk' 2021-06-17 18:01:44 +02:00
Vincent BRIDIER
50a73019a4 Merge branch 'develop' into trunk 2021-06-17 18:01:10 +02:00
Vincent BRIDIER
f4d6aa03b7 Merge branch 'trunk' 2021-06-17 09:16:39 +02:00
Vincent BRIDIER
cb262b91e9 Merge branch 'develop' into trunk 2021-06-17 09:15:51 +02:00
622 changed files with 48866 additions and 125135 deletions

5
.browserslistrc Normal file
View file

@ -0,0 +1,5 @@
# Browsers that we support
last 3 version
> 0.3%
not dead # no browsers without security updates

View file

@ -3,9 +3,9 @@ root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
@ -13,3 +13,9 @@ trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
[*.vue]
indent_size = 2

View file

@ -5,9 +5,11 @@ APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_HOST=dev-mysql
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
@ -15,22 +17,51 @@ DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
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=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_HOST=dev-mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN=informatique.internet@ldc.fr
MAIL_ADMIN=digital@ldc.fr
# Sentry
SENTRY_LARAVEL_DSN=
# MATOMO
MATOMO_SITE_ID=
# CRM
CRM_ENDPOINT=
CRM_TOKEN=
CRM_PASSWORD=
# MESBR
MESBR_ENDPOINT=
# ADMIN SECURE
# - API ENDPOINT (Optional)
LDC_WEB_API_ENDPOINT=""
# - API KEY
LDC_WEB_API_KEY=""
# - LIST SEPARATED WITH ',' (Optional)
LDC_WEB_CIDR_MANUAL_ALLOWED_LIST=""
# Export pagination config, defaut 65530.
CODE_PAGINATE=
# Dictionnary of chars to use fo generating unique codes.
CODE_CHAR_LIST=
# Code length (chars).
CODE_CHAR_LENGTH=

14
.gitattributes vendored
View file

@ -1,5 +1,11 @@
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
CHANGELOG.md export-ignore
.styleci.yml export-ignore

55
.gitignore vendored
View file

@ -1,29 +1,46 @@
public/front/*
.env
.env.backup
.env.production
.phpunit.result.cache
.stylelintcache
/.fleet
/.idea
/.phpunit.cache
/.vscode
/node_modules
/bootstrap
/public/build
/public/cache
/public/hot
/public/sitemap.xml
/public/storage
/public/fichiers
/public/images/produits
/public/images/ranges
/public/css/*
/public/js/*
resources/views/errors/*
resources/views/front/basic/*
mix-manifest.json
/storage/*.key
/vendor
/resources/views/front/basic
.env
.env.dev
.env.prod
.env.backup
.phpunit.result.cache
auth.json
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
package-lock.json
composer.lock
resources/front
.vscode
.npmrc
/resources/theme
# https://lab.s-web.fr/ldc-web/projets/gen-game-web/-/issues/17
public/fichiers
storage
resources/theme
.npm/
.composer/
.zshrc
# 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

11
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,11 @@
include:
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'lint/lint.yml'
- project: 'ldc-web/shared-ci'
ref: 'main'
file: 'security/security.yml'
stages:
- lint
- security

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "resources/theme"]
path = resources/theme
url = ../gen-game-web-themes/theme-merger.git

2
.husky/commit-msg Normal file
View file

@ -0,0 +1,2 @@
#!/bin/sh
npx commitlint --config node_modules/@uwil/husky-config/commitlint.config.js --edit $1

View file

@ -1,13 +0,0 @@
php:
preset: laravel
disabled:
- unused_use
finder:
not-name:
- index.php
- server.php
js:
finder:
not-name:
- webpack.mix.js
css: true

43
.stylelintrc.json Normal file
View file

@ -0,0 +1,43 @@
{
"extends": [
"stylelint-config-standard-scss",
"stylelint-config-sass-guidelines",
"stylelint-config-idiomatic-order"
],
"plugins": [
"stylelint-scss"
],
"files": [
"**/*.css",
"**/*.scss"
],
"ignoreFiles": [
"**/vendor/*.css",
"**/vendor/**/*.css",
"**/vendor/*.scss",
"**/vendor/**/*.scss",
"**/vendor/*.sass",
"**/vendor/**/*.sass",
"**/vendor/*.less",
"**/vendor/**/*.less"
],
"customSyntax": "postcss-scss",
"rules": {
"declaration-property-value-no-unknown": null,
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,
"max-nesting-depth": 5,
"selector-max-compound-selectors": 5,
"scss/double-slash-comment-empty-line-before": [
"always",
{
"ignore": [
"between-comments",
"stylelint-commands",
"inside-block"
]
}
]
}
}

22
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,22 @@
{
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#962a40",
"activityBar.background": "#962a40",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#40952a",
"activityBarBadge.foreground": "#e7e7e7",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#962a40",
"statusBar.background": "#6e1f2f",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#962a40",
"statusBarItem.remoteBackground": "#6e1f2f",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#6e1f2f",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#6e1f2f99",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.remoteColor": "#6e1f2f"
}

View file

@ -1,61 +1,59 @@
<p align="center"><a href="https://laravel.com" target="_blank"><img src="https://raw.githubusercontent.com/laravel/art/master/logo-lockup/5%20SVG/2%20CMYK/1%20Full%20Color/laravel-logolockup-cmyk-red.svg" width="400"></a></p>
# GEN GAME WEB
<p align="center">
<a href="https://travis-ci.org/laravel/framework"><img src="https://travis-ci.org/laravel/framework.svg" alt="Build Status"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/d/total.svg" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/laravel/framework"><img src="https://poser.pugx.org/laravel/framework/license.svg" alt="License"></a>
</p>
## Installation
## About Laravel
``` Bash
cp .env.example .env
php artisan key:generate /!\ bien sauvegarder la key
php artisan migrate:fresh --step
# Seulement pour developper un theme par exemple
php artisan db:seed --class DevelopmentSeeder
php artisan uwil:user:create
```
Laravel is a web application framework with expressive, elegant syntax. We believe development must be an enjoyable and creative experience to be truly fulfilling. Laravel takes the pain out of development by easing common tasks used in many web projects, such as:
## Création du compte Admin
- [Simple, fast routing engine](https://laravel.com/docs/routing).
- [Powerful dependency injection container](https://laravel.com/docs/container).
- Multiple back-ends for [session](https://laravel.com/docs/session) and [cache](https://laravel.com/docs/cache) storage.
- Expressive, intuitive [database ORM](https://laravel.com/docs/eloquent).
- Database agnostic [schema migrations](https://laravel.com/docs/migrations).
- [Robust background job processing](https://laravel.com/docs/queues).
- [Real-time event broadcasting](https://laravel.com/docs/broadcasting).
``` Bash
php artisan uwil:user:create
```
Laravel is accessible, powerful, and provides tools required for large, robust applications.
## Ajouter Parametrage du jeu
## Learning Laravel
**Les lots sont à ajouter avant de générer les instants gagants**. En effet les instants gagants sont liés à des lots.
Laravel has the most extensive and thorough [documentation](https://laravel.com/docs) and video tutorial library of all modern web application frameworks, making it a breeze to get started with the framework.
If you don't feel like reading, [Laracasts](https://laracasts.com) can help. Laracasts contains over 1500 video tutorials on a range of topics including Laravel, modern PHP, unit testing, and JavaScript. Boost your skills by digging into our comprehensive video library.
## Générer des coupons et les mails
## Laravel Sponsors
``` Bash
artisan queue:work
```
We would like to extend our thanks to the following sponsors for funding Laravel development. If you are interested in becoming a sponsor, please visit the Laravel [Patreon page](https://patreon.com/taylorotwell).
En cas de manipulation pourrie, penser a vider le cache pour supprimer le lock
sur le job de generation des codes.
### Premium Partners
## THEME
- **[Vehikl](https://vehikl.com/)**
- **[Tighten Co.](https://tighten.co)**
- **[Kirschbaum Development Group](https://kirschbaumdevelopment.com)**
- **[64 Robots](https://64robots.com)**
- **[Cubet Techno Labs](https://cubettech.com)**
- **[Cyber-Duck](https://cyber-duck.co.uk)**
- **[Many](https://www.many.co.uk)**
- **[Webdock, Fast VPS Hosting](https://www.webdock.io/en)**
- **[DevSquad](https://devsquad.com)**
- **[OP.GG](https://op.gg)**
### Instalation
## Contributing
Pour installer le theme bien **mettre à jour les submodules avec git**,
`git submodule update --init --recursive`
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
puis utiliser la commande `php artisan uwil:theme:switch NOM_DU_THEME` et `php artisan optimize:clear && php artisan optimize`
## Code of Conduct
Pour mettre a jour la liste des themes :
In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://laravel.com/docs/contributions#code-of-conduct).
### Mise à jour
## Security Vulnerabilities
**Note** : cette partie n'est pas encore certaine.
If you discover a security vulnerability within Laravel, please send an e-mail to Taylor Otwell via [taylor@laravel.com](mailto:taylor@laravel.com). All security vulnerabilities will be promptly addressed.
- Mettre à jour `git submodule update --remote --merge resources/theme`
Ou
## License
``` Bash
cd resources/themes;
git pull origin main;
cd ../..;
git add resources/themes
```
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
Puis commit.

View file

@ -0,0 +1,188 @@
<?php
namespace App\Auth;
use App\Models\Customer;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider as UserProviderContract;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
/**
* Crm user provider to integrate nicely with Laravel
*/
class CrmUserProvider extends EloquentUserProvider implements UserProviderContract
{
/**
* The model to use, specified in auth config guard.
*
* @var string
*/
private $usedModel = Customer::class;
/**
* Retrieve a user by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
*/
public function retrieveByCredentials(array $credentials)
{
// * Let laravel find a model
$model = parent::retrieveByCredentials($credentials) ??
self::retrieveTrashed($credentials);
try {
/** @var \LDC\LDCCRM\CrmApiV4 */
$crm = app('crmAPI');
// Si pas de compte local.
if (!$model) {
$crmCustomer = $crm->getCustomer($credentials['email']);
if (!$crmCustomer) {
// * Customer is not found on CRM.
return null;
}
$model = $this->createLocalCustomer($crmCustomer);
}
return $model;
} catch (ApiErrorException $e) {
// * Customer is not found.
if ($e->getApiError()->equals(ApiError::modelNotFound)) {
return null;
}
report($e);
Log::error('Unhandled response ' . $e->getApiError()->label());
} catch (CrmException | \Exception $e) {
report($e);
// Une erreur critique est survenue avec la CRM.
return null;
} //end try
Log::error(__CLASS__ . '::retrieveByCredentials reached end of possibilities.');
return null;
}
/**
* Validate a user against the given credentials.
*
* @param UserContract $model
* @param array $credentials
* @return boolean|null
* @throws \RuntimeException If model provided is not a Customer or connection to server fails.
* @throws \LDC\LDCCRM\Exceptions\CrmException On CRM call error.
* @throws \Exception On CRM call error.
*/
public function validateCredentials(UserContract $model, array $credentials): bool|null
{
/** @var \App\Models\Customer $model */
if (\get_class($model) !== $this->usedModel) {
throw new \RuntimeException('Cannot authenticate any thing else than ' . $this->usedModel);
}
try {
/** @var \LDC\LDCCRM\CrmApiV4 */
$crm = app('crmAPI');
// * If has trait soft delete and customer trashed, then restore customer.
if (
\in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, \class_uses_recursive($model)) and
$model->trashed()
) {
$model->restore();
}
// * Ne devrait pas survenir, $crm->login throws ApiError::authFailed()
return $crm->login($credentials['email'], $credentials['password']);
} catch (ApiErrorException $e) {
if (
$e->getApiError()->equals(ApiError::modelNotFound) ||
($e->getApiError()->equals(ApiError::authFailed) && !$model->existsInCrm())
) {
$model->delete();
return false;
}
if ($e->getApiError()->equals(ApiError::authFailed)) {
return false;
}
// ! Assert Authenticable model must be valided (MustValidate trait).
if ($e->getApiError()->equals(ApiError::customerNotActivated)) {
$model->email_verified_at = null;
$model->save();
$model->sendEmailVerificationNotification();
Session::flash('warning', \trans('front.messages.account_not_activated'));
return true;
}
} catch (CrmException | \Exception $e) {
report($e);
if (config('app.debug')) {
throw $e;
}
return false;
} //end try
Log::error(__CLASS__ . '::validateCredentials reached end of possibilities.');
return null;
}
/**
* Create a local customer
*
* @param \LDC\LDCCRM\Models\Customer $crmCustomer
* @return \Illuminate\Database\Eloquent\Model
* @throws \Throwable If saving to DB failed.
*/
private function createLocalCustomer(\LDC\LDCCRM\Models\Customer $crmCustomer): Model
{
/** @var \App\Models\Customer $model */
$model = $this->usedModel::withTrashed()->firstOrNew([
'email' => $crmCustomer->email
]);
$model->hashcode = $crmCustomer->hashcode;
$model->email_verified_at = $crmCustomer->status ? now() : null;
if ($model->trashed()) {
$model->restore();
}
$model->saveOrFail();
return $model;
}
/**
* Retrieve a trashed customer by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
* @throws \ErrorException If cannot connect to LDAP.
*/
private function retrieveTrashed(array $credentials): ?Authenticatable
{
// * Make changes
if (empty($credentials)) {
return null;
}
// First we will add each credential element to the query as a where clause.
// Then we can execute the query and, if we found a customer, return it in a
// Eloquent User "model" that will be utilized by the Guard instances.
$query = $this->createModel()->newQuery();
foreach ($credentials as $key => $value) {
if (!Str::contains($key, 'password')) {
$query->where($key, $value);
}
}
/** @var \App\Models\Customer|null $query */
return $query->withTrashed()->first();
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class AsArrayOfDiscount implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return array<\App\Lib\Discount>
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/
public function get($model, string $key, $value, array $attributes): array
{
// phpcs:enable
return \collect(\json_decode($value, true))->map(function ($input) use ($model, $key, $attributes) {
$inputCast = new AsDiscount();
return $inputCast->get($model, $key, \json_encode($input), $attributes);
})->all();
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param \App\Lib\Discount $value
* @param array $attributes
* @return string Json encoded array<\App\Lib\Discount>
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/
public function set($model, string $key, $value, array $attributes)
{
// phpcs:enable
return \json_encode(\collect($value)->map(function ($input) use ($model, $key, $attributes) {
$inputCast = new AsDiscount();
return \json_decode($inputCast->set($model, $key, $input, $attributes));
})->all());
}
}

65
app/Casts/AsDiscount.php Normal file
View file

@ -0,0 +1,65 @@
<?php
namespace App\Casts;
use App\Lib\Discount;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
class AsDiscount implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return \App\Lib\Discount
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/
public function get($model, string $key, $value, array $attributes): Discount
{
// phpcs:enable
$inputData = \json_decode($value, true);
return Discount::make(...\array_values($inputData));
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return string Json encoded \App\Lib\Inputs\Definition\Input
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/
public function set($model, string $key, $value, array $attributes)
{
// phpcs:enable
$this->assertIsInput($value);
/** @var \App\Lib\Discount $value */
return $value->toJson();
}
/**
* Assert is instance of \App\Lib\Discount
*
* @param mixed $value
* @return void
* @throws \RuntimeException If the value is not an input.
*/
private function assertIsInput(mixed $value): void
{
if (!\is_object($value) or !($value instanceof Discount)) {
$value = \json_encode($value);
throw new \RuntimeException(
__CLASS__ . ' needs value to be array of \'' . Discount::class . "' given is {$value}"
);
}
}
}

61
app/Casts/Ip.php Normal file
View file

@ -0,0 +1,61 @@
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Database\Eloquent\Model;
/**
* @template TGet
* @template TSet
*/
class Ip implements CastsAttributes
{
/**
* Transform the attribute from the underlying model values.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array<string,mixed> $attributes
* @return \IPLib\Address\AddressInterface|null
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
*/
public function get(Model $model, string $key, mixed $value, array $attributes)
{
// phpcs:enable
return \IPLib\Factory::parseAddressString($value);
}
/**
* Transform the attribute to its underlying model values.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param \IPLib\Address\AddressInterface|string|null $value
* @param array<string,mixed> $attributes
* @return mixed
* @throws \RuntimeException If value is not or instance of \IPLib\Address\AddressInterface.
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
*/
public function set(Model $model, string $key, mixed $value, array $attributes)
{
// phpcs:enable
if (is_null($value)) {
return null;
}
if (is_string($value)) {
$value = \IPLib\Factory::parseAddressString($value);
}
if (!is_object($value) or !($value instanceof \IPLib\Address\AddressInterface)) {
throw new \RuntimeException(
"{$key} has to be instance of " .
\IPLib\Address\AddressInterface::class
);
}
$value->getAddressType();
return $value;
}
}

View file

@ -1,57 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
class CreateAdmin extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'create:admin';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$firstname = $this->ask('What is the firstname?', 'Informatique');
$lastname = $this->ask('What is the lastname?', 'Internet');
$email = $this->ask('What is the email?', 'equipe.web@ldc.fr');
$pwd = $this->secret('What is the password?');
$role = $this->ask('Role ?', '1');
$user = new User();
$user->firstname = $firstname;
$user->lastname = $lastname;
$user->birth_date = now();
$user->email = $email;
$user->password = Hash::make($pwd);
$user->address = "-";
$user->town = "Le Mans";
$user->postal_code = "72000";
$user->country = "France";
$user->email_verify_token = "-";
$user->email_verified = 1;
$user->role = $role;
$user->save();
$this->info('User created.');
return 0;
}
}

View file

@ -1,39 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
class DeleteNotConfirmedUser extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'users:deletenc';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Remove users who have not confirmed their accounts within 24 hours of registration';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$users = User::where([['email_verified', 0], ['created_at', '<', Carbon::now()->subDay()]])->get();
foreach ($users as $user) {
$user->delete();
}
return 0;
}
}

View file

@ -1,192 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Mail\ExportCodesUniques as MailExportCodesUniques;
use App\Models\Code;
use App\Models\Settings;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class ExportCodesUniques extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'export:codeunique';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
// Nombre total de code à exporter.
$nbCodeTot = $this->ask("Combien de code doivent être exporté ?");
while (empty(intval($nbCodeTot)) || !is_int(intval($nbCodeTot))) {
$this->info("Valeur incorrect, veuillez entrer un nombre entier.");
$nbCodeTot = $this->ask("Veuillez entrer une nouvelle valeur");
}
$nbCodeTot = intval($nbCodeTot);
// Nombre de code par fichier.
$nbCodeFic = $this->ask("Combien de code par fichier ?");
while (empty(intval($nbCodeFic)) || !is_int(intval($nbCodeFic))) {
$this->info("Valeur incorrect, veuillez entrer un nombre entier.");
$nbCodeFic = $this->ask("Veuillez entrer une nouvelle valeur");
}
$nbCodeFic = intval($nbCodeFic);
// Nombre de code par ligne.
$nbCodeLigne = $this->ask("Combien de code par ligne dans un fichier ?");
while (empty(intval($nbCodeLigne)) || !is_int(intval($nbCodeLigne))) {
$this->info("Valeur incorrect, veuillez entrer un nombre entier.");
$nbCodeLigne = $this->ask("Veuillez entrer une nouvelle valeur");
}
$nbCodeLigne = intval($nbCodeLigne);
$totalExportedCodes = 0;
$currentFilename = 1;
$currentExportedCodes = 0;
$currentLineCodes = ($nbCodeLigne === 1 ? 1 : 0);
$this->info("Traitement en cours, veuillez patienter...");
$this->process(
$nbCodeFic,
$nbCodeTot,
$nbCodeLigne,
$totalExportedCodes,
$currentFilename,
$currentExportedCodes,
$currentLineCodes
);
$this->info("Fin du traitement, creation de l'archive...");
// Compression des fichiers.
$zip = new \ZipArchive();
if (
$zip->open(
storage_path('export_codes_uniques/') . "CodesUniques_" . date('dmY') . ".zip",
\ZipArchive::CREATE
) === true
) {
if ($handle = opendir(storage_path('export_codes_uniques/tmp'))) {
while (false !== ($file_to_zip = readdir($handle))) {
if (pathinfo($file_to_zip, PATHINFO_EXTENSION) != 'csv') {
continue;
}
$zip->addFile(storage_path('export_codes_uniques/tmp/') . $file_to_zip, "$file_to_zip");
}
closedir($handle);
}
$zip->close();
}
// On vide le dossier temporaire des fichiers csv.
if ($handle = opendir(storage_path('export_codes_uniques/tmp'))) {
while (false !== ($file_to_zip = readdir($handle))) {
if (pathinfo($file_to_zip, PATHINFO_EXTENSION) != 'csv') {
continue;
}
unlink(storage_path('export_codes_uniques/tmp/') . $file_to_zip);
}
closedir($handle);
}
$this->info("L'archive CodesUniques_" . date('dmY') . ".zip a ete creer.");
// Envoi de mail à MAIL_ADMIN avec le nom du ZIP "CodesUniques_". date('dmY') .".zip" .
Mail::to(config('mail.MAIL_ADMIN'))
->send(new MailExportCodesUniques(app(Settings::class)->name, $nbCodeFic, $nbCodeTot, $nbCodeLigne));
return 0;
}
private function process($nbCodeFic, $nbCodeTot, $nbCodeLigne, $totalExportedCodes, $currentFilename, $currentExportedCodes, $currentLineCodes, $lastExportedCodeId = null)
{
$paginate = config('codes.paginate');
if ($nbCodeTot < $paginate) {
$nbCodeToRetrieve = $nbCodeTot;
} else {
if ($totalExportedCodes + $paginate > $nbCodeTot) {
$nbCodeToRetrieve = $nbCodeTot - $totalExportedCodes;
} else {
$nbCodeToRetrieve = $paginate;
}
}
if ($lastExportedCodeId === null) {
$codes = Code::where('code_exported', 0)->take($nbCodeToRetrieve)->get();
} else {
$codes = Code::where([
['code_exported', '=', 0],
['id', '>', $lastExportedCodeId]
])->take($nbCodeToRetrieve)->get();
}
$countCodes = count($codes);
$exportedCodesId = [];
foreach ($codes as $k => $code) {
if ($currentExportedCodes < $nbCodeTot) {
if ($currentLineCodes === ($nbCodeLigne === 1 ? 1 : $nbCodeLigne - 1)) {
file_put_contents(
storage_path('export_codes_uniques/tmp/') . $currentFilename . ".csv",
$code->code_unique . PHP_EOL,
FILE_APPEND
);
$totalExportedCodes++;
$currentExportedCodes++;
$currentLineCodes = ($nbCodeLigne === 1 ? 1 : 0);
} else {
file_put_contents(
storage_path('export_codes_uniques/tmp/') . $currentFilename . ".csv",
$code->code_unique . ";",
FILE_APPEND
);
$totalExportedCodes++;
$currentExportedCodes++;
$currentLineCodes++;
}
array_push($exportedCodesId, $code->id);
$lastExportedCodeId = $code->id;
if ($currentExportedCodes === $nbCodeFic) {
$currentFilename++;
$currentExportedCodes = 0;
}
if ((($k + 1) === $countCodes) && $totalExportedCodes < $nbCodeTot) {
Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]);
return $this->process(
$nbCodeFic,
$nbCodeTot,
$nbCodeLigne,
$totalExportedCodes,
$currentFilename,
$currentExportedCodes,
$currentLineCodes,
$lastExportedCodeId
);
}
if (($k + 1) === $countCodes) {
Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]);
}
} //end if
}//end foreach
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
class TestMail extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:mail:test';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Send test email to equipe.web@ldc.fr';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
Mail::raw(sprintf('UWIL mail-test from %s %s', \config('app.name'), \config('app.url')), function ($msg) {
$msg->to('equipe.web@ldc.fr')->subject('Test Email');
});
}
}

View file

@ -0,0 +1,77 @@
<?php
namespace App\Console\Commands\Theme;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use function Laravel\Prompts\search;
class SwitchCommand extends Command implements PromptsForMissingInput
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:theme:switch {theme : The theme folder name on theme-merge project}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Switch GGW theme and install it';
/**
* Prompt for missing input arguments using the returned questions.
*
* @return array
*/
protected function promptForMissingArgumentsUsing()
{
return [
'theme' => fn () => search(
label: 'Search for a theme:',
placeholder: 'default-theme',
options: fn (string $value): array => \collect(File::directories(\resource_path('theme')))
->map(fn (string $dir_path) => \pathinfo($dir_path, \PATHINFO_BASENAME))
->filter(fn (string $dir_path) => $value ? Str::of($dir_path)->contains($value) : true)
->filter(fn (string $dir_path) => $dir_path !== 'mounted')
->all()
)
];
}
/**
* Execute the console command.
*
* @return void
*/
public function handle(): void
{
$mount_path = \resource_path('theme/mounted');
/** @var string */
$theme = $this->argument('theme');
if (File::exists($mount_path)) {
\throw_if(
empty(ToolboxHelper::runConsoleCommand("readlink {$mount_path}", false)),
"{$mount_path} is not a link, cannot remove it"
);
\throw_if(
!File::delete($mount_path),
"{$mount_path} Failed to remove file"
);
}
File::link(\resource_path("theme/{$theme}"), $mount_path);
Artisan::call('optimize:clear');
}
}

View file

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

View file

@ -2,6 +2,7 @@
namespace App\Console;
use App\Jobs\DeleteOlds\DeleteOldCustomers;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -22,6 +23,9 @@ class Kernel extends ConsoleKernel
*/
protected function schedule(Schedule $schedule)
{
// ! ATTENTION: Le job DeleteOldCustomers a été désactivé, car il n'est pas applicable pour l'instant.
// ! Il faudra décorreler les customers des gains et participations via les FK.
// $schedule->job(DeleteOldCustomers::class)->dailyAt('22:50');
}
/**

View file

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

25
app/Enums/CodeType.php Normal file
View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum CodeType: int
{
use BaseEnum;
case unique = 1;
case ean = 2;
case both = 3;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::unique->name => 'Codes uniques',
self::ean->name => 'Codes EAN',
self::both->name => 'Codes uniques et codes EAN'
];
}

30
app/Enums/InputType.php Normal file
View file

@ -0,0 +1,30 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum InputType: int
{
use BaseEnum;
case boolean = 1;
case dropdownArray = 2;
case email = 3;
case image = 4;
case imageArray = 5;
case labelArray = 6;
case miniblocArray = 7;
case number = 8;
case selectEnum = 9;
case password = 10;
case string = 11;
case text = 12;
case url = 13;
/** Vimeo only */
case videoVimeo = 14;
/** Youtube only */
case videoYoutube = 15;
/** Youtube or Vimeo. */
case video = 16;
}

View file

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

View file

@ -0,0 +1,25 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum StaticPageType: int
{
use BaseEnum;
case legals = 1;
case rules = 2;
case confidentiality = 3;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::legals->name => 'Mentions légales',
self::rules->name => 'Règlement',
self::confidentiality->name => 'Charte de confidentialité'
];
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Enums\Theme;
use App\Traits\Enums\BaseEnum;
enum BootstrapThemeEnum: int
{
use BaseEnum;
case light = 1;
case dark = 2;
/**
* Optionnal labels definition
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::light->name => 'Light',
self::dark->name => 'Dark',
];
}

View file

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

View file

@ -2,26 +2,47 @@
namespace App\Exceptions;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
* A list of exception types with their corresponding custom log levels.
*
* @var array
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $dontReport = [
RedirectTo::class
];
protected $levels = [];
/**
* A list of the inputs that are never flashed for validation exceptions.
* A list of the exception types that are not reported.
*
* @var array
* @var array<int, class-string<\Throwable>>
*/
protected $dontReport = [];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
}

View file

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

View file

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class LoginController extends Controller
{
use AuthenticatesUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:backend')->except('logout');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('backend');
}
/**
* Show the application's login form.
*
* @return \Illuminate\Contracts\View\View
*/
public function showLoginForm(): View
{
return view('back::auth.login');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string
{
return response()->redirectToIntended()->getTargetUrl();
}
/**
* Attempt to log the user into the application.
*
* @param \Illuminate\Http\Request $request
* @return boolean
*/
protected function attemptLogin(Request $request)
{
$credentials = $this->credentials($request);
// * Attempt login only using published users.
$credentials['published'] = true;
return $this->guard()->attempt(
$credentials,
$request->boolean('remember')
);
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Http\Controllers\Bo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Support\Facades\Auth;
class ResetPasswordController extends Controller
{
use ResetsPasswords;
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('backend');
}
/**
* Where to redirect users after login.
*
* @return string
*/
protected function redirectTo(): string
{
return \route('bo.login');
}
}

View file

@ -3,38 +3,160 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\BannedIps\StoreBannedIpRequest;
use App\Http\Requests\Bo\BannedIps\UpdateBannedIpRequest;
use App\Models\BannedIp;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class BannedIpController extends Controller
{
public function index()
/**
* Create the controller instance.
*/
public function __construct()
{
$ips = BannedIp::all();
return view('bo.banned-ip.index', compact('ips'));
$this->authorizeResource(BannedIp::class);
}
public function saveDatas(Request $request)
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
$ips = $request->ips;
/** @var \Illuminate\Database\Eloquent\Builder $bannedipModels */
$bannedipModels = BannedIp::query();
for ($i = 0; $i < count($ips); $i++) {
if (!empty($ips[$i]["ip"])) {
$ip = BannedIp::updateOrCreate(['ip' => $ips[$i]["ip"]]);
} else {
unset($ips[$i]);
$i--;
}
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$bannedipModels,
$search,
null,
'ip',
);
}
$searchFields = trans('validation.custom.ip');
return $ips;
/** Sort columns with a query */
$this->sortQuery($bannedipModels);
/** Custom pagination */
$bannedipModels = $this->paginate($bannedipModels);
return view('back::pages.banned-ips.index', compact('bannedipModels', 'search', 'searchFields'));
}
public function destroy(Request $request)
/**
* Show the form for creating a new resource.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Contracts\View\View
*/
public function create(BannedIp $banned_ip): \Illuminate\Contracts\View\View
{
$ip = BannedIp::where("ip", $request->ip);
$ip->delete();
return view('back::pages.banned-ips.create', [
'bannedipModel' => $banned_ip,
]);
}
return "Suppression terminée !";
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\BannedIps\StoreBannedIpRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreBannedIpRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$banned_ip = new BannedIp();
$banned_ip->fill($request->validated());
if ($banned_ip->saveOrFail()) {
return redirect()
->route('bo.banned_ips.edit', $banned_ip)
->with('success', trans(
'crud.messages.has_been_created',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Contracts\View\View
*/
public function edit(BannedIp $banned_ip): \Illuminate\Contracts\View\View
{
return view('back::pages.banned-ips.edit', [
'bannedipModel' => $banned_ip,
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\BannedIps\UpdateBannedIpRequest $request
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateBannedIpRequest $request, BannedIp $banned_ip): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $banned_ip) {
$banned_ip->fill($request->validated());
if ($banned_ip->saveOrFail()) {
return redirect()
->route('bo.banned_ips.edit', $banned_ip)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(BannedIp $banned_ip): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($banned_ip) {
if ($banned_ip->deleteOrFail()) {
return redirect()
->route('bo.banned_ips.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
}
return redirect()
->back()
->with('error', __(
':crud.messages.cannot_be_deleted',
['model' => Str::singular(trans('models.classes.banned_ips'))]
));
});
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Codes\CodeSearchRequest;
use App\Http\Requests\Bo\Codes\ExportCodesRequest;
use App\Http\Requests\Bo\Codes\GenerateCodesRequest;
use App\Jobs\ProcessExportCodesUniques;
use App\Jobs\ProcessUniqueCode;
use App\Models\Code;
use App\Models\Participation;
use App\Models\Settings;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Queue;
use Kwaadpepper\Serial\Exceptions\SerialCasterException;
class CodeController extends Controller
{
/**
* Show code form to generate and export
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
$this->authorize('generate', Code::class);
/** @var \App\Models\Settings */
$settings = app(Settings::class);
$nbCodeBase = Code::query()->max('id');
$nbCodeUsed = Code::query()->whereHas(
'participations',
operator: '>',
count: $settings->nb_unique_code_use
)->count();
$nbCodeExported = Code::query()->where('code_exported', true)->count();
$nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count();
return view('back::pages.codes.index', compact(
'settings',
'nbCodeBase',
'nbCodeUsed',
'nbCodeExported',
'nbCodeRemainingToExport'
));
}
/**
* Run generate codes action
*
* @param \App\Http\Requests\Bo\Codes\GenerateCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function generate(GenerateCodesRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('generate', Code::class);
Queue::push(
new ProcessUniqueCode(
intval($request->quantity),
config('codes.codeLength')
)
);
return back()->with(
'success',
trans(
// phpcs:ignore Generic.Files.LineLength.TooLong
'La tache de génération à été ajoutée, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.',
[
'email' => config('mail.admin')
]
)
);
}
/**
* Run export codes
*
* @param \App\Http\Requests\Bo\Codes\ExportCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function export(ExportCodesRequest $request): \Illuminate\Http\RedirectResponse
{
$this->authorize('export', Code::class);
Queue::push(
new ProcessExportCodesUniques(
intval($request->nb_code_fic),
intval($request->nb_code_tot),
intval($request->nb_code_ligne)
)
);
return back()->with(
'success',
// phpcs:ignore Generic.Files.LineLength.TooLong
__('L\'export des codes à commencé, un mail de notification sera envoyé à l\'adresse :emaillorsque le traitement sera terminé.', [
'email' => config('mail.admin')
])
);
}
/**
* Search for a specific code.
*
* @param \App\Http\Requests\Bo\Codes\CodeSearchRequest $request
* @return \Illuminate\Contracts\View\View
*/
public function search(CodeSearchRequest $request): \Illuminate\Contracts\View\View
{
$this->authorize('search', Code::class);
$code = null;
try {
$code = Code::query()->where('id', Code::serialToInt($request->code))->first();
} catch (\Throwable $e) {
if (\get_class($e) === SerialCasterException::class) {
$code = false;
}
} finally {
if ($code === false) {
\session()->flash('warning', trans('Code non valide.'));
}
if ($code === null) {
\session()->flash('warning', trans('Code introuvable.'));
}
}
$used_counter = null;
if ($code) {
$used_counter = Participation::query()
->whereHas('code', fn(Builder $q) => $q->where('id', $code->id))
->count();
}
return view('back::pages.codes.search', compact('code', 'used_counter'));
}
}

View file

@ -1,104 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\CodeSearchRequest;
use App\Http\Requests\ExportCodesRequest;
use App\Http\Requests\GenerateCodesRequest;
use App\Jobs\ProcessExportCodesUniques;
use App\Jobs\ProcessUniqueCode;
use App\Models\Code;
use App\Models\Settings;
use Kwaadpepper\Serial\Exceptions\SerialCasterException;
class CodesController extends Controller
{
/**
* Show code form to generate and export
*
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
$settings = app(Settings::class);
$nbCodeBase = Code::count();
$nbCodeUsed = Code::where('code_used', 1)->count();
$nbCodeExported = Code::where('code_exported', 1)->count();
$nbCodeRemainingToExport = Code::where('code_exported', 0)->count();
return view('bo.codes.index', compact(
'settings',
'nbCodeBase',
'nbCodeUsed',
'nbCodeExported',
'nbCodeRemainingToExport'
));
}
/**
* Run generate codes action
*
* @param GenerateCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function generate(GenerateCodesRequest $request)
{
ProcessUniqueCode::dispatch(
$request->quantity,
$request->code_length
);
return back()->with(
'success',
"La génération à commencée, un mail de notification sera envoyé à l'adresse " .
config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé."
);
}
/**
* Run export codes
*
* @param ExportCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function export(ExportCodesRequest $request)
{
ProcessExportCodesUniques::dispatch(
intval($request->nb_code_fic),
intval($request->nb_code_tot),
intval($request->nb_code_ligne)
);
return back()->with(
'success',
"L'export des codes à commencé, un mail de notification sera envoyé à l'adresse " .
config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé."
);
}
/**
* Search for a specific code.
*
* @param CodeSearchRequest $request
* @return \Illuminate\View\View|\Illuminate\Contracts\View\Factory
*/
public function search(CodeSearchRequest $request)
{
$code = null;
try {
$code = Code::whereId(Code::serialToInt($request->code))->first();
} catch (\Throwable $e) {
if (\get_class($e) === SerialCasterException::class) {
$code = false;
}
} finally {
if ($code === false) {
\session()->flash('warning', trans('Code non valide.'));
}
if ($code === null) {
\session()->flash('warning', trans('Code introuvable.'));
}
}
return view('bo.codes.search', compact('code'));
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\CodeEan;
use Illuminate\Http\Request;
class CodesEANController extends Controller
{
public function index()
{
$ean = CodeEan::all();
return view('bo.codes-ean.index', compact('ean'));
}
public function saveDatas(Request $request)
{
$codes = $request->codes;
for ($i = 0; $i < count($codes); $i++) {
$codeToInsert = !empty($codes[$i]["code"]) && !empty($codes[$i]["product"]);
if (isset($codes[$i]["id"]) && $codes[$i]["id"] !== null && $codeToInsert) {
$c = CodeEan::findOrFail($codes[$i]["id"]);
$c->code = $codes[$i]["code"];
$c->product = $codes[$i]["product"];
$c->save();
} else {
if ($codeToInsert) {
$c = CodeEan::updateOrCreate(['code' => $codes[$i]["code"], 'product' => $codes[$i]["product"]]);
$codes[$i]["id"] = $c->id;
} else {
unset($codes[$i]);
$i--;
}
}
}
return $codes;
}
public function destroy(Request $request)
{
$code = CodeEan::findOrFail($request->id);
$code->delete();
return "Suppression terminée !";
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\Content;
use Illuminate\Http\Request;
class ContentController extends Controller
{
public function manage($id, Request $request)
{
$content = Content::findOrFail($id);
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$value = $request->value;
$content->value = $value;
$content->save();
return redirect()->route('bo.content', $content->id)->with('success', 'Enregistré !');
}
return view('bo.content.manage', compact('content'));
}
}

View file

@ -6,65 +6,76 @@ use App\Http\Controllers\Controller;
use App\Models\Participation;
use App\Models\Settings;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
class DashboardController extends Controller
{
public function index()
/**
* Dashboard Page
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
// Pour les stats.
$settings = app(Settings::class);
$participations = Participation::selectRaw("COUNT(*) AS count, DATE(created_at) AS date")
->groupByRaw("DATE(created_at)")->get();
$participationsUniquePerDay = Participation::selectRaw("DISTINCT user_id AS user, COUNT(*) AS count, DATE(created_at) AS date")
->groupByRaw("DATE(created_at), user")->get();
$statsUnique = [];
foreach ($participationsUniquePerDay as $participationPerDay) {
$dateUnique = Carbon::parse($participationPerDay->date)->format('d/m/Y');
$statsUnique[$dateUnique] = isset($statsUnique[$dateUnique]) ? $statsUnique[$dateUnique] + 1 : 1;
}
$participationsUnique = Participation::selectRaw("DISTINCT user_id AS user, COUNT(*) AS count")
->groupBy("user")->orderBy("user")->get();
// * Pour les stats.
/** @var \App\Models\Settings */
$settings = app(Settings::class);
$avgParticipUser = 0;
$participationPeriod = [];
$statsUnique = [];
$query = DB::table((new Participation())->getTable());
$dateFrmtr = function (object $row) {
$row->date = Carbon::parse($row->date)->isoFormat('DD/MM/YYYY');
return $row;
};
// Données calculées à afficher.
$totalParticipation = Participation::count();
if (count($participationsUnique) > 0) {
$avgParticipUser = round($totalParticipation / count($participationsUnique), 1);
} else {
$avgParticipUser = 0;
/** @var \Illuminate\Support\Collection<string,object> */
$participationsPerDay = (clone $query)
->selectRaw('COUNT(*) AS count, DATE(created_at) AS date')
->groupByRaw('DATE(created_at)')->get()->map($dateFrmtr)->keyBy('date');
/** @var \Illuminate\Support\Collection<object> */
$participationsUniquePerDay = (clone $query)
->selectRaw('DISTINCT customer_id AS user, COUNT(*) AS count, DATE(created_at) AS date')
->groupByRaw('DATE(created_at), user')->get()->map($dateFrmtr);
/** @var \Illuminate\Support\Collection<object> */
$participationsUniquePerCustomer = (clone $query)
->selectRaw("DISTINCT customer_id AS user, COUNT(*) AS count")
->groupBy('user')->orderBy('user')->get();
$participationsTotal = Participation::query()->count();
/** @var \Illuminate\Support\Collection<object> 3 codes EAN les plus utilisés */
$codesEan = (clone $query)
->selectRaw('COUNT(product_id) AS nb_usage, product_id, ean_code')
->join('products', 'product_id', '=', 'products.id')
->groupBy('product_id', 'ean_code')
->orderByDesc('nb_usage')->limit(3)->get();
$participationsUniquePerDay->each(function (object $row) use (&$statsUnique) {
Arr::set($statsUnique, $row->date, (Arr::get($statsUnique, $row->date) ?? 0) + 1);
});
// * Calcul participation moyenne par utilisateur.
if ($participationsUniquePerCustomer->count() > 0) {
$avgParticipUser = round($participationsTotal / $participationsUniquePerCustomer->count(), 1);
}
// Tableau des dates comprises dans la table participation.
foreach ($participations as $participation) {
$datesParticipations[Carbon::parse($participation->date)->format('d/m/Y')] = $participation->count;
}
// * Création des tableaux avec toutes les dates depuis le début du jeu
// * et le nombre de participations associées pour le graph JS.
$start_date = $settings->start_date;
$end_date = now()->lt($settings->end_date) ? now() : $settings->end_date;
$dates = $participationsPerDay->keys();
$count = $participationsPerDay->pluck('count');
$countUnique = $dates->map(fn (string $formated_date) => Arr::get($statsUnique, $formated_date, 0));
// Création des tableaux avec toutes les dates depuis le début du jeu et le nombre de participations associées.
$date = Carbon::parse($settings->start_date);
$dateFin = now() < $settings->end_date ? Carbon::now() : Carbon::parse($settings->end_date);
// Récupération des 3 codes EAN les plus utilisés.
$codesEan = Participation::select(
DB::raw('COUNT(code_ean_id) AS nb_usage, code_ean_id')
)->groupBy('code_ean_id')->orderByDesc('nb_usage')->limit(3)->get();
// Génération des tableaux de date depuis le début du jeu.
$dates = [];
$count = [];
$countUnique = [];
while ($dateFin > $date) {
$dates[] = $date->format('d/m/Y');
$count[] = isset($datesParticipations[$date->format('d/m/Y')]) ?
$datesParticipations[$date->format('d/m/Y')] : 0;
$countUnique[] = isset($statsUnique[$date->format('d/m/Y')]) ? $statsUnique[$date->format('d/m/Y')] : 0;
$date->addDay();
}
return view('bo.dashboard.index', compact(
return view('back::pages.dashboard.index', compact(
'dates',
'count',
'countUnique',
'totalParticipation',
'participationsTotal',
'avgParticipUser',
'codesEan',
'settings'

View file

@ -0,0 +1,180 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\FaqQuestions\StoreFaqQuestionRequest;
use App\Http\Requests\Bo\FaqQuestions\UpdateFaqQuestionRequest;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class FaqQuestionController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(FaqQuestion::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $faqQuestionModels */
$faqQuestionModels = FaqQuestion::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$faqQuestionModels,
$search,
null,
'question',
'answer'
);
}
$searchFields = \sprintf(
'%s, %s ...',
Str::ucfirst(__('validation.custom.question')),
__('validation.custom.answer'),
);
/** Sort columns with a query */
$this->sortQuery($faqQuestionModels);
/** Custom pagination */
$faqQuestionModels = $this->paginate($faqQuestionModels);
return view('back::pages.faq-questions.index', compact('faqQuestionModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Contracts\View\View
*/
public function create(FaqQuestion $faqQuestion): \Illuminate\Contracts\View\View
{
$faqThemeModels = FaqTheme::all();
return view('back::pages.faq-questions.create', [
'faqQuestionModel' => $faqQuestion,
'faqThemeModels' => $faqThemeModels
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\FaqQuestions\StoreFaqQuestionRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreFaqQuestionRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$faqQuestion = new FaqQuestion();
$faqQuestion->fill($request->validated());
if ($faqQuestion->saveOrFail()) {
return redirect()
->route('bo.faq-questions.edit', $faqQuestion)
->with(
'success',
trans(
'crud.messages.has_been_created',
['model' => trans_choice('models.classes.faq-questions', 1)]
)
);
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => trans_choice('models.classes.faq-questions', 1)]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Contracts\View\View
*/
public function edit(FaqQuestion $faqQuestion): \Illuminate\Contracts\View\View
{
$faqThemeModels = FaqTheme::all();
return view('back::pages.faq-questions.edit', [
'faqQuestionModel' => $faqQuestion,
'faqThemeModels' => $faqThemeModels
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\FaqQuestions\UpdateFaqQuestionRequest $request
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateFaqQuestionRequest $request,
FaqQuestion $faqQuestion
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $faqQuestion) {
$faqQuestion->fill($request->validated());
if ($faqQuestion->saveOrFail()) {
return redirect()
->route(
'bo.faq-questions.edit',
$faqQuestion
)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => trans_choice('models.classes.faq-questions', 1)]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => trans_choice('models.classes.faq-questions', 1)]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\FaqQuestion $faqQuestion
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(FaqQuestion $faqQuestion): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($faqQuestion) {
if ($faqQuestion->deleteOrFail()) {
return redirect()
->route('bo.faq-questions.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => trans_choice('models.classes.faq-questions', 1)]
));
}
return redirect()
->back()
->with('error', __(
':crud.messages.cannot_be_deleted',
['model' => trans_choice('models.classes.faq-questions', 1)]
));
});
}
}

View file

@ -1,59 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
use Illuminate\Http\Request;
class FaqQuestionsController extends Controller
{
public function index()
{
$questions = FaqQuestion::all();
for ($i = 0; $i < count($questions); $i++) {
$questions[$i]['toggle'] = false;
}
$themes = FaqTheme::all();
return view('bo.faq-questions.index', compact('questions', 'themes'));
}
public function destroy(Request $request)
{
if ($request->id !== null) {
$question = FaqQuestion::findOrFail($request->id);
$question->delete();
}
}
public function saveDatas(Request $request)
{
$questions = $request->questions;
for ($i = 0; $i < count($questions); $i++) {
if (isset($questions[$i]["id"])) {
$q = FaqQuestion::findOrFail($questions[$i]["id"]);
$q->question = $questions[$i]["question"];
$q->answer = $questions[$i]["answer"];
$q->positive_vote = $questions[$i]["positive_vote"];
$q->negative_vote = $questions[$i]["negative_vote"];
$q->theme_id = $questions[$i]["theme_id"];
$q->save();
} else {
$t = FaqQuestion::updateOrCreate(
[
'question' => $questions[$i]["question"],
'answer' => $questions[$i]["answer"],
'positive_vote' => $questions[$i]["positive_vote"],
'negative_vote' => $questions[$i]["negative_vote"],
'theme_id' => $questions[$i]["theme_id"]
]
);
$questions[$i]["id"] = $t->id;
}
}//end for
return $questions;
}
}

View file

@ -0,0 +1,170 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\FaqThemes\StoreFaqThemeRequest;
use App\Http\Requests\Bo\FaqThemes\UpdateFaqThemeRequest;
use App\Models\FaqTheme;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class FaqThemeController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(FaqTheme::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $faqThemeModels */
$faqThemeModels = FaqTheme::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$faqThemeModels,
$search,
null,
'name',
);
}
$searchFields = Str::ucfirst(__('validation.attributes.name'));
/** Sort columns with a query */
$this->sortQuery($faqThemeModels);
/** Custom pagination */
$faqThemeModels = $this->paginate($faqThemeModels);
return view('back::pages.faq-themes.index', compact('faqThemeModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Contracts\View\View
*/
public function create(FaqTheme $faqTheme): \Illuminate\Contracts\View\View
{
return view('back::pages.faq-themes.create', [
'faqThemeModel' => $faqTheme,
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\FaqThemes\StoreFaqThemeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreFaqThemeRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$faqTheme = new FaqTheme();
$faqTheme->fill($request->validated());
if ($faqTheme->saveOrFail()) {
return redirect()
->route('bo.faq-themes.edit', $faqTheme)
->with(
'success',
trans(
'crud.messages.has_been_created',
['model' => trans_choice('models.classes.faq-themes', 1)]
)
);
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => trans_choice('models.classes.faq-themes', 1)]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Contracts\View\View
*/
public function edit(FaqTheme $faqTheme): \Illuminate\Contracts\View\View
{
return view('back::pages.faq-themes.edit', [
'faqThemeModel' => $faqTheme,
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\FaqThemes\UpdateFaqThemeRequest $request
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateFaqThemeRequest $request,
FaqTheme $faqTheme
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $faqTheme) {
$faqTheme->fill($request->validated());
if ($faqTheme->saveOrFail()) {
return redirect()
->route(
'bo.faq-themes.edit',
$faqTheme
)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => trans_choice('models.classes.faq-themes', 1)]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => trans_choice('models.classes.faq-themes', 1)]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\FaqTheme $faqTheme
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(FaqTheme $faqTheme): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($faqTheme) {
if ($faqTheme->deleteOrFail()) {
return redirect()
->route('bo.faq-themes.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => trans_choice('models.classes.faq-themes', 1)]
));
}
return redirect()
->back()
->with('error', __(
'crud.messages.cannot_be_deleted',
['model' => trans_choice('models.classes.faq-themes', 1)]
));
});
}
}

View file

@ -1,51 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
use Illuminate\Http\Request;
class FaqThemesController extends Controller
{
public function index()
{
$themes = FaqTheme::all();
return view('bo.faq-themes.index', compact('themes'));
}
public function destroy(Request $request)
{
$deleteOk = 0;
if (FaqQuestion::where('theme_id', $request->id)->count() > 0) {
$deleteOk = 1;
} elseif ($request->id !== null) {
$theme = FaqTheme::findOrFail($request->id);
$theme->delete();
} else {
$deleteOk = 2;
}
return $deleteOk;
}
public function saveDatas(Request $request)
{
$themes = $request->themes;
for ($i = 0; $i < count($themes); $i++) {
if (isset($themes[$i]["id"])) {
$t = FaqTheme::findOrFail($themes[$i]["id"]);
$t->name = $themes[$i]["name"];
$t->save();
} else {
$t = FaqTheme::updateOrCreate(['name' => $themes[$i]["name"]]);
$themes[$i]["id"] = $t->id;
}
}
return $themes;
}
}

View file

@ -1,47 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\GainView;
use Illuminate\Http\Request;
class GainViewController extends Controller
{
public function index()
{
$gv = GainView::all();
return view('bo.gain-views.index', compact('gv'));
}
public function destroy(Request $request)
{
$retour = "Suppression terminée !";
if ($request->id !== null) {
$gv = GainView::findOrFail($request->id);
$gv->delete();
}
return $retour;
}
public function saveDatas(Request $request)
{
$gv = $request->gainViews;
for ($i = 0; $i < count($gv); $i++) {
if (isset($gv[$i]["id"])) {
$g = GainView::findOrFail($gv[$i]["id"]);
$g->name = $gv[$i]["name"];
$g->file_name = $gv[$i]["file_name"];
$g->save();
} else {
$g = GainView::updateOrCreate(['name' => $gv[$i]["name"], 'file_name' => $gv[$i]["file_name"]]);
$gv[$i]["id"] = $g->id;
}
}
return $gv;
}
}

View file

@ -6,104 +6,177 @@ use App\Http\Controllers\Controller;
use App\Models\InstantWin;
use App\Models\Prize;
use App\Models\Settings;
use Carbon\CarbonInterval;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class InstantWinController extends Controller
{
public function index()
/**
* Create the controller instance.
*/
public function __construct()
{
$nbInstantWin = InstantWin::count();
$canGenerate = 0;
$this->authorizeResource(InstantWin::class);
}
if (intval($nbInstantWin) > 0) {
$canGenerate = 0;
} else {
$nbLots = Prize::count();
if (intval($nbLots) > 0) {
$canGenerate = 1;
} else {
$canGenerate = 0;
}
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
$instantWinModelList = InstantWin::query()->orderBy('available_at')->get();
$instantWin = InstantWin::orderBy('appear_date')->get();
return view('bo.instant-win.index', [
'can_generate' => $canGenerate,
'instant_win' => $instantWin
return view('back::pages.instant-win.index', [
'instantWinModelList' => $instantWinModelList
]);
}
public function instantWinGenerate()
/**
* Returns generated instant win.
*
* @return \Illuminate\Http\JsonResponse
*/
public function generate(): JsonResponse
{
$start_date = Settings::latest()->value("start_date");
$end_date = Settings::latest()->value("end_date");
$lots = Prize::all();
$this->authorize('generate', InstantWin::class);
return DB::transaction(function () {
/**
* À partir de quelle heure on peut gagner
* ! Seul l'heure sera prize en compte
*/
$day_start = now()->setTime(6, 0);
/**
* Jusqu'à quelle heure on peut gagner
* ! Seul l'heure sera prize en compte
*/
$day_end = now()->setTime(22, 29);
/** @var \App\Models\Settings */
$settings = app(Settings::class);
$prizeModelList = Prize::all();
$begin = new \DateTime($start_date);
$end = new \DateTime($end_date);
$interval = new \DateInterval('P1D');
$daterange = new \DatePeriod($begin, $interval, $end);
$dateGlobal = [];
$lotsGlobal = [];
/** @var \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar> */
$datesGlobal = collect();
/**
* @var \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement>
* Liste de tous les lots à gagner
*/
$lotsGlobal = collect();
// * Seed srand !
\mt_srand(time());
// * Init prize list
$prizeModelList->each(function (Prize $prize) use ($lotsGlobal, $day_start, $day_end) {
for ($i = 0; $i < $prize->quantity; $i++) {
$el = new WinTimeElement();
$el->prize = $prize;
$el->time = now()->setTime(
mt_rand($day_start->hour, $day_end->hour),
mt_rand($day_start->minute, $day_end->minute)
);
$lotsGlobal->push($el);
}
});
foreach ($lots as $k => $lot) {
for ($i = 0; $i < $lot->quantity; $i++) {
$lotsGlobal[] = [
'id' => $lot->id,
'name' => $lot->label_one,
'hour' => str_pad(mt_rand(6, 22), 2, "0", STR_PAD_LEFT)
. ":" . str_pad(mt_rand(0, 59), 2, "0", STR_PAD_LEFT) . ":00"
];
$lotsGlobal->shuffle(time());
// * Init days calendar
foreach ($settings->start_date->toPeriod($settings->end_date, CarbonInterval::make('1day')) as $date) {
$el = new WinDayCalendar();
$el->lots = collect();
$el->time = $date->setTime(0, 0);
$datesGlobal->push($el);
}
}
shuffle($lotsGlobal);
foreach ($daterange as $date) {
array_push($dateGlobal, ['date' => $date->format("Y-m-d"), 'lots' => []]);
}
$res = $this->generate($lotsGlobal, $dateGlobal, 0);
// * Save Instant wins
$this->generateArray($lotsGlobal, $datesGlobal, 0)->each(function (WinDayCalendar $winDayCalendar) {
$winDayCalendar->lots->each(function (WinTimeElement $winTimeElement) {
$instantWin = new InstantWin();
$instantWin->available_at = $winTimeElement->time;
$instantWin->prize()->associate($winTimeElement->prize);
$instantWin->saveOrFail();
});
});
foreach ($res as $date) {
foreach ($date['lots'] as $lot) {
$iw = new InstantWin();
$iw->prize_id = $lot['id'];
$iw->appear_date = date('Y-m-d H:i:s', strtotime($date['date'] . " " . $lot['hour']));
$iw->save();
}
}
$instantWins = InstantWin::all();
return json_encode($instantWins);
// * Output instant wins to ajax response
return response()->json(InstantWin::all());
});
}
private function generate($lotsGlobal, $dateGlobal, $lotsDone)
/**
* Generate instant win.
*
* Même algo que celui implémenté par Romain.
*
* @param \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement> $lotsGlobal
* Liste de tous les lots à gagner
* @param \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar> $dateGlobal
* @param integer $prizesDone
* @return \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinDayCalendar>
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
private function generateArray(Collection $lotsGlobal, Collection $dateGlobal, int $prizesDone): Collection
{
$rKey = array_rand($dateGlobal, 1);
if ($lotsDone < count($dateGlobal)) {
if (count($dateGlobal[$rKey]['lots']) > 0) {
return $this->generate($lotsGlobal, $dateGlobal, $lotsDone);
}
// phpcs:enable
/** @var array-key Un jour sélectionné aléatoire. */
$rKey = $dateGlobal->keys()->random(1)->firstOrFail();
// ? À-t-on déjà assigné un lot par jour (au moins) ?
// ? Le jour aléatoire sélectionné à déja un lot ?
if ($prizesDone < $dateGlobal->count() and $dateGlobal->get($rKey)->lots->count() > 0) {
return $this->generateArray($lotsGlobal, $dateGlobal, $prizesDone);
}
array_push($dateGlobal[$rKey]['lots'], $lotsGlobal[$lotsDone++]);
if ($lotsDone < count($lotsGlobal)) {
return $this->generate($lotsGlobal, $dateGlobal, $lotsDone);
}
foreach ($dateGlobal as $k => $date) {
usort($dateGlobal[$k]['lots'], [$this, 'sortFunction']);
// * Insert next prize on a randowm day
$dateGlobal->get($rKey)->lots->push($lotsGlobal->get($prizesDone++));
// ? On a distribué assez de lots ?
if ($prizesDone < $lotsGlobal->count()) {
return $this->generateArray($lotsGlobal, $dateGlobal, $prizesDone);
}
// * Tri par heure et assignation du jour correct.
$dateGlobal->each(function (WinDayCalendar $date) {
$date->lots = $date->lots->sort(fn (WinTimeElement $a, WinTimeElement $b) =>
$a->time->timestamp - $b->time->timestamp)
->map(function (WinTimeElement $time) use ($date) {
$time->time->setDate(
$date->time->year,
$date->time->month,
$date->time->day
);
return $time;
});
});
return $dateGlobal;
}
private function sortFunction($a, $b)
{
return strtotime($a["hour"]) - strtotime($b["hour"]);
}
}
/**
* @property \App\Models\Prize $prize Le lot à gagner.
* @property \Carbon\Carbon $time Une heure aléatoire.
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
*/
class WinTimeElement extends \stdClass
{
// phpcs:enable
}
/**
* @property \Carbon\Carbon|\Carbon\CarbonInterface $time
* Un jour dans le temps.
* @property \Illuminate\Support\Collection<array-key,\App\Http\Controllers\Bo\WinTimeElement> $lots
* Liste des lots tombant ce jour.
* @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
*/
class WinDayCalendar extends \stdClass
{
// phpcs:enable
}

View file

@ -0,0 +1,88 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\Participation;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
class ParticipationController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Participation::class);
}
/**
* View all winners
*
* @return \Illuminate\Contracts\View\View
*/
public function viewWinnerList(): View
{
$this->authorize('viewWinnerList', Participation::class);
/** @var \Illuminate\Database\Eloquent\Builder $participationModels */
$participationModels = Participation::query()->with(['customer', 'code', 'instantWin', 'product'])->whereHas('instantWin');
/** Sort columns with a query */
$this->sortQuery($participationModels);
/** Custom pagination */
$participationModels = $this->paginate($participationModels);
return view('back::pages.winners-list.index', compact(
'participationModels'
));
}
/**
* Display participation form
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Contracts\View\View
*/
public function setComment(Participation $participation): View
{
$this->authorize('setComment', $participation);
return view('back::pages.winners-list.set-comment', [
'participationModel' => $participation
]);
}
/**
* Set Comment
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
*/
public function setCommentSubmit(Participation $participation): RedirectResponse
{
$this->authorize('setComment', $participation);
$participation->comment = request()->string('comment');
$participation->saveOrFail();
return back()->with('success', \trans(
'Le commentaire à bien été mis à jour'
));
}
/**
* Change prize sent
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
*/
public function changePrizeSent(Participation $participation): RedirectResponse
{
$this->authorize('changePrizeSent', $participation);
$participation->prize_sent = true;
$participation->saveOrFail();
return back()->with('success', \trans(
'Le lot est bien marqué comme envoyé et une notification à été envoyé au gagant'
));
}
}

View file

@ -0,0 +1,176 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Prizes\StorePrizeRequest;
use App\Http\Requests\Bo\Prizes\UpdatePrizeRequest;
use App\Models\Prize;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class PrizeController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Prize::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $prizeModels */
$prizeModels = Prize::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$prizeModels,
$search,
null,
'label',
);
}
$searchFields = \sprintf(
'%s ...',
Str::ucfirst(__('validation.custom.label'))
);
/** Sort columns with a query */
$this->sortQuery($prizeModels);
/** Custom pagination */
$prizeModels = $this->paginate($prizeModels);
return view('back::pages.prizes.index', compact('prizeModels', 'search', 'searchFields'));
}
/**
* Display the specified resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/
public function show(Prize $prize): \Illuminate\Contracts\View\View
{
return view('back::pages.prizes.show', ['prizeModel' => $prize]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/
public function create(Prize $prize): \Illuminate\Contracts\View\View
{
return view('back::pages.prizes.create', [
'prizeModel' => $prize
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Prizes\StorePrizeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StorePrizeRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$prize = new Prize();
$prize->fill($request->validated());
if ($prize->saveOrFail()) {
return redirect()
->route('bo.prizes.edit', $prize)
->with('success', trans(
'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.prize'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.prize'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/
public function edit(Prize $prize): \Illuminate\Contracts\View\View
{
return view('back::pages.prizes.edit', [
'prizeModel' => $prize
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Prizes\UpdatePrizeRequest $request
* @param \App\Models\Prize $prize
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdatePrizeRequest $request, Prize $prize): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $prize) {
$prize->fill($request->validated());
if ($prize->saveOrFail()) {
return redirect()
->route('bo.prizes.edit', $prize)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.prize'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.prize'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Prize $prize): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($prize) {
if ($prize->deleteOrFail()) {
return redirect()
->route('bo.prizes.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.prize'))]
));
}
return redirect()
->back()
->with('error', __(
':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.prize'))]
));
});
}
}

View file

@ -1,101 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\GainView;
use App\Models\Prize;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
class PrizesController extends Controller
{
public function index()
{
$prizes = Prize::all();
$gainViews = GainView::all();
return view('bo.prizes.index', compact('prizes', 'gainViews'));
}
public function saveDatas(Request $request)
{
$prizes = json_decode($request->prizes);
$pdfs = $request->file('pdf');
try {
$request->validate([
'pdf_winning_proof.*' => 'file|mimes:pdf|size:20480',
]);
for ($i = 0; $i < count($prizes); $i++) {
$prize = $prizes[$i];
if ($prize->id !== null) {
$p = Prize::findOrFail($prize->id);
$p->arcadia_id = $prize->arcadia_id;
$p->label_one = $prize->label_one;
$p->label_many = $prize->label_many;
$p->quantity = $prize->quantity;
$p->arcadia_operation_code = $prize->arcadia_operation_code;
$p->arcadia_support_code = $prize->arcadia_support_code;
$p->need_proof = $prize->need_proof;
$p->gain_view_id = $prize->gain_view_id;
if (isset($pdfs[$i])) {
$fileName = Str::slug($prize->label_one) . '-' .
date('d-m-Y') . '.' . $pdfs[$i]->extension();
$p->pdf_winning_proof = $fileName;
$pdfs[$i]->move(public_path('fichiers/pdf/'), $fileName);
$prizes[$i]->pdf_winning_proof = $fileName;
}
$p->save();
} else {
$p = new Prize();
$p->arcadia_id = $prize->arcadia_id;
$p->label_one = $prize->label_one;
$p->label_many = $prize->label_many;
$p->quantity = $prize->quantity;
$p->arcadia_operation_code = $prize->arcadia_operation_code;
$p->arcadia_support_code = $prize->arcadia_support_code;
$p->need_proof = $prize->need_proof;
$p->gain_view_id = $prize->gain_view_id;
if (isset($pdfs[$i])) {
$fileName = Str::slug($prize->label_one) . '-' .
date('d-m-Y') . '.' . $pdfs[$i]->extension();
$p->pdf_winning_proof = $fileName;
$pdfs[$i]->move(public_path('fichiers/pdf/'), $fileName);
$prizes[$i]->pdf_winning_proof = $fileName;
}
$p->save();
$prizes[$i]->id = $p->id;
} //end if
}//end for
} catch (Exception $e) {
//
}//end try
return $prizes;
}
public function destroy(Request $request)
{
$retour = "Suppression terminée !";
if ($request->id !== null) {
try {
$prize = Prize::findOrFail($request->id);
$prize->delete();
} catch (Exception $e) {
$retour = "Erreur lors de la suppression : " . $e->getMessage();
}
} else {
$retour = "Erreur lors de la suppression : Aucun ID envoyé.";
}
return $retour;
}
}

View file

@ -0,0 +1,190 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Products\StoreProductRequest;
use App\Http\Requests\Bo\Products\UpdateProductRequest;
use App\Models\Product;
use App\Models\Range;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ProductController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Product::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $productModels */
$productModels = Product::query()->with('range');
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$productModels,
$search,
null,
'name',
'ean_code',
);
}
$searchFields = \sprintf(
'%s, %s ...',
Str::ucfirst(__('validation.attributes.name')),
__('validation.custom.ean_code'),
);
/** Sort columns with a query */
$this->sortQuery($productModels);
/** Custom pagination */
$productModels = $this->paginate($productModels);
return view('back::pages.products.index', compact('productModels', 'search', 'searchFields'));
}
/**
* Display the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function show(Product $product): \Illuminate\Contracts\View\View
{
return view('back::pages.products.show', ['productModel' => $product]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/
public function create(Product $product): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
{
$rangeModels = Range::all();
if (!count($rangeModels)) {
return \redirect()->route('bo.ranges.index')
->with(
'error',
'Vous devez ajouter une gamme avant de pouvoir ajouter un produit'
);
}
return view('back::pages.products.create', [
'productModel' => $product,
'rangeModels' => $rangeModels
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Products\StoreProductRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreProductRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$product = new Product();
$product->fill($request->validated());
if ($product->saveOrFail()) {
return redirect()
->route('bo.products.edit', $product)
->with('success', trans(
'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.product'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.product'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function edit(Product $product): \Illuminate\Contracts\View\View
{
$rangeModels = Range::all();
return view('back::pages.products.edit', [
'productModel' => $product,
'rangeModels' => $rangeModels
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Products\UpdateProductRequest $request
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateProductRequest $request, Product $product): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $product) {
$product->fill($request->validated());
if ($product->saveOrFail()) {
return redirect()
->route('bo.products.edit', $product)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.product'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.product'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Product $product
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Product $product): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($product) {
if ($product->deleteOrFail()) {
return redirect()
->route('bo.products.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.product'))]
));
}
return redirect()
->back()
->with('error', __(
':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.product'))]
));
});
}
}

View file

@ -1,155 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreProductRequest;
use App\Http\Requests\UpdateProductRequest;
use App\Models\Product;
use App\Models\Range;
use Illuminate\Support\Str;
class ProductsController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::all();
return view('bo.products.index', compact('products'));
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
$ranges = Range::all();
if (!count($ranges)) {
return \redirect()->route('bo.ranges.index')
->with(
'error',
'Vous devez ajouter une gamme avant de pouvoir ajouter un produit'
);
}
return view('bo.products.create', compact('ranges'));
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreProductRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreProductRequest $request)
{
$name = $request->name;
$slug = Str::slug($name);
$no_art = $request->no_art;
$code_ean = $request->code_ean;
$picture = $request->picture;
$product = new Product();
$product->name = $name;
$product->no_art = $no_art;
$product->code_ean = $code_ean;
$product->range()->associate($request->range_id);
$product->save();
if ($picture !== null) {
$imageName = $product->id . '_' . $slug . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/produits'), $imageName);
$product->picture = $imageName;
$product->save();
}
return redirect()->route('bo.products.index');
}
/**
* Display the specified resource.
*
* @param integer $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
//
}
/**
* Show the form for editing the specified resource.
*
* @param integer $id
* @return \Illuminate\Http\Response
*/
public function edit($id)
{
$ranges = Range::all();
if (!count($ranges)) {
return \redirect()->route('bo.ranges.index')
->with(
'error',
'Vous devez ajouter une gamme avant de pouvoir ajouter un produit'
);
}
$product = Product::findOrFail($id);
return view('bo.products.edit', compact('product', 'ranges'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateProductRequest $request
* @param integer $id
* @return \Illuminate\Http\Response
*/
public function update(UpdateProductRequest $request, $id)
{
$name = $request->name;
$slug = Str::slug($name);
$no_art = $request->no_art;
$code_ean = $request->code_ean;
$picture = $request->picture;
$product = Product::findOrFail($id);
$product->range()->associate($request->range_id);
if ($picture !== null) {
if ($product->picture !== null && file_exists(public_path('images/produits/') . $product->picture)) {
unlink(public_path('images/produits/') . $product->picture);
}
$imageName = $product->id . '_' . $slug . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/produits'), $imageName);
}
$product->name = $name;
$product->no_art = $no_art;
$product->code_ean = $code_ean;
if ($picture !== null) {
$product->picture = $imageName;
}
$product->save();
return redirect()->route('bo.products.edit', $product->id)->with('success', 'Modifications enregistrés !');
}
/**
* Remove the specified resource from storage.
*
* @param integer $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$product = Product::findOrFail($id);
$product->delete();
return redirect()->route('bo.products.index');
}
}

View file

@ -3,135 +3,176 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreRangeRequest;
use App\Http\Requests\UpdateRangeRequest;
use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
use App\Models\Range;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class RangeController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Range::class);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index()
public function index(Request $request): \Illuminate\Contracts\View\View
{
$ranges = Range::paginate();
return view('bo.ranges.index', compact('ranges'));
}
/** @var \Illuminate\Database\Eloquent\Builder $rangeModels */
$rangeModels = Range::query();
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
return view('bo.ranges.create');
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\StoreRangeRequest $request
* @return \Illuminate\Http\Response
*/
public function store(StoreRangeRequest $request)
{
$name = $request->name;
$slug = Str::slug($name);
$picture = $request->picture;
$range = new Range();
$range->name = $name;
$range->save();
if ($picture !== null) {
$imageName = $range->id . '_' . $slug . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/ranges'), $imageName);
$range->picture = $imageName;
$range->save();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$rangeModels,
$search,
null,
'name',
);
}
$searchFields = Str::ucfirst(__('validation.attributes.name'));
return \response()->redirectToRoute('bo.ranges.index')
->with('success', 'La gamme à bien été enregistré !');
/** Sort columns with a query */
$this->sortQuery($rangeModels);
/** Custom pagination */
$rangeModels = $this->paginate($rangeModels);
return view('back::pages.ranges.index', compact('rangeModels', 'search', 'searchFields'));
}
/**
* Display the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function show(Range $range)
public function show(Range $range): \Illuminate\Contracts\View\View
{
return view('bo.ranges.view', compact('range'));
return view('back::pages.ranges.show', ['rangeModel' => $range]);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
*/
public function create(Range $range): \Illuminate\Contracts\View\View
{
return view('back::pages.ranges.create', [
'rangeModel' => $range,
]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\StoreRangeRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreRangeRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$range = new Range();
$range->fill($request->validated());
if ($range->saveOrFail()) {
return redirect()
->route('bo.ranges.edit', $range)
->with('success', trans(
'crud.messages.has_been_created',
['model' => Str::singular(__('models.classes.range'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_created',
['model' => Str::singular(__('models.classes.range'))]
));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\Response
* @return \Illuminate\Contracts\View\View
*/
public function edit(Range $range)
public function edit(Range $range): \Illuminate\Contracts\View\View
{
return view('bo.ranges.edit', compact('range'));
return view('back::pages.ranges.edit', [
'rangeModel' => $range
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\Response
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateRangeRequest $request, Range $range)
public function update(UpdateRangeRequest $request, Range $range): \Illuminate\Http\RedirectResponse
{
$name = $request->name;
$slug = Str::slug($name);
$picture = $request->picture;
if ($picture !== null) {
if ($range->picture !== null && file_exists(public_path('images/ranges/') . $range->picture)) {
unlink(public_path('images/ranges/') . $range->picture);
return DB::transaction(function () use ($request, $range) {
$range->fill($request->validated());
if ($range->saveOrFail()) {
return redirect()
->route('bo.ranges.edit', $range)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.range'))]
));
}
$imageName = $range->id . '_' . $slug . '_' . Str::random(5) . '.' . $picture->extension();
$picture->move(public_path('images/ranges'), $imageName);
}
$range->name = $name;
if ($picture !== null) {
$range->picture = $imageName;
}
$range->saveOrFail();
return \response()->redirectToRoute('bo.ranges.edit', $range)
->with('success', 'La gamme à bien été mise à jour !');
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.range'))]
));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\Response
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Range $range)
public function destroy(Range $range): \Illuminate\Http\RedirectResponse
{
if ($range->products()->count()) {
return back()->withError(
'Cette gamme est associé a des produits, la supression n\'est pas possible'
);
}
if (!$range->delete()) {
return back()->withError(
'Erreur lors de la suppression de la gamme'
);
}
return \response()->redirectToRoute('bo.ranges.index')
->with('warning', 'La gamme à bien été supprimée !');
return DB::transaction(function () use ($range) {
if ($range->deleteOrFail()) {
return redirect()
->route('bo.ranges.index')
->with('success', __(
'crud.messages.has_been_deleted',
['model' => Str::singular(__('models.classes.range'))]
));
}
return redirect()
->back()
->with('error', __(
':crud.messages.cannot_be_deleted',
['model' => Str::singular(__('models.classes.range'))]
));
});
}
}

View file

@ -0,0 +1,269 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Mail\Won\DiscountWonMail;
use App\Mail\Won\PrizeIsSentMail;
use App\Mail\Won\PrizeWonMail;
use App\Mail\Won\WinningProofSubmitedMail;
use App\Models\Prize;
use App\Models\Settings;
use App\Lib\Discount;
use App\Models\Participation;
use Illuminate\Contracts\View\View;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Support\Arr;
class Re7Controller extends Controller
{
/**
* Re7Controller
*
* @return void
*/
public function __construct()
{
$this->middleware(function (Request $request, \Closure $next) {
$this->authorize('fo.showRe7');
return $next($request);
});
}
/**
* Show Home page
*
* @return \Illuminate\Contracts\View\View
*/
public function home(): View
{
return view('theme::pages.home');
}
/**
* Show Teaser page
*
* @return \Illuminate\Contracts\View\View
*/
public function teaser(): View
{
return view('theme::pages.teaser');
}
/**
* Show Teaser page
*
* @return \Illuminate\Contracts\View\View
*/
public function endGame(): View
{
return view('theme::pages.end-game');
}
/**
* Show code input page
*
* @return \Illuminate\Contracts\View\View
*/
public function participate(): View
{
return view('theme::pages.participation.participate');
}
/**
* Show code input page
*
* @return \Illuminate\Contracts\View\View
*/
public function participationExhausted(): View
{
return view('theme::pages.participation.participation-exhausted');
}
// * -- Participation
/**
* Show prize page
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/
public function prize(Prize $prize): View
{
// * Fake participation model
$participationModel = new Participation();
$participationModel = 9999999;
return view('theme::pages.participation.won', [
'prizeModel' => $prize,
'participationModel' => $participationModel
]);
}
/**
* Show discount page
*
* @return \Illuminate\Contracts\View\View
*/
public function discount(): View
{
/** @var \App\Models\Settings */
$settings = app(Settings::class);
/** @var \App\Lib\Discount */
$discount = Arr::get(
$settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons)
);
if (!$settings->discounts_activated) {
return abort(404);
}
$mesBrDiscount = new Discount();
return view('theme::pages.participation.won', compact('discount', 'mesBrDiscount'));
}
/**
* Show loose page
*
* @return \Illuminate\Contracts\View\View
*/
public function loose(): View
{
return view('theme::pages.participation.loose');
}
// * -- END Participation
/**
* View won prize mail
*
* @param \App\Models\Prize $prize
* @return \Illuminate\Contracts\View\View
*/
public function mailPrize(Prize $prize): View
{
return view('back::pages.re7-preview-mail', [
'render_url' => route('re7.mail-print', [
'type' => 'prize',
'prize' => $prize
])
]);
}
/**
* View won discount mail
*
* @return \Illuminate\Contracts\View\View
*/
public function mailDiscount(): View
{
/** @var \App\Models\Settings */
$settings = app(Settings::class);
if (!$settings->discounts_activated) {
return abort(404);
}
return view('back::pages.re7-preview-mail', [
'render_url' => route('re7.mail-print', [
'type' => 'discount'
])
]);
}
/**
* View proof mail received mail
*
* @return \Illuminate\Contracts\View\View
*/
public function mailProofReceived(): View
{
return view('back::pages.re7-preview-mail', [
'render_url' => route('re7.mail-print', [
'type' => 'proof'
])
]);
}
/**
* View sent prize mail
*
* @return \Illuminate\Contracts\View\View
*/
public function mailPrizeSent(): View
{
return view('back::pages.re7-preview-mail', [
'render_url' => route('re7.mail-print', [
'type' => 'prize-sent'
])
]);
}
/**
* Print a specific mail
*
* @param string $type
* @param \App\Models\Prize|null $prize
* @return \Illuminate\Http\Response
*/
public function mailPrint(string $type, Prize|null $prize = null): Response
{
/** @var \App\Models\User */
$user = auth('backend')->user();
/** @var \App\Models\Settings */
$settings = app(Settings::class);
switch ($type) {
case 'discount':
/** @var \App\Lib\Discount */
$discount = Arr::get(
$settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons)
);
return response((new DiscountWonMail(
new Address(
$user->email,
$user->first_name
),
$settings->name,
$discount,
config('app.url'),
$settings->company,
))->render());
case 'prize':
return response((new PrizeWonMail(
new Address(
$user->email,
$user->first_name
),
$settings->name,
$prize,
config('app.url'),
$settings->company,
))->render());
case 'prize-sent':
return response((new PrizeIsSentMail(
new Address(
$user->email,
$user->first_name
),
$settings->name,
$settings->company,
))->render());
case 'proof':
return response((new WinningProofSubmitedMail(
new Address(
$user->email,
$user->first_name
),
$settings->name,
$settings->company,
))->render());
default:
return response('', 404);
} //end switch
}
}

View file

@ -3,89 +3,101 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Settings\UpdateSettingsRequest;
use App\Lib\Discount;
use App\Lib\Helpers\FileStorageHelper;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class SettingsController extends Controller
{
public function manage(Request $request)
/**
* Create the controller instance.
*/
public function __construct()
{
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
$this->authorizeResource(Settings::class);
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$name = $request->name;
$startDate = $request->start_date;
$endDate = $request->end_date;
$nbParticipationPlayer = $request->nb_participation_player;
$nbParticipationIp = $request->nb_participation_ip;
$nbUniqueCodeUse = $request->nb_unique_code_use;
$useEanCodes = $request->use_ean_codes === null ? 0 : 1;
$activatedTeaser = $request->activated_teaser === null ? 0 : 1;
$teaserEndActivity = $request->teaser_end_activity;
$activatedClosePage = $request->activated_close_page === null ? 0 : 1;
$closePageActiveDate = $request->close_page_active_date;
$redirectClose = $request->redirect_close;
$reductionsActivated = $request->reductions_activated === null ? 0 : 1;
$reductionsSite = $request->reductions_site;
$reductionsCampaign = $request->reductions_campaign;
$webCoupons = $request->web_coupons;
$facebookActivated = $request->facebook_activated === null ? 0 : 1;
$facebookAppId = $request->facebook_app_id;
$facebookSecret = $request->facebook_secret;
$notificationEmails = $request->notification_emails;
$winningProof = $request->winning_proof === null ? 0 : 1;
$company = $request->company;
$analytics = $request->analytics;
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Settings $settings
* @return \Illuminate\Contracts\View\View
*/
public function edit(Settings $settings): \Illuminate\Contracts\View\View
{
return view('back::pages.settings.edit', [
'settingsModel' => $settings
]);
}
// Check du lien de redirection après fermeture.
if (strpos($redirectClose, 'https://') === false && strpos($redirectClose, 'http://') === false) {
$redirectClose = 'https://' . $redirectClose;
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Settings\UpdateSettingsRequest $request
* @param \App\Models\Settings $settings
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateSettingsRequest $request, Settings $settings): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $settings) {
$data = $request->validated();
// * Assign parsed Web coupon objects
Arr::set($data, 'web_coupons', collect(Arr::get($data, 'web_coupons'))->filter()
->map(fn (array $coupon) => $this->parseWebCoupon($coupon))
->filter()->all());
$settings->fill($data);
if ($settings->saveOrFail()) {
return redirect()
->route('bo.settings.edit', $settings)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.settings'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(__('models.classes.settings'))]
));
});
}
$settings->name = $name;
$settings->start_date = $startDate;
$settings->end_date = $endDate;
$settings->nb_participation_player = $nbParticipationPlayer;
$settings->nb_participation_ip = $nbParticipationIp;
$settings->nb_unique_code_use = $nbUniqueCodeUse;
$settings->use_ean_codes = $useEanCodes;
$settings->activated_teaser = $activatedTeaser;
if ($activatedTeaser === 1) {
$settings->teaser_end_activity = Carbon::parse($teaserEndActivity);
}
$settings->activated_close_page = $activatedClosePage;
if ($activatedClosePage === 1) {
$settings->close_page_active_date = Carbon::parse($closePageActiveDate);
}
$settings->redirect_close = $redirectClose;
$settings->reductions_activated = $reductionsActivated;
if ($reductionsActivated === 1) {
$settings->reductions_site = $reductionsSite;
$settings->reductions_campaign = $reductionsCampaign;
$settings->web_coupons = $webCoupons;
}
$settings->facebook_activated = $facebookActivated;
$settings->facebook_app_id = $facebookAppId;
$settings->facebook_secret = $facebookSecret;
$settings->notification_emails = $notificationEmails;
$settings->winning_proof = $winningProof;
$settings->company = $company;
$settings->analytics = $analytics;
$settings->save();
$settingsGlobal = app(Settings::class)
->select('name', 'company', 'analytics', 'start_date', 'end_date')->first();
Cache::put('settingsGlobal', $settingsGlobal);
$success = "Modifications enregistrés !";
return view('bo.settings.manage', compact('settings', 'success'));
}//end if
return view('bo.settings.manage', compact('settings'));
/**
* Try to parse webCoupon
*
* @param array<string,string> $coupon
* @return \App\Lib\Discount|null
*/
private function parseWebCoupon(array $coupon): Discount | null
{
/** @var \Illuminate\Http\UploadedFile|string */
$picture = Arr::get($coupon, 'picture');
if (is_object($picture) and ($picture instanceof \Illuminate\Http\UploadedFile)) {
$picture = FileStorageHelper::storeFile(
new Settings(),
$picture,
true,
'web-coupon.' . $picture->extension()
);
}
try {
return Discount::make(
$picture,
Arr::get($coupon, 'valid_on'),
Arr::get($coupon, 'face_value'),
Arr::get($coupon, 'percentage'),
Arr::get($coupon, 'web_coupon_identifiant'),
);
} catch (\RuntimeException | \TypeError) {
return null;
}
}
}

View file

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

View file

@ -0,0 +1,169 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Users\StoreUserRequest;
use App\Http\Requests\Bo\Users\UpdateUserRequest;
use App\Models\User;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class UserController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(User::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $userModels */
$userModels = User::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$userModels,
$search,
null,
'first_name',
'last_name',
'email',
'role',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.first_name'),
trans('validation.attributes.last_name'),
trans('validation.attributes.email'),
trans('validation.attributes.role'),
]);
/** Sort columns with a query */
$this->sortQuery($userModels);
/** Custom pagination */
$userModels = $this->paginate($userModels);
return view('back::pages.users.index', compact('userModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function create(User $user): \Illuminate\Contracts\View\View
{
return view('back::pages.users.create', ['userModel' => $user]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Users\StoreUserRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreUserRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$user = new User();
$user->fill($request->validated());
if ($user->saveOrFail()) {
return redirect()->route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function edit(User $user): \Illuminate\Contracts\View\View
{
return view('back::pages.users.edit', ['userModel' => $user]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateUserRequest $request, User $user): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $user) {
$user->fill($request->validated());
if ($user->saveOrFail()) {
return redirect()->route('bo.users.edit', $user)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->route('bo.users.edit', $user)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\User $user
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(User $user): \Illuminate\Http\RedirectResponse
{
if ($user->deleteOrFail()) {
return redirect()->route('bo.users.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.user'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\User $user
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(User $user): \Illuminate\Contracts\View\View
{
return $this->create($user->replicate());
}
}

View file

@ -1,117 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Mail\CourrierArcadiaError;
use App\Models\Participation;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class WinnersListController extends Controller
{
private $apiCrm;
public function __construct()
{
$this->apiCrm = new \LDCCRM\ApiV1(config('crm.endpoint'), config('crm.token'));
}
public function index()
{
$winnersList = Participation::has('instantWin')->get();
$settings = Settings::select('use_ean_codes', 'winning_proof')->first();
return view('bo.winners-list.index', compact('winnersList', 'settings'));
}
public function courrierArcadia($id)
{
$participation = Participation::findOrFail($id);
$crmUser = $this->apiCrm->getCustomer($participation->user->crm_id);
if ($crmUser->status === 'NOK') {
return back()->withErrors("Erreur lors de la récupération de l'utilisateur dans la CRM : Code : " .
$crmUser->code . " Message : " . $crmUser->message);
}
$crmId = $participation->user->crm_id;
$crmUser = $crmUser->user;
$corps = '<arcadia>' . "\n";
$corps .= '<Commande> ' . "\n";
$corps .= "\t" . '<Num_Commande></Num_Commande>' . "\n";
$corps .= "\t" . '<Date_Commande>' . date('d/m/Y') . '</Date_Commande>' . "\n";
$corps .= "\t" . '<Operation></Operation> ' . "\n";
$corps .= "\t" . '<Civilite></Civilite>' . "\n";
$corps .= "\t" . '<Nom><![CDATA[' . trim($crmUser->firstname) . ']]></Nom>' . "\n";
$corps .= "\t" . '<Prenom><![CDATA[' . trim($crmUser->lastname) . ']]></Prenom>' . "\n";
$corps .= "\t" . '<Adresse><![CDATA[' . trim($crmUser->address) . ']]></Adresse>' . "\n";
$corps .= "\t" . '<Adresse_Complementaire><![CDATA[' . trim($crmUser->address2) . ']]></Adresse_Complementaire>' . "\n";
$corps .= "\t" . '<CP><![CDATA[' . trim($crmUser->postcode) . ']]></CP>' . "\n";
$corps .= "\t" . '<Ville><![CDATA[' . trim($crmUser->city) . ']]></Ville>' . "\n";
$corps .= '<Ligne_de_Commande>' . "\n";
$corps .= "\t" . '<Code_Support></Code_Support> ' . "\n";
$corps .= "\t" . '<Libelle_Type_Support></Libelle_Type_Support> ' . "\n";
$corps .= "\t" . '<Quantite>1</Quantite>' . "\n";
$corps .= "\t" . '<Poids_Unitaire_Colis></Poids_Unitaire_Colis>' . "\n";
$corps .= '</Ligne_de_Commande>' . "\n";
$corps .= '</Commande> ' . "\n";
$corps .= '</arcadia>';
return view('bo.winners-list.arcadia', compact('participation', 'corps', 'crmId'));
}
public function createCourrierArcadia(Request $request, $id)
{
$participation = Participation::findOrFail($id);
$instantWin = $participation->instantWin;
if ($instantWin->prize->arcadia_operation_code !== 0 && $instantWin->prize->arcadia_support_code !== 0) {
$fields = [
'root' => [
[
'fieldName' => 'order_date',
'fieldValue' => date('d/m/Y')
],
[
'fieldName' => 'operation',
'fieldValue' => $instantWin->prize->arcadia_operation_code
]
],
'orderLine' => [
[
'fieldName' => 'code_unique',
'fieldValue' => $participation->code->code_unique
],
[
'fieldName' => 'support_code',
'fieldValue' => $instantWin->prize->arcadia_support_code
],
[
'fieldName' => 'label_type_support',
'fieldValue' => 'Gain du lot : ' . $instantWin->prize->label_one
]
]
];
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
if ($settings->use_ean_codes === 1) {
$fields['orderLine'][] = [
'fieldName' => 'code_on_pack',
'fieldValue' => $participation->codeEan->code
];
}
$arcadiaMail = $this->apiCrm->ArcadiaOutgoingMail($participation->user->crm_id, $fields, 'game');
if ($arcadiaMail->status === 'NOK') {
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError($participation->user));
return back()->withErrors("Erreur lors de l'envoi du courrier Arcadia. Code : " .
$arcadiaMail->code . " Message : " . $arcadiaMail->message);
}
}//end if
return back()->with('success', 'Courrier envoyé !');
}
}

View file

@ -2,14 +2,267 @@
namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Enums\Theme\BootstrapThemeEnum;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
/**
* Utility function for getting random values with weighting.
* Pass in an associative array, such as array('A'=>5, 'B'=>45, 'C'=>50)
* An array like this means that "A" has a 5% chance of being selected, "B" 45%, and "C" 50%.
* The return value is the array key, A, B, or C in this case. Note that the values assigned
* do not have to be percentages. The values are simply relative to each other. If one value
* weight was 2, and the other weight of 1, the value with the weight of 2 has about a 66%
* chance of being selected. Also note that weights should be integers.
*
* @param array<\App\Lib\Discount> $weightedValues
* @return integer The winning key
* @url https://stackoverflow.com/a/11872928/4355295
*/
protected function getRandomWeightedElementIndex(array $weightedValues): int
{
mt_srand(time());
$rand = mt_rand(1, max((int) collect($weightedValues)->sum('percentage'), 1));
foreach ($weightedValues as $key => $value) {
$rand -= $value->percentage;
if ($rand <= 0) {
return $key;
}
}
return $rand;
}
/**
* Gives the redirection path for FO Auth controllers
*
* @return string
*/
protected function authLoggedRedirectPath(): string
{
return response()->redirectToRoute('fo.homepage')->getTargetUrl();
}
/**
* Build Search query on specified fields
* splitting search into words or using whole sentence
*
* @param \Illuminate\Database\Eloquent\Builder $query The eloquent query builder.
* @param string $search The query string.
* @param callable $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.
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function searchQuery(
Builder $query,
string $search,
callable $searchQuery = null,
string ...$fields
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
$query->where(function ($query) use ($searchQuery, $words, $search, $fields) {
foreach ($words as $word) {
if (!strlen($word)) {
continue;
}
$this->searchOnFields($query, $word, $fields);
} //end foreach
// Search whole using whole sentence.
$this->searchOnFields($query, $search, $fields);
if ($searchQuery) {
$searchQuery($query);
}
});
} //end if
return $query;
}
/**
* Build query search on all fields
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(
Builder $query,
string $search,
array $fields
): \Illuminate\Database\Eloquent\Builder {
$table = $query->getModel()->getTable();
foreach ($fields as $field) {
$field = str_replace('?', '\?', $field);
if (is_array($field)) {
$query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . htmlspecialchars($search) . '%']
);
return $query;
}
// Get models woth enum's label.
if (is_array($this->getSearchValue($query, $search, $field))) {
foreach ($this->getSearchValue($query, $search, $field) as $array) {
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($array->value) . '%']
);
}
return $query;
}
// Get models with relation's name/label.
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 . '%');
} elseif (Schema::hasColumn(Str::remove('_id', $field) . 's', 'label')) {
$queryRelation->where('label', 'like', '%' . $search . '%');
}
});
return $query;
}
$query->orWhereRaw(
"UPPER(`$table`.`$field`) LIKE UPPER(?)",
['%' . htmlspecialchars($this->getSearchValue($query, $search, $field)) . '%']
);
} //end foreach
return $query;
}
/**
* Get the search value (on string or Enum).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param string $search
* @param string $field
* @return string|array
*/
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.
$cast and // Check if there is cast about specific field.
enum_exists($cast) // Check if the cast is an enum.
) {
// @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));
});
return $enum->toArray();
} else {
return $search;
}
}
/**
* Sort columns with a query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param array $ignore
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
if (request()->sort_col) {
Session::put("$rName.sort_col", request()->sort_col);
Session::put("$rName.sorted", true);
}
if (request()->sort_way) {
Session::put("$rName.sort_way", request()->sort_way);
Session::put("$rName.sorted", true);
}
if (request()->rst) {
Session::remove("$rName.sorted");
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 (!in_array(request()->sort_col, $ignore)) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
}
} elseif (Schema::hasColumn($table, 'order')) {
$query = $query->orderBy('order', 'ASC');
Session::put("$rName.sort_col", 'order');
Session::put("$rName.sort_way", 'asc');
} elseif (Schema::hasColumn($table, 'updated_at')) {
$query = $query->orderBy('updated_at', 'DESC');
Session::put("$rName.sort_col", 'updated_at');
Session::put("$rName.sort_way", 'desc');
} elseif (Schema::hasColumn($table, 'created_at')) {
$query = $query->orderBy('created_at', 'DESC');
Session::put("$rName.sort_col", 'created_at');
Session::put("$rName.sort_way", 'desc');
}
return $query;
}
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function paginate(
Builder $query,
ItemsPerPaginationEnum $pagination = null
): \Illuminate\Contracts\Pagination\LengthAwarePaginator {
$currentRoutePath = Str::slug(request()->route()->getName());
$cacheKey = "pagination.{$currentRoutePath}";
$pagination = $pagination ?? ItemsPerPaginationEnum::twelve;
$pagination = ToolboxHelper::getValidatedEnum(
Cache::get($cacheKey, $pagination->value()),
'pagination',
'\App\Enums\Pagination\ItemsPerPaginationEnum',
);
if (Cache::get($cacheKey) !== $pagination) {
Cache::put($cacheKey, $pagination);
}
$query = $query->paginate($pagination);
return $query;
}
/**
* Set the bootstrap theme or light (default).
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse
*/
protected function setTheme(Request $request): \Illuminate\Http\RedirectResponse
{
$theme = ToolboxHelper::getValidatedEnum(
$request->theme ?: Cache::get('theme', BootstrapThemeEnum::light->value()),
'theme',
'\App\Http\Controllers\BootstrapThemeEnum',
);
if (Cache::get("theme") !== $theme) {
Cache::put("theme", $theme);
}
return redirect()->back()->with('success', __('crud.messages.theme_updated'));
}
}

View file

@ -1,23 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Settings;
class EndPageController extends Controller
{
public function index()
{
$settings = app(Settings::class)
->select('start_date', 'end_date', 'activated_close_page', 'close_page_active_date', 'redirect_close')
->first();
if (
$settings->end_date < now() &&
$settings->activated_close_page === 1 &&
$settings->close_page_active_date > now()
) {
return view('front.basic.end', compact('settings'));
}
return redirect()->route('home');
}
}

View file

@ -1,290 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ParticipateRequest;
use App\Mail\CourrierArcadiaError;
use App\Mail\NotifAdmin;
use App\Mail\NotifWinAdmin;
use App\Mail\PrizeWon;
use App\Mail\ReductionWon;
use App\Models\BannedIp;
use App\Models\InstantWin;
use App\Models\Participation;
use App\Models\Reduction;
use LDCWeb\MesBR;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Mail;
class EnterCodeController extends Controller
{
/**
*
* @var \LDCCRM\ApiV1
*/
private $apiCrm;
/**
* EnterCodeController
*/
public function __construct()
{
$this->apiCrm = new \LDCCRM\ApiV1(config('crm.endpoint'), config('crm.token'));
}
/**
* Show enter code form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
$nbParticipations = Participation::where('user_id', Auth::user()->id)->count();
$settings = app(Settings::class)->select('use_ean_codes', 'nb_participation_player')->first();
if ($nbParticipations >= $settings->nb_participation_player) {
return view('front.basic.participation-exhausted', compact('settings'));
}
return view('front.basic.enter-code', compact('settings'));
}
/**
* Participate Method
*
* @param ParticipateRequest $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function participate(ParticipateRequest $request)
{
// Récupération des données à utilisées.
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
// * Cas IP bannie.
$ipBanned = BannedIp::where('ip', app('clientIp'))->first() === null ? 0 : 1;
if ($ipBanned) {
return view('front.basic.participation-exhausted', compact('settings'));
}
// * Cas nombre de participation maximale atteint.
$nbParticipations = Participation::where('user_id', auth()->user()->id)->count();
if ($nbParticipations >= $settings->nb_participation_player) {
return view('front.basic.participation-exhausted', compact('settings'));
}
// * Tous les cas "bloquants" sont passé, l'utilisateur participe à partir d'ici.
$participation = new Participation();
$participation->user_id = auth()->user()->id;
$participation->code_unique_id = $request->code->id;
$participation->code_ean_id = $request->code_ean ? $request->code_ean->id : null;
$participation->ip = app('clientIp');
$participation->save();
// * Gestion des instants gagnants.
$userAlreadyWon = Participation::has('instantWin')
->whereHas('user', function ($q) {
$q->whereId(auth()->user()->id);
})->first();
// * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant.
if ($userAlreadyWon) {
return $this->serveCouponWin($settings, $participation);
}
$instantWin = InstantWin::where([['appear_date', '<', now()], ['won', 0]])
->orderBy('appear_date', 'ASC')->first();
if ($instantWin) {
// Ici si fail car unique dupliqué catch.
try {
// Gain d'un gros lot.
return $this->instantWin($settings, $request, $participation, $instantWin);
} catch (\ErrorException $e) {
// Unbind instant gagnant.
$participation->instantWin()->delete();
// TODO: si erreur il faut envoyer un mail à equipe.web@ldc.fr
} //end try
}
return $this->serveCouponWin($settings, $participation);
}
/**
* Handle instant wins
*
* @param Settings $settings
* @param Request $request
* @param Participation $participation
* @param InstantWin|null $instantWin
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
private function instantWin(Settings $settings, Request $request, Participation &$participation, $instantWin)
{
$instantWin->won = true;
// Should save now to prevent next player win on the same winning moment.
$instantWin->save();
$participation->instantWin()->save($instantWin);
// Envoi du courrier Arcadia.
if (
$instantWin->prize->arcadia_operation_code !== 0 &&
$instantWin->prize->arcadia_support_code !== 0
) {
$crmId = auth()->user()->crm_id;
$fields = [
'root' => [
[
'fieldName' => 'order_date',
'fieldValue' => date('d/m/Y')
],
[
'fieldName' => 'operation',
'fieldValue' => $instantWin->prize->arcadia_operation_code
]
],
'orderLine' => [
[
'fieldName' => 'code_unique',
'fieldValue' => $request->serial
],
[
'fieldName' => 'support_code',
'fieldValue' => $instantWin->prize->arcadia_support_code
],
[
'fieldName' => 'label_type_support',
'fieldValue' => 'Gain du lot : ' . $instantWin->prize->label_one
]
]
];
if ($settings->use_ean_codes === 1) {
$codeEan = $request->code_ean;
$fields['orderLine'][] = [
'fieldName' => 'code_on_pack',
'fieldValue' => "$codeEan"
];
}
$arcadiaMail = $this->apiCrm->ArcadiaOutgoingMail($crmId, $fields, 'game');
if ($arcadiaMail->status === 'KO') {
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(auth()->user()));
}
} //end if
Mail::to(auth()->user()->email)->send(new PrizeWon(auth()->user(), $instantWin->prize, $settings));
$mails = json_decode($settings->notification_emails);
foreach ($mails as $mail) {
Mail::to($mail->email)->send(new NotifWinAdmin($settings, $participation));
}
$participation->save();
return view("front.basic." . $instantWin->prize->gain_view->file_name, compact('settings'))
->with('prize', $instantWin->prize);
}
/**
* Détermine si un BR doit être servi
* renvoi la vue de gain BR ou la vue "Perdu"
*
* @param Settings $settings
* @param Participation $participation
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
private function serveCouponWin(Settings $settings, Participation $participation)
{
// Si le jeu est 100% gagnant.
if ($settings->reductions_activated === 1) {
$coupon = $this->getRandomCoupon($settings);
$crmCoupon = $this->updateCoupon($settings, $coupon->web_coupon_identifiant, $participation);
if (!$crmCoupon) {
$participation->delete();
return back()->withErrors("Une erreur est survenue, merci de réessayez ultérieurement.");
}
Mail::to(Auth::user()->email)->send(new ReductionWon(Auth::user(), $coupon, $settings, $crmCoupon));
return view('front.basic.reduction', compact('crmCoupon', 'coupon'));
} else {
return view('front.basic.loose');
}
}
/**
* Get a random coupon
*
* @param Settings $settings
* @return mixed
*/
private function getRandomCoupon(Settings $settings)
{
$webCoupons = json_decode($settings->web_coupons);
if (count($webCoupons) > 1) {
$lots = [];
foreach ($webCoupons as $webCoupon) {
$lots[] = $webCoupon;
}
foreach ($lots as $lot) {
for ($i = 0; $i < $lot->percentage; $i++) {
$tmp[] = $lot;
}
}
shuffle($tmp);
return $tmp[0];
} else {
return $webCoupons[0];
}
}
/**
* Update a coupon
*
* @param Settings $settings
* @param mixed $coupon
* @param Participation $participation
* @return \App\Models\Reduction|null
*/
private function updateCoupon(Settings $settings, $coupon, Participation $participation)
{
$reduction = null;
$mesBR = new MesBR(config('mesbr.endpoint'), $settings->reductions_site, $settings->reductions_campaign);
$crmUser = $this->apiCrm->getCustomer(Auth::user()->crm_id);
if ($crmUser->status === 'OK') {
$mesBR->updateContact($crmUser->user);
$result = $mesBR->updatePanier($crmUser->user, $coupon);
if ($result["status"] === "OK") {
$reduction = new Reduction();
$reduction->link = $result["link"];
$reduction->verification = $result["verif"];
$reduction->basket_id = $result["basketId"];
$reduction->save();
$participation->reduction_id = $reduction->id;
$participation->save();
} else {
Mail::to(config('mail.MAIL_ADMIN'))
->send(new NotifAdmin(\sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result["code"],
$result["message"]
)));
}
} //end if
return $reduction;
}
/**
* Undocumented function
*
* @return \Illuminate\Http\Response
*/
public function formWinningProof()
{
$participation = Participation::where([
['user_id', Auth::user()->id],
['sticker', null],
['ticket', null],
['barcode', null]
])->has('instantWin')->first();
if ($participation) {
return view('front.basic.winning-proof', compact('participation'));
}
return redirect()->route('home');
}
}

View file

@ -1,19 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
class FAQController extends Controller
{
public function index()
{
$questions = FaqQuestion::all();
$themes = FaqTheme::all();
if (count($questions) === 0 && count($themes) === 0) {
return redirect()->route('home');
}
return view('front.basic.faq', compact('questions', 'themes'));
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
use Illuminate\Support\Facades\Auth;
class ConfirmPasswordController extends Controller
{
use ConfirmsPasswords;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:frontend');
}
/**
* Display the password confirmation view.
*
* @return \Illuminate\View\View|\Illuminate\Http\RedirectResponse
*/
public function showConfirmForm(): \Illuminate\View\View|\Illuminate\Http\RedirectResponse
{
if (Auth::guard('frontend')->check()) {
return \response()->redirectToRoute('home');
}
return view('theme::auth.passwords.confirm');
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return $this->authLoggedRedirectPath();
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
/**
* Message to user
*
* @var string
* @phpcs:disable Generic.Files.LineLength.TooLong
*/
private $message = 'front.messages.password_reset_email_sent';
//phpcs:enable
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:frontend');
}
/**
* Display the form to request a password reset link.
*
* @return \Illuminate\View\View
*/
public function showLinkRequestForm(): View
{
return view('theme::auth.passwords.email');
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker(): PasswordBroker
{
return Password::broker('customers');
}
/**
* Get the response for a successful password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe.
*
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkResponse()
{
return back()->with('info', trans($this->message));
}
/**
* Get the response for a failed password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkFailedResponse(Request $request, string $response)
{
// ! Ne pas notifier que l'utilisateur n'existe pas.
if ($response === 'passwords.user') {
return back()->with('info', trans($this->message));
}
// * Renvoyer les autres message d'erreur style throttle.
return back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use App\Models\Settings;
use App\Providers\RouteServiceProvider;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
class LoginController extends Controller
{
use AuthenticatesUsers;
use ThrottlesLogins;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:frontend')->except('logout');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('frontend');
}
/**
* Show the application's login form.
*
* @return \Illuminate\View\View
*/
public function showLoginForm(): View
{
return view('theme::auth.login');
}
/**
* Check if the authenticated user can access the game
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\Customer $user
* @return void
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
*/
protected function authenticated(Request $request, Customer $user): void
{
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $user->api_data;
/** @var \App\Models\Settings */
$settings = app(Settings::class);
$countries = $settings->authorized_countries;
if (count($countries) && !in_array($crmCustomer->country->value, $countries)) {
$this->logout($request);
Session::flash('info', trans('front.messages.game_not_available_in_country'));
} else {
$user->last_connection = \now();
$user->saveOrFail();
Session::flash('info', trans('front.messages.login_success'));
}
}
/**
* The user has logged out of the application.
*
* @param \Illuminate\Http\Request $request
* @return void
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
protected function loggedOut(Request $request): void
{
Session::flash('info', trans('front.messages.logout_success'));
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return response()->redirectToIntended()->getTargetUrl();
}
}

View file

@ -0,0 +1,157 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Models\Customer;
use Illuminate\Contracts\Validation\Validator as ValidationValidator;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class RegisterController extends Controller
{
use RegistersUsers;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:frontend');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard()
{
return Auth::guard('frontend');
}
/**
* Show the application registration form.
*
* @return \Illuminate\View\View
*/
public function showRegistrationForm()
{
return view('theme::auth.register');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
* @throws IlluminateValidationException If data is incorrect.
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
protected function validator(array $data): ValidationValidator
{
// phpcs:enable
$request = CreateAccountRequest::capture();
$request->prepareForValidation();
return Validator::make(
$request->validationData(),
$request->rules(),
$request->messages(),
$request->attributes()
);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\Customer
* @throws IlluminateValidationException If there are validation errors.
* @throws \RuntimeException On unhandled api Error.
*/
protected function create(array $data): Customer
{
$data['ok_cgv'] = \request()->boolean('ok_cgv');
$data['opt_in'] = \request()->boolean('opt_in');
$data['phone'] = Arr::get($data, 'phone');
// * This field is not standard in crm
$data['postcode'] = Arr::get($data, 'postal_code');
Arr::forget($data, 'postal_code');
try {
return DB::transaction(function () use ($data) {
$crmCustomer = new CrmCustomer(Arr::only(
$data,
[
'firstname', 'lastname', 'birth_date', 'address',
'address2', 'postcode', 'country', 'city', 'phone',
'email', 'password', 'opt_in',
]
));
$crmCustomer->save();
// * Enregistrement du Hashcode
/** @var \App\Models\Customer $customer */
$customer = Customer::withTrashed()->firstOrNew([
'email' => $data['email']
]);
$customer->fill(Arr::only($data, $customer->getFillable()));
$customer->hashcode = $crmCustomer->hashcode;
if ($customer->trashed()) {
$customer->restore();
}
$customer->email_verified_at = $crmCustomer->status ? now() : null;
// ! Last connection sera null, pour identifier la deuxième connexion,
// ! Pour accéder au partie du Klub.
if ($customer->saveOrFail()) {
// phpcs:ignore Generic.Files.LineLength.TooLong
Session::flash('success', trans('front.messages.account_created_email_confirmation_sent'));
}
return $customer;
});
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerExists)) {
throw IlluminateValidationException::withMessages([
'error' => trans('front.messages.email_already_in_use')
]);
}
if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
}
report($e);
Log::error(__CLASS__ . '::create reached end of possibilities.');
throw new \RuntimeException(__CLASS__ . '::create reached end of possibilities.');
} catch (CrmValidationException $e) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
} //end try
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath()
{
return $this->authLoggedRedirectPath();
}
}

View file

@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Foundation\Auth\ResetsPasswords;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Password;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException as ExceptionsValidationException;
class ResetPasswordController extends Controller
{
use ResetsPasswords;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:frontend');
$this->middleware('signed')->only('showResetForm');
$this->middleware('throttle:6,1')->only('showResetForm');
}
/**
* Get the guard to be used during authentication.
*
* @return \Illuminate\Contracts\Auth\StatefulGuard
*/
protected function guard(): StatefulGuard
{
return Auth::guard('frontend');
}
/**
* Display the password reset view for the given token.
*
* If no token is present, display the link request form.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function showResetForm(Request $request): \Illuminate\Contracts\View\View
{
$token = $request->route()->parameter('token');
return view('theme::auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
/**
* Reset the given user's password.
*
* @param \App\Models\Customer $customer
* @param string $password
* @return void
* @throws \Illuminate\Validation\ValidationException If something goes wrong.
*/
protected function resetPassword(\App\Models\Customer $customer, string $password): void
{
/** @var \LDC\LDCCRM\CrmApiV4 */
$crm = app('crmAPI');
try {
if (!($crmCustomer = $crm->getCustomer($customer->hashcode))) {
throw ValidationException::withMessages([
'error' => trans('Changement impossible, compte inexistant.')
]);
}
$crmCustomer->password = $password;
$crmCustomer->save();
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
throw ValidationException::withMessages([
'error' => trans('front.messages.unexpected_error_try_later')
]);
} catch (ExceptionsValidationException $e) {
throw ValidationException::withMessages([
'password' => $e->getMessage()
]);
}
$customer->setRememberToken(Str::random(60));
$customer->save();
event(new PasswordReset($customer));
$this->guard()->login($customer);
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return $this->authLoggedRedirectPath();
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker(): PasswordBroker
{
return Password::broker('customers');
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\Events\Verified;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
class VerificationController extends Controller
{
use VerifiesEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:frontend')->except('verify');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
/**
* Show the email verification notice.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View
*/
public function show(Request $request): RedirectResponse|View
{
return $request->user('frontend')->hasVerifiedEmail()
? redirect($this->redirectPath())
: view('theme::auth.verify');
}
/**
* Mark the authenticated user's email address as verified.
*
* ! Overloaded trait
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse
* @throws \Illuminate\Auth\Access\AuthorizationException If request is invalid.
*/
public function verify(Request $request): JsonResponse|RedirectResponse
{
/** @var \App\Models\Customer */
$user = Customer::findOrFail($request->route('id'));
if (!hash_equals((string) $request->route('id'), (string) $user->getKey())) {
throw new AuthorizationException();
}
if (!hash_equals((string) $request->route('hash'), sha1($user->getEmailForVerification()))) {
throw new AuthorizationException();
}
if ($user->hasVerifiedEmail()) {
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect($this->redirectPath());
}
DB::transaction(function () use ($user) {
if ($user->markEmailAsVerified()) {
$this->setCrmCustomerAsVerified($user);
event(new Verified($user));
}
});
if ($response = $this->verified($request)) {
return $response;
}
return $request->wantsJson()
? new JsonResponse([], 204)
: redirect($this->redirectPath())->with('verified', true);
}
/**
* The user has been verified.
*
* @param \Illuminate\Http\Request $request
* @return mixed
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
protected function verified(Request $request)
{
// phpcs:enable
return redirect($this->redirectPath())->with('success', trans(
'front.messages.account_activated_can_play'
));
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return response()->redirectToRoute('fo.participate')->getTargetUrl();
}
/**
* Set Crm Customer active
*
* @param \App\Models\Customer $customer
* @return void
* @throws \Illuminate\Http\Exceptions\HttpResponseException In case of failure.
*/
private function setCrmCustomerAsVerified(Customer $customer): void
{
try {
$customer->clearApiDataCache();
$crmCustomer = $customer->api_data;
$crmCustomer->activate();
$customer->clearApiDataCache();
} catch (ValidationException) {
redirect($this->redirectPath())->with('error', trans(
'front.messages.unexpected_error_try_later'
))->throwResponse();
} catch (CrmException | ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerAlreadyActive)) {
// * Ignore this and continue, as it may be activated via another CRM service.
return;
} else {
report($e);
redirect($this->redirectPath())->with('error', trans(
'front.messages.unexpected_error_try_later'
))->throwResponse();
}
}
}
}

View file

@ -0,0 +1,175 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\VerifyInformationsRequest;
use App\Http\Requests\Fo\Customers\SingleOptinRequest;
use App\Repositories\Fo\CrmRepository;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Session;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use LDC\LDCCRM\Models\SingleOptin;
class CustomerController extends Controller
{
/** @var \App\Repositories\Fo\CrmRepository $crmRepository */
private $crmRepository;
/**
* ParticipateController
*
* @return void
*/
public function __construct()
{
$this->crmRepository = new CrmRepository();
}
/**
* Verify profil info form
*
* @return \Illuminate\Contracts\View\View
*/
public function profilVerify(): View
{
// ! Pas de Gate/Policies Ici volontairement, vu avec Vincent
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
$crmCustomer = $customer->api_data;
return view('theme::auth.profil-verify', compact('crmCustomer'));
}
/**
* Profile verify
*
* @param \App\Http\Requests\Fo\Customers\VerifyInformationsRequest $request
* @return \Illuminate\Http\RedirectResponse
* @throws IlluminateValidationException In case of any error validation or within the crm.
*/
public function profilVerifySubmit(VerifyInformationsRequest $request): RedirectResponse
{
// ! Pas de Gate/Policies Ici volontairement, vu avec Vincent
try {
return DB::transaction(function () use ($request) {
// * --- FILL AND SAVE CUSTOMER
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
// * Màj, on ne mets pas à jour le mdp ou l'adresse email, juste les champs pour mesBr
$customer->updateApiData(\array_merge(Arr::only(
$request->validated(),
[
'firstname',
'lastname',
'birth_date',
'address',
'country',
'city',
'phone',
'opt_in',
]
), ['postcode' => $request->input('postal_code')]));
Session::flash('success', trans('front.messages.profile_updated_success'));
return response()->redirectToRoute('fo.participate');
});
} catch (CrmValidationException $e) {
// * Local Validation failure
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
}
report($e);
Log::error(__CLASS__ . '::update reached end of possibilities.');
} catch (CrmException $e) {
report($e);
Log::error(__CLASS__ . '::error occured on calling crm.');
throw IlluminateValidationException::withMessages([
'error' => trans('Une erreur est survenue, veuillez réssayer plus tard')
]);
} //end try
return back()->withInput()->withErrors(trans('front.messages.unexpected_error_try_later'));
}
/**
* View all customer winnig participations
*
* @return \Illuminate\Contracts\View\View
*/
public function viewWinningParticipations(): View
{
$this->authorize('viewWonList', \App\Models\Participation::class);
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
// * Recupération des informations CRM
/** @var \LDC\LDCCRM\Models\Customer*/
$crmUser = $this->crmRepository->fetchCrmCustomer($customer);
/** @var \LDC\MESBR\MesBr */
$mesBr = app('mesBrAPI');
$brBasket = $mesBr->getBasket($crmUser);
if (!empty($brBasket['coupons'])) {
$canPrint = (collect($brBasket['coupons'])->sum('quantity') - collect($brBasket['coupons'])->sum('printed'));
}
$winningList = $customer->participations()
->with(['instantWin'])->where(function (Builder $q) {
$q->whereHas('instantWin');
})->get();
return view('theme::pages.participation.winning-participations', [
'winningList' => $winningList,
'canPrint' => $canPrint ?? null
]);
}
/**
* Call the CRM to add a single optin
*
* @param \App\Http\Requests\Fo\Customers\SingleOptinRequest $request
* @return string|array|null|\Illuminate\Contracts\Translation\Translator
* @throws \LDC\LDCCRM\Exceptions\ValidationException ValidationException.
* @throws \Illuminate\Contracts\Container\BindingResolutionException BindingResolutionException.
*/
public function addOptIn(SingleOptinRequest $request)
{
try {
SingleOptin::setStatus($request->email, true);
return \response(trans("front.messages.newsletter_subscription_success"), 200);
} catch (ApiErrorException $e) {
// Il y a deja un customerInfo avec optin a true pour ce customer.
if ($e->getApiError()->equals(ApiError::singleOptinCustomerExists)) {
return trans('front.messages.newsletter_email_already_subscribed');
}
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
return \response(trans('front.messages.unexpected_error_try_later'), 404);
} catch (CrmException $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
return \response(trans('front.messages.unexpected_error_try_later'), 404);
}
}
}

View file

@ -0,0 +1,90 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use Illuminate\Support\Arr;
class DiscountController extends Controller
{
/**
* Fetch PDF BR.
*
* @return \Illuminate\Http\RedirectResponse
*/
public function fetchBrPdf(): \Illuminate\Http\RedirectResponse
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
$pdf = $this->fetchBrPdfFromMesBr($customer);
if ($pdf === null) {
\back()->withErrors([
'coupon' => trans('front.messages.pdf_download_error')
])->throwResponse();
}
return redirect()->to(
sprintf(
"%s/basket/%s/print",
config('mesbr.endpoint'),
$pdf
)
);
}
/**
* Fetch coupons PDF.
*
* @param \App\Models\Customer $customer
* @return string|null
* @phpcs:disable Generic.Metrics.CyclomaticComplexity.TooHigh
*/
private function fetchBrPdfFromMesBr(Customer $customer): ?string
{
// phpcs:enable
// * Phase - 1 Recupération des informations CRM
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->api_data;
try {
/** @var \LDC\MESBR\MesBr */
$mesBr = app('mesBrAPI');
$result = $mesBr->getBasket($crmCustomer);
$result = \is_object($result) ? (array) $result : $result;
if (Arr::get($result, 'status') !== "OK") {
report(new \RuntimeException(
\sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s',
Arr::get($result, 'code') ?? 'Undefined',
Arr::get($result, 'message') ?? 'Undefined',
)
));
return null;
}
// * La réponse est conforme ?
$needed_fields = ['uuid'];
if (!Arr::has($result, $needed_fields)) {
report(new \RuntimeException(sprintf(
'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s',
json_encode($needed_fields),
json_encode($result),
)));
return null;
}
// * Tout est OK
return Arr::get($result, 'uuid') ?: null;
} catch (\RuntimeException $e) {
report($e);
// * Retenter ?
return null;
} //end try
}
}

View file

@ -0,0 +1,95 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
use App\Models\Range;
use App\Models\Settings;
use Illuminate\Support\Facades\Gate;
class HomeController extends Controller
{
/**
* Display home page
*
* @return \Illuminate\Contracts\View\View
*/
public function home(): \Illuminate\Contracts\View\View
{
if (!Gate::check('fo.gameEnabled')) {
\session()->flash('warning', trans('front.messages.game_missing_required_elements'));
}
return view('theme::pages.home');
}
/**
* Display products with game stickers
*
* @return \Illuminate\Contracts\View\View
* @phpcs:ignore Squiz.Commenting.FunctionComment.EmptyThrows
* @throws \Illuminate\Http\Exceptions\HttpResponseException
*/
public function showProducts(): \Illuminate\Contracts\View\View
{
// ? Show product list authorized ?
if (!Gate::allows('fo.showProducts')) {
\response()->redirectToRoute('fo.homepage')->throwResponse();
}
$rangeModelList = Range::query()->with('products')->orderBy('id')->get();
return view('theme::pages.products', compact('rangeModelList'));
}
/**
* Display Faq page
*
* @return \Illuminate\Contracts\View\View
*/
public function showFaq(): \Illuminate\Contracts\View\View
{
// ? Show QA authorized ?
if (!Gate::allows('fo.showFaq')) {
\response()->redirectToRoute('fo.homepage')->throwResponse();
}
$questionModelList = FaqQuestion::getCached();
$themeModelList = FaqTheme::getCached();
return view('theme::pages.faq', compact(
'questionModelList',
'themeModelList'
));
}
/**
* Show the teaser page if it is activated
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/
public function teaser(): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
{
/** @var \App\Models\Settings */
$settings = app(Settings::class);
if (!$settings->should_display_teaser_page) {
return redirect()->route('fo.homepage');
}
return view('theme::pages.teaser');
}
/**
* Show the end game page if it is activated
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
*/
public function endGame(): \Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
{
/** @var \App\Models\Settings */
$settings = app(Settings::class);
if (!$settings->should_display_end_page) {
return redirect()->route('fo.homepage');
}
return view('theme::pages.end-game');
}
}

View file

@ -0,0 +1,308 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Http\Requests\Fo\ParticipateRequest;
use App\Lib\Discount as LibDiscount;
use App\Mail\Won\PrizeWonAdminMail;
use App\Models\Customer;
use App\Models\InstantWin;
use App\Models\Participation;
use App\Models\Settings;
use App\Notifications\Customer\HasWonDiscount;
use App\Notifications\Customer\HasWonInstantWin;
use App\Repositories\Fo\CrmRepository;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class ParticipateController extends Controller
{
/** @var \App\Repositories\Fo\CrmRepository $crmRepository */
private $crmRepository;
/**
* ParticipateController
*
* @return void
*/
public function __construct()
{
$this->crmRepository = new CrmRepository();
$this->middleware(function (Request $request, callable $next) {
$this->authorize('fo.gameEnabled');
$this->assertUserHasEnoughInfoToUseMesBr();
return $next($request);
});
}
/**
* Display participation form
*
* @return \Illuminate\Contracts\View\View
*/
public function participate(): \Illuminate\Contracts\View\View
{
if (!Gate::check('participate', Participation::class)) {
return view('theme::pages.participation.participation-exhausted');
}
return view('theme::pages.participation.participate');
}
/**
* Participate Method
*
* @param \App\Http\Requests\Fo\ParticipateRequest $request
* @return \Illuminate\Contracts\View\View
*/
public function participateSubmit(ParticipateRequest $request): \Illuminate\Contracts\View\View
{
return DB::transaction(function () use ($request) {
// ! On verrouille le ou les instants gagnant
/** @var \App\Models\InstantWin|null */
$instantWin = InstantWin::query()->lockForUpdate()
->where('available_at', '<', now())
->whereDoesntHave('participation')
->get()->sortBy('available_at')->first();
/** @var \App\Models\Settings */
$settings = app(Settings::class);
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
/** @var \App\Models\Code|null */
$code = $request->post('unique_code');
/** @var \App\Models\Product|null */
$product = $request->post('product');
// * Make new participation
$participation = Participation::makeNew(
app('clientIp'),
$customer,
$code,
$product
);
// * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant.
// ? As-t-on un instant gagnant ?
if (Participation::customerAlreadyHasAnInstantWin($customer) or !$instantWin) {
// * --- DISCOUNT WIN ---
// ? Les BR sont activés ?
if (!$settings->discounts_activated) {
// * On enregistre la participation du joueur.
$participation->save();
// * Pas de coupons disponibles, le joueur à perdu
return view('theme::pages.participation.loose');
}
// * Le joueur à gagné un coupon.
/** @var \App\Lib\Discount Coupon aléatoire en fonction du ratio de chaque coupon */
$discount = Arr::get(
$settings->web_coupons,
$this->getRandomWeightedElementIndex($settings->web_coupons)
);
$this->setDiscountAsWon($customer, $discount);
// * Le joueur à remporté un coupon
$participation->discount_code = (string) $discount->web_coupon_identifiant;
// * On enregistre la participation du joueur.
$participation->save();
$crmCustomer = $this->crmRepository->fetchCrmCustomer($customer);
$customer->notify(new HasWonDiscount(
$discount,
$crmCustomer->firstname
));
return view('theme::pages.participation.won', compact('discount'));
} //end if
// * --- INSTANT WIN ---
// * On enregistre la participation du joueur.
$participation->save();
// * Le joueur à remporté un instant gagnant
$instantWin->participation()->associate($participation);
$instantWin->save();
$crmCustomer = $this->crmRepository->fetchCrmCustomer($customer);
// * Notify customer for his win
$customer->notify(new HasWonInstantWin(
$instantWin->prize,
$crmCustomer->firstname
));
// * Dispatch notification to mail list
Mail::to($settings->notification_emails)->send(new PrizeWonAdminMail(
$settings->name,
$participation->created_at,
$instantWin->prize->label,
$crmCustomer,
optional($code)->code_unique,
optional($product)->ean_code
));
return view('theme::pages.participation.won', [
'prizeModel' => $instantWin->prize,
'participationModel' => $participation,
'crmCustomer' => $crmCustomer
]);
});
}
// * --- PRIVATE METHODS ---
/**
* Try Call CRM + MesBr to setup discount
*
* @param \App\Models\Customer $customer
* @param LibDiscount $discount
* @return void
* @throws \Illuminate\Http\Exceptions\HttpResponseException If Any error occurs.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
*/
private function setDiscountAsWon(Customer $customer, LibDiscount $discount): void
{
// phpcs:enable
// * Phase - 1 Recupération des informations CRM
/** @var \LDC\LDCCRM\Models\Customer*/
$crmUser = $this->crmRepository->fetchCrmCustomer($customer);
// * Phase - 2 Mise à jour du coupon dans MesBr
try {
/** @var \App\Models\Settings */
$settings = app(Settings::class);
/** @var \LDC\MESBR\MesBr */
$mesBr = app('mesBrAPI');
// * MaJ Customer
/** @var string */
$crm_customer_hashcode = retry(3, function () use ($mesBr, $crmUser) {
/**
* Parce que MesBr n'est pas fiable il faut retenter pour avoir
* l'id client, des fois cet id sera dans hashcode...
*
* Id est necessaire pour update panier..
*/
$result = $mesBr->updateCustomer($crmUser->hashcode, $crmUser->email, $crmUser->country->label());
if (optional($result)->status !== "OK") {
throw new \RuntimeException(
\sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result->code ?? 'Undefined',
$result->message ?? 'Undefined'
)
);
}
/** @var object|null */
$crm_customer = optional($result)->customer;
if (empty(optional($crm_customer)->hashcode)) {
throw new \RuntimeException(
'MESBR : Une erreur est survenue. Update Customer n\'as pas retourné le customer hashcode'
);
}
return $crm_customer->hashcode;
}, 100);
// * Maj Panier
$result = $mesBr->updateBasket(
$crmUser,
strval($discount->web_coupon_identifiant)
);
if (Arr::get($result, 'status') === "OK") {
// * La réponse est conforme ?
$needed_fields = ['ref'];
if (!Arr::has($result, $needed_fields)) {
throw new \RuntimeException(sprintf(
'MESBR : Il manques des champs dans la réponse, attendu => %s, donné => %s',
json_encode($needed_fields),
json_encode($result),
));
}
// * Tout est OK
return;
}
throw new \RuntimeException(
\sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result["code"] ?? 'Undefined',
$result["message"] ?? 'Undefined'
)
);
} catch (\RuntimeException $e) {
report($e);
// * Retenter ?
back()->with('error', trans('front.messages.unexpected_error_try_later'))->throwResponse();
} //end try
throw new \Error('Should not reach this point');
}
/**
* Assert that user has enough info filled on his profile to win a Discount
* Or redirect him to the fill form
* ! Also validates its birth date (because beeing an adult is required to participate)
*
* @return void
* @throws \Illuminate\Http\Exceptions\HttpResponseException If customer data doest not validate
* or and error occurs.
*/
private function assertUserHasEnoughInfoToUseMesBr(): void
{
// * Valider que le client dispose des donnés requises pour MesBr
try {
/** @var \App\Models\Customer */
$authUser = auth('frontend')->user();
$crmCustomer = $this->crmRepository->fetchCrmCustomer($authUser);
// * On valide les données selon les critères du site
$rules = collect((new CreateAccountRequest())->rules());
Validator::make(
[
'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address,
'postal_code' => $crmCustomer->postcode,
'country' => $crmCustomer->country->value,
'city' => $crmCustomer->city
],
[
'birth_date' => $rules->get('birth_date'),
'address' => $rules->get('address'),
'postal_code' => $rules->get('postal_code'),
'country' => $rules->get('country'),
'city' => $rules->get('city')
]
)->validate();
Session::remove('needCrmInfoUpdate');
} catch (ValidationException) {
response()->redirectToRoute('fo.profil.verify')
->with('warning', trans(
'front.messages.profile_incomplete_cannot_play'
))->throwResponse();
} catch (\Exception $e) {
report($e);
// * Retenter ?
response()->redirectToRoute('fo.homepage')
->with('error', trans('front.messages.unexpected_error_try_later'))
->throwResponse();
} //end try
}
// * --- END PRIVATE METHODS ---
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\StaticPage;
use Illuminate\Contracts\View\View;
class StaticPageController extends Controller
{
/**
* Show the rules view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function reglement()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::rules)->firstOrFail();
return $this->viewStaticPage($staticPageModel);
}
/**
* Show the privacy view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function charte()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::confidentiality)->firstOrFail();
return $this->viewStaticPage($staticPageModel);
}
/**
* Show the legals view
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function legals()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::legals)->firstOrFail();
return $this->viewStaticPage($staticPageModel);
}
/**
* Display a static page
*
* @param \App\Models\StaticPage $staticPageModel
* @return \Illuminate\Contracts\View\View
*/
private function viewStaticPage(StaticPage $staticPageModel): View
{
return view('theme::pages.static_page', [
'staticPageModel' => $staticPageModel
]);
}
}

View file

@ -0,0 +1,160 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\SubmitWinningProofRequest;
use App\Mail\Won\WinningProofAdminMail;
use App\Models\Participation;
use App\Models\Settings;
use App\Notifications\Customer\HasSubmitedWinProof;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Validation\ValidationException as IlluminateValidationException;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
class WinningProofController extends Controller
{
/**
* WinningProofController
*
* @return void
*/
public function __construct()
{
$this->middleware(function (Request $request, callable $next) {
$this->authorize('fo.gameEnabled');
$this->authorize('submitProof', $request->route('participation'));
return $next($request);
});
}
/**
* Display winning proof form
*
* @param \App\Models\Participation $participation
* @return \Illuminate\Contracts\View\View
*/
public function displayForm(Participation $participation): View
{
$participation->load('instantWin.prize');
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
$crmCustomer = $customer->api_data;
return view('theme::pages.participation.won-form-only', [
'participationModel' => $participation,
'prizeModel' => $participation->instantWin->prize,
'crmCustomer' => $crmCustomer
]);
}
/**
* Submit winning proofs,
* - 1 Met à jour le profil du customer (sans lui en faire part),
* - 2 Save les pictures de preuve de gains
* - 3 Envoi un mail au abonnés (settings notification_emails) avec les infos client
*
* @param \App\Http\Requests\Fo\SubmitWinningProofRequest $request
* @param \App\Models\Participation $participation
* @return \Illuminate\Http\RedirectResponse
* @throws IlluminateValidationException In case of failure.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
*/
public function displayFormSubmit(
SubmitWinningProofRequest $request,
Participation $participation
): RedirectResponse {
// phpcs:enable
try {
return DB::transaction(function () use ($request, $participation) {
$participation->load([
'instantWin.prize',
'code',
'product'
]);
/** @var \App\Models\Settings */
$settings = app(Settings::class);
// * --- PHASE 1 Update Crm Customer ---
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
$crmCustomer = $customer->api_data;
/** @var array */
$data = $request->validated();
// * This field is not standard in crm
$data['postcode'] = Arr::get($data, 'postal_code');
Arr::forget($data, 'postal_code');
$customer->updateApiData(Arr::only(
$data,
[
'firstname',
'lastname',
'birth_date',
'address',
'address2',
'postcode',
'country',
'city',
'phone',
'email',
]
));
// * --- PHASE 2 Update participation ---
$participation->fill(Arr::only($request->validated(), [
'barcode',
'ticket',
'sticker'
]));
$participation->save();
$customer->notify(new HasSubmitedWinProof($request->input('firstname')));
// * Dispatch notification to mail list
Mail::to($settings->notification_emails)->send(new WinningProofAdminMail(
$settings->name,
$participation->created_at,
$participation->instantWin->prize->label,
$crmCustomer,
optional($participation->code)->code_unique,
optional($participation->product)->ean_code,
$participation->sticker,
$participation->ticket,
$participation->barcode,
));
return response()->redirectToRoute('fo.homepage')
->with('success', trans(
'front.messages.winning_proof_submitted_will_be_contacted'
));
});
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
}
report($e);
Log::error(__CLASS__ . '::create reached end of possibilities.');
throw new \RuntimeException(__CLASS__ . '::create reached end of possibilities.');
} catch (CrmValidationException $e) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
} //end try
}
}

View file

@ -1,14 +0,0 @@
<?php
namespace App\Http\Controllers;
class GainsController extends Controller
{
public function index()
{
if (view()->exists('front.basic.gains')) {
return view('front.basic.gains');
}
return redirect()->route('home');
}
}

View file

@ -1,11 +0,0 @@
<?php
namespace App\Http\Controllers;
class HomeController extends Controller
{
public function home()
{
return view('front.basic.home');
}
}

View file

@ -1,14 +0,0 @@
<?php
namespace App\Http\Controllers;
class HowToPlayController extends Controller
{
public function index()
{
if (view()->exists('front.basic.how-to-play')) {
return view('front.basic.how-to-play');
}
return redirect()->route('home');
}
}

View file

@ -1,275 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Mail\PasswordReset;
use App\Mail\VerifyEmail;
use App\Models\Settings;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
class LoginController extends Controller
{
private $apiCrm;
public function __construct()
{
$this->middleware('guest')->except('logout');
$this->apiCrm = new \LDCCRM\ApiV1(config('crm.endpoint'), config('crm.token'));
}
public function index()
{
return view('front.basic.login');
}
public function authenticate(Request $request)
{
$request->validate([
'email' => 'required|email',
'password' => 'required',
]);
$email = filter_var($request->email, FILTER_SANITIZE_EMAIL);
$password = $request->password;
$remember = $request->remember !== null ? true : false;
$userLocal = User::where('email', $email)->first();
if ($userLocal) {
if ($userLocal->email_verified === 1) {
if ($userLocal->role > 2) {
$connect = $this->apiCrm->loginCustomer($email, $password);
if ($connect->status === 'KO') {
if ($connect->code === 500) {
return back()->withInput($request->except("password"))
->withErrors("La connexion est impossible pour le moment, veuillez réessayer plus tard.");
} else {
return back()->withInput($request->except("password"))
->withErrors("Email ou mot de passe incorrect.");
}
}
}
} else {
$userLocal->email_verify_token = Str::uuid();
$userLocal->save();
Mail::to($email)->send(new VerifyEmail($userLocal));
return back()->withInput($request->except("password"))
->withErrors("Votre compte existe mais votre email n'est pas confirmé, merci de vérifier vos emails.");
}//end if
} else {
$connect = $this->apiCrm->loginCustomer($email, $password);
if ($connect->status === 'OK') {
$userLocal = User::create([
'crm_id' => $connect->user->crm_id,
'firstname' => $connect->user->firstname,
'lastname' => $connect->user->lastname,
'birth_date' => $connect->user->birth_date,
'email' => $email,
// TODO : MOTDEPASSE ???.
'password' => Hash::make("MOTDEPASSE"),
'address' => $connect->user->address,
'address2' => $connect->user->address2,
'town' => $connect->user->city,
'postal_code' => $connect->user->postcode,
'country' => $connect->user->country,
'email_verified' => 1
]);
} else {
// Compte existant mais non activé.
if ($connect->code === 116) {
$userLocal = User::create([
'crm_id' => $connect->user->crm_id,
'firstname' => $connect->user->firstname,
'lastname' => $connect->user->lastname,
'birth_date' => $connect->user->birth_date,
'email' => $email,
// TODO : MOTDEPASSE ???.
'password' => Hash::make("MOTDEPASSE"),
'address' => $connect->user->address,
'address2' => $connect->user->address2,
'town' => $connect->user->city,
'postal_code' => $connect->user->postcode,
'country' => $connect->user->country
]);
$userLocal->email_verify_token = Str::uuid();
$userLocal->save();
Mail::to($email)->send(new VerifyEmail($userLocal));
return back()->withInput($request->except("password"))
->withErrors("Votre compte existe mais votre email n'est pas confirmé, merci de vérifier vos emails.");
} elseif ($connect->code === 500) {
return back()->withInput($request->except("password"))
->withErrors("La connexion est impossible pour le moment, veuillez réessayer plus tard.");
} else {
return back()->withInput($request->except("password"))
->withErrors("Email ou mot de passe incorrect.");
} //end if
} //end if
}//end if
// TODO : Modifier avec separation guard.
$tmpPwd = "MOTDEPASSE";
if ($userLocal->role <= 2) {
$tmpPwd = $password;
}
if (Auth::attempt(["email" => $email, "password" => $tmpPwd], $remember)) {
if ($userLocal->role <= 2) {
return redirect()->route('bo.dashboard');
}
return redirect()->route('saisir-code');
} else {
return back()->withInput($request->except('password'))->withErrors('Les identifiants entrés sont incorrects.');
}
}
public function logout()
{
Auth::logout();
return redirect()->route('home');
}
public function confirmEmail($verifyToken)
{
$result = false;
$user = User::where('email_verify_token', $verifyToken)->first();
if (!$user) {
return view('front.basic.confirm-email', compact('result'));
}
$activation = $this->apiCrm->activateCustomer($user->crm_id);
if ($activation->status === 'OK') {
$user->email_verified = 1;
$user->email_verify_token = null;
$user->save();
$result = true;
} else {
// User already active.
if ($activation->code === 110) {
$user->email_verified = 1;
$user->email_verify_token = null;
$user->save();
$result = true;
}
}
return view('front.basic.confirm-email', compact('result'));
}
public function forgotPassword(Request $request)
{
if ($request->tokenValid && $request->token) {
$tokenValid = $request->tokenValid;
$token = $request->token;
return view('front.basic.forgot-password', compact('tokenValid', 'token'));
}
return view('front.basic.forgot-password');
}
public function forgotPasswordReset(Request $request, $token)
{
if (!$request->hasValidSignature(false)) {
return redirect()->route('login')
->withErrors("Le lien de changement de votre mot de passe n'est plus valide, veuillez réessayer.");
}
$tokenValid = false;
$user = User::where('password_token', $token)->first();
if (!$user) {
return redirect()->route('forgot-password', compact('tokenValid'))
->withErrors('Le lien de changement du mot de passe est incorrect.');
}
$tokenValid = true;
return redirect()->route('forgot-password', compact('tokenValid', 'token'));
}
public function mailPassword(Request $request)
{
$email = filter_var($request->email, FILTER_SANITIZE_STRING);
$user = User::where('email', $email)->first();
if (!$user) {
$userCrm = $this->apiCrm->getCustomerByEmail($email);
if ($userCrm->status === 'OK') {
$user = User::create([
'crm_id' => $userCrm->user->crm_id,
'firstname' => $userCrm->user->firstname,
'lastname' => $userCrm->user->lastname,
'birth_date' => $userCrm->user->birth_date,
'email' => $email,
// TODO : MOTDEPASSE ???
'password' => Hash::make("MOTDEPASSE"),
'address' => $userCrm->user->address,
'address2' => $userCrm->user->address2,
'town' => $userCrm->user->city,
'postal_code' => $userCrm->user->postcode,
'country' => $userCrm->user->country,
'email_verified' => 1
]);
if ($userCrm->user->status === 0) {
$user->email_verified = 0;
$user->email_verify_token = Str::uuid();
$user->save();
Mail::to($email)->send(new VerifyEmail($user));
return back()->withInput($request->except("password"))
->withErrors("Votre compte existe mais votre email n'est pas confirmé, merci de vérifier vos emails.");
}
} else {
if ($userCrm->code === 101) {
return back()->withInput($request->only('email'))
->withErrors("Le mail que vous avez entré n'est pas enregistré dans nos services.");
} else {
return back()->withErrors("Une erreur est survenue, veuillez réessayer plus tard.");
}
} //end if
}//end if
$user->password_token = Str::uuid();
$user->save();
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
Mail::to($email)->send(new PasswordReset($user, $settings));
return redirect()->route('login')
->with('success', 'Un mail de réinitialisation de votre mot de passe vous a été envoyé(e).');
}
public function changePassword(Request $request)
{
$request->validate([
'password' => 'required|min:8|confirmed',
]);
$user = User::where('password_token', filter_var($request->token, FILTER_SANITIZE_STRING))->first();
if (!$user) {
return back()->withErrors('Le lien de changement de votre mot de passe est incorrect, veuillez réessayer.');
}
// TODO : Ajouter controle du contenu du mot de passe.
$newPassword = $request->password;
$update = $this->apiCrm->changePwdCustomer($user->crm_id, $newPassword);
if ($update->status === 'OK') {
$user->password_token = null;
$user->save();
return redirect()->route('login')
->with('success', 'Vous pouvez désormais vous connecter avec votre nouveau mot de passe.');
} else {
if ($update->code === 123) {
return back()
->withErrors("Il semblerait que vous ayez déjà utilisé ce mot de passe pour votre compte. Veuillez réessayer en changeant de mot de passe.");
} else {
return back()->withErrors("Une erreur est survenue, veuillez réessayer plus tard.");
}
}
}
}

View file

@ -1,17 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Range;
class ProduitsController extends Controller
{
public function index()
{
$ranges = Range::with('products')->orderBy('id')->get();
if (view()->exists('front.basic.produits-porteurs')) {
return view('front.basic.produits-porteurs', compact('ranges'));
}
return redirect()->route('home');
}
}

View file

@ -1,143 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Mail\VerifyEmail;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
class RegisterController extends Controller
{
use RegistersUsers;
// TODO : Utilité ??
protected $redirectTo = RouteServiceProvider::HOME;
private $apiCrm;
public function __construct()
{
$this->middleware('guest');
$this->apiCrm = new \LDCCRM\ApiV1(config('crm.endpoint'), config('crm.token'));
}
public function index()
{
return view('front.basic.register-crm');
}
public function register(Request $request)
{
$request->validate([
'lastname' => 'required|string|max:255',
'firstname' => 'required|string|max:255',
'birth_date' => 'required|string|max:10',
'email' => 'required|string|email|max:255|unique:users|confirmed',
'password' => 'required|string|min:8|confirmed',
'address' => 'required|string|max:255',
'address2' => 'max:255',
'town' => 'required|string|max:255',
'postal_code' => 'required|string|max:255',
'country' => 'required|string|max:255',
'reglement' => 'accepted',
]);
$lastname = filter_var($request->lastname, FILTER_SANITIZE_STRING);
$firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
$birthdate = $request->birth_date;
$email = filter_var($request->email, FILTER_SANITIZE_EMAIL);
$password = $request->password;
$address = filter_var($request->address, FILTER_SANITIZE_STRING);
$address2 = $request->address2 !== null ? filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
$town = filter_var($request->town, FILTER_SANITIZE_STRING);
$postcode = filter_var($request->postal_code, FILTER_SANITIZE_STRING);
$country = filter_var($request->country, FILTER_SANITIZE_STRING);
$optin = $request->news !== null ? 1 : 0;
$tokenMail = Str::uuid();
$MJ = Carbon::now()->subYears(18);
if ($this->isSQLDate($birthdate)) {
$d = Carbon::createFromFormat('Y-m-d', $birthdate);
if (!$d->lessThanOrEqualTo($MJ)) {
return back()
->withInput($request->except('password'))
->withErrors("Vous devez être majeur pour vous inscrire au jeu.");
}
} elseif ($this->isFormDate($birthdate)) {
$d = Carbon::createFromFormat('d/m/Y', $birthdate);
if (!$d->lessThanOrEqualTo($MJ)) {
return back()
->withInput($request->except('password'))
->withErrors("Vous devez être majeur pour vous inscrire au jeu.");
}
$birthdate = $d->toDateString();
} else {
return back()
->withInput($request->except('password'))
->withErrors("Erreur sur le champs : date de naissance.");
}
$crmUser = $this->apiCrm->createCustomer($email, $password, $firstname, $lastname, $optin, $birthdate, null, $address, $address2, $town, $postcode, ucfirst($country));
if ($crmUser->status === 'OK') {
$user = User::create([
'crm_id' => $crmUser->user->crm_id,
'firstname' => $firstname,
'lastname' => $lastname,
'birth_date' => $birthdate,
'email' => $email,
'password' => Hash::make("MOTDEPASSE"), // TODO : ???
'address' => $address,
'address2' => $address2,
'town' => $town,
'postal_code' => $postcode,
'country' => ucfirst($country),
'email_verify_token' => $tokenMail,
]);
Mail::to($email)->send(new VerifyEmail($user));
return redirect()->route('login')
->with('success', "Pour finaliser votre inscription, merci de cliquer sur le lien qui vient de vous être envoyé par email<br>" .
"Informations complémentaires :<br>" .
"- Vérifiez vos spams : l'email de confirmation s'y trouve peut-être<br>" .
"- Attention : le mail peut parfois mettre quelques minutes à vous parvenir<br>" .
"- L'activation de votre compte doit se faire dans les 24 heures qui suivent la demande de création.");
} else {
if ($crmUser->code === 103) {
return back()
->withInput($request->except('password'))
->withErrors("Un compte utilisateur est déjà associé à votre email.");
} else {
return back()
->withInput($request->except('password'))
->withErrors("Une erreur est survenue, merci de recommencer.");
}
}//end if
}
private function isSQLDate($date)
{
if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/", $date)) {
return true;
}
return false;
}
private function isFormDate($date)
{
if (preg_match("/^(0[1-9]|[1-2][0-9]|3[0-1])\/(0[1-9]|1[0-2])\/[0-9]{4}$/", $date)) {
return true;
}
return false;
}
}

View file

@ -1,18 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Settings;
class TeaserController extends Controller
{
public function index()
{
$settings = app(Settings::class)
->select('start_date', 'end_date', 'activated_teaser', 'teaser_end_activity')->first();
if ($settings->activated_teaser === 1 && $settings->teaser_end_activity > now()) {
return view('front.basic.teaser', compact('settings'));
}
return redirect()->route('home');
}
}

View file

@ -1,26 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Content;
class ViewContentController extends Controller
{
public function reglement()
{
$content = Content::find(3);
return view('front.basic.reglement', compact('content'));
}
public function charte()
{
$content = Content::find(2);
return view('front.basic.charte', compact('content'));
}
public function legals()
{
$content = Content::find(1);
return view('front.basic.legals', compact('content'));
}
}

View file

@ -1,133 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Mail\NotifAdmin;
use App\Mail\WinningProof;
use App\Models\Participation;
use App\Models\Settings;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class WinningProofController extends Controller
{
private $apiCrm;
public function __construct()
{
$this->apiCrm = new \LDCCRM\ApiV1(config('crm.endpoint'), config('crm.token'));
}
public function sendWinningProof(Request $request)
{
// On récupère la participation gagnante en attente des infos
$participation = Participation::where([
['user_id', Auth::user()->id],
['sticker', null],
['ticket', null],
['barcode', null]
])->has('instantWin')->first();
if (!$participation) {
return redirect()->route('home');
}
$request->validate([
'lastname' => 'required|string|max:255',
'firstname' => 'required|string|max:255',
'address' => 'required|string|max:255',
'address2' => 'max:255',
'town' => 'required|string|max:255',
'postal_code' => 'required|max:255',
'country' => 'required|string|max:255',
'phone' => 'required|string|max:50',
'sticker' => 'required|mimes:jpeg,png,jpg,pdf|max:2048',
'ticket' => 'required|mimes:jpeg,png,jpg,pdf|max:2048',
'barcode' => 'required|mimes:jpeg,png,jpg,pdf|max:2048',
]);
$lastname = filter_var($request->lastname, FILTER_SANITIZE_STRING);
$firstname = filter_var($request->firstname, FILTER_SANITIZE_STRING);
$address = filter_var($request->address, FILTER_SANITIZE_STRING);
$address2 = $request->address2 !== null ? filter_var($request->address2, FILTER_SANITIZE_STRING) : null;
$town = filter_var($request->town, FILTER_SANITIZE_STRING);
$postalCode = filter_var($request->postal_code, FILTER_SANITIZE_STRING);
$country = filter_var($request->country, FILTER_SANITIZE_STRING);
$phone = filter_var($request->phone, FILTER_SANITIZE_STRING);
$sticker = $request->sticker;
$ticket = $request->ticket;
$barcode = $request->barcode;
// Mise à jour de l'utilisateur en local.
$user = User::find(Auth::user()->id);
$user->lastname = $lastname;
$user->firstname = $firstname;
$user->address = $address;
$user->address2 = $address2;
$user->town = $town;
$user->postal_code = $postalCode;
$user->country = $country;
$user->phone = $phone;
$user->save();
// Mise à jour utilisateur dans la CRM.
$update = $this->apiCrm->updateCustomer(
$user->crm_id,
$firstname,
$lastname,
null,
null,
$phone,
$address,
$address2,
$town,
$postalCode,
ucfirst($country)
);
if ($update->status !== 'OK') {
Log::warning('Erreur lors de la mise a jour du user (crm_id : ' . $user->crm_id .
') - code : ' . $update->code . ' - message : ' . $update->message);
Mail::to(config('mail.MAIL_ADMIN'))->send(new NotifAdmin(
'CRM : Erreur lors de la mise a jour du user (crm_id : ' .
$user->crm_id . ') - code : ' . $update->code . ' - message : ' . $update->message
));
}
$participationId = $participation->id;
$stickerFileName = $participationId . '-sticker.' . $sticker->extension();
$sticker->move(public_path('fichiers/preuve-de-gain/' . $participationId . '/'), $stickerFileName);
$stickerPath = public_path() . '/fichiers/preuve-de-gain/' . $participationId . '/' . $stickerFileName;
$ticketFileName = $participationId . '-ticket.' . $ticket->extension();
$ticket->move(public_path('fichiers/preuve-de-gain/' . $participationId . '/'), $ticketFileName);
$ticketPath = public_path() . '/fichiers/preuve-de-gain/' . $participationId . '/' . $ticketFileName;
$barcodeFileName = $participationId . '-code-barre.' . $barcode->extension();
$barcode->move(public_path('fichiers/preuve-de-gain/' . $participationId . '/'), $barcodeFileName);
$barcodePath = public_path() . '/fichiers/preuve-de-gain/' . $participationId . '/' . $barcodeFileName;
$participation->sticker = $stickerFileName;
$participation->ticket = $ticketFileName;
$participation->barcode = $barcodeFileName;
$participation->save();
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
$mails = json_decode($settings->notification_emails);
foreach ($mails as $mail) {
Mail::to($mail->email)->send(new WinningProof(
$stickerPath,
$ticketPath,
$barcodePath,
$participation,
$user,
$settings
));
}
return view('front.basic.replay');
}
}

View file

@ -1,70 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\Models\Settings;
use App\Models\Reduction;
class re7Controller extends Controller
{
public function index()
{
return view('re7');
}
public function teaser()
{
$settings = app(Settings::class)
->select('start_date', 'end_date', 'activated_teaser', 'teaser_end_activity')->first();
return view('front.basic.teaser', compact('settings'));
}
public function prizeMail()
{
return view('front.basic.prize-mail');
}
public function reductionMail()
{
return view('front.basic.reduction-mail');
}
public function br()
{
$settings = app(Settings::class);
$coupon = $this->getRandomCoupon($settings);
$crmCoupon = new Reduction();
return view('front.basic.reduction', compact('coupon', 'crmCoupon'));
}
public function gain1()
{
return view('front.basic.gain1');
}
public function gain2()
{
return view('front.basic.gain2');
}
private function getRandomCoupon($settings)
{
$webCoupons = json_decode($settings->web_coupons);
if (count($webCoupons) > 1) {
$lots = [];
foreach ($webCoupons as $webCoupon) {
$lots[] = $webCoupon;
}
foreach ($lots as $lot) {
for ($i = 0; $i < $lot->percentage; $i++) {
$tmp[] = $lot;
}
}
shuffle($tmp);
return $tmp[0];
} else {
return $webCoupons[0];
}
}
}

View file

@ -11,12 +11,13 @@ class Kernel extends HttpKernel
*
* These middleware are run during every request to your application.
*
* @var array
* @var array<int, string>
*/
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
@ -25,20 +26,23 @@ class Kernel extends HttpKernel
/**
* The application's route middleware groups.
*
* @var array
* @var array<string, array<int, string>>
*/
protected $middlewareGroups = [
'web' => [
'htmlsanitize',
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class,
\App\Http\Middleware\CheckThemeVersion::class
],
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
@ -48,22 +52,20 @@ class Kernel extends HttpKernel
*
* These middleware may be assigned to groups or used individually.
*
* @var array
* @var array<string, string>
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'game-date' => \App\Http\Middleware\GameCheckDate::class,
'is-admin' => \App\Http\Middleware\isAdmin::class,
'is-super-admin' => \App\Http\Middleware\isSuperAdmin::class,
're7' => \App\Http\Middleware\re7Middleware::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'check-game-status' => \App\Http\Middleware\CheckGameStatus::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
];
}

View file

@ -3,19 +3,37 @@
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
protected function redirectTo(Request $request): ?string
{
if (! $request->expectsJson()) {
return route('login');
$logins = [
'backend' => route('bo.login')
];
if (!$request->expectsJson()) {
// * Notify must login
Session::flash('warning', trans('Veuillez vous connecter pour continuer'));
foreach ($request->route()->gatherMiddleware() as $middleware) {
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) {
$route = $logins[str_replace('auth:', '', $middleware)] ?? null;
if ($route) {
return $route;
}
}
}
return route('fo.login');
}
return null;
}
}

View file

@ -1,18 +0,0 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
class CheckForMaintenanceMode extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array
*/
protected $except = [
//
];
}

View file

@ -0,0 +1,38 @@
<?php
namespace App\Http\Middleware;
use App\Models\Settings;
use Illuminate\Http\Request;
class CheckGameStatus
{
/**
* Handle an incoming request.
*
* * Check with game settings which page should be shown and redirect if needed.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
* @throws \RuntimeException If tha game has an unhandled state.
*/
public function handle(Request $request, \Closure $next)
{
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
return match (true) {
// * End page
$settings->should_display_end_page => redirect()->route('fo.end'),
// * Teaser
$settings->should_display_teaser_page => redirect()->route('fo.teaser'),
// * Game is ended
$settings->game_has_ended => redirect($settings->redirect_close),
// * Game has started
$settings->game_has_begun => $next($request),
// * The game will begun, but has no teaser activated.
default => redirect($settings->redirect_close)
};
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\Response;
class CheckThemeVersion
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request):(\Symfony\Component\HttpFoundation\Response) $next
* @return \Symfony\Component\HttpFoundation\Response
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle(Request $request, \Closure $next): Response
{
// phpcs:enable
if (!app()->runningInConsole()) {
$appInfos = Cache::remember('composer', 84600, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
// * Assert theme version does not diverge more than minor
$appVer = Str::of($appInfos->version)->explode('.');
$themeVer = Str::of(config('theme.version'))->explode('.');
if (
// * Major check
Arr::get($appVer, 0) !== Arr::get($themeVer, 0) or
// * Patch check
Arr::get($appVer, 1) !== Arr::get($themeVer, 1)
) {
Session::flash('warning', trans(
'Ce thème n\'a semble t-il pas été vérifié pour cette version du moteur'
));
}
}
return $next($request);
}
}

View file

@ -6,13 +6,10 @@ use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
* @var array<int, string>
*/
protected $except = [
//
];
protected $except = [];
}

View file

@ -1,40 +0,0 @@
<?php
namespace App\Http\Middleware;
use App\Models\Settings;
use Closure;
class GameCheckDate
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
// Check si le teaser est actif
if ($settings->start_date > now()) {
if ($settings->activated_teaser === 1 && $settings->teaser_end_activity > now()) {
return redirect()->route('teaser');
}
return redirect($settings->redirect_close);
}
// Check si le jeu est terminé
if ($settings->end_date < now()) {
if ($settings->activated_close_page === 1 && $settings->close_page_active_date > now()) {
return redirect()->route('end');
}
return redirect($settings->redirect_close);
}
return $next($request);
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Http\Middleware;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class HtmlSanitizer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|array ...$params
* @return \Symfony\Component\HttpFoundation\Response
*/
public function handle(Request $request, \Closure $next, string|array ...$params): Response
{
$inputs = $request->all();
/** @var callable|null */
$cleanInput = null;
$cleanInput = function (
null|string|array &$input,
string $name,
?string $rootName = null
) use (
$params,
&$cleanInput
): void {
// * Nested element array
if (is_array($input)) {
array_walk_recursive($input, $cleanInput, $name);
}
if (!is_string($input)) {
return;
}
if (in_array($name, $params) or in_array($rootName ?? $name, $params)) {
// ? Is a json input ?
if ($isJson = \json_decode($input, true) and is_array($isJson)) {
// ! Decode each json field recursive.
\array_walk_recursive($isJson, function (string|array &$item) {
if (\is_string($item)) {
$item = clean($item);
$item = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($item);
}
});
} else {
$input = clean($input);
$input = ToolboxHelper::stripCkEditorSchemesOnMediaInputs($input);
}
} else {
$input = strip_tags($input);
}
};
\array_walk($inputs, $cleanInput);
$request->merge($inputs);
return $next($request);
}
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array<int, string>
*/
protected $except = [];
}

View file

@ -2,24 +2,40 @@
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
* @param \Illuminate\Http\Request $request
* @param \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response) $next
* @param string|null ...$guards
* @return \Symfony\Component\HttpFoundation\Response
* @throws \RuntimeException If guard is unhandled.
* @phpcs:disable Squiz.Commenting.FunctionComment.IncorrectTypeHint
*/
public function handle($request, Closure $next, $guard = null)
public function handle(Request $request, \Closure $next, ...$guards): Response
{
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
// phpcs:enable phpcs:disable
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (auth($guard)->check()) {
switch ($guard) {
case 'frontend':
return \redirect()->route('fo.homepage');
case 'backend':
if (!$request->routeIs('bo.*')) {
continue 2;
}
return \redirect()->route('bo.dashboard');
default:
throw new \RuntimeException("Unhandled guard `{$guard}` .");
}
}
}
return $next($request);

View file

@ -0,0 +1,97 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
public function handle(Request $request, Closure $next): Response
{
// * For Vite DEV
$host = \parse_url((string) \config('app.url'), \PHP_URL_HOST);
$viteDevOrigin = config('app.debug') && \is_string($host) && $host !== '' ? "https://{$host}:4443" : null;
$viteDevSocket = config('app.debug') && \is_string($host) && $host !== '' ? "wss://{$host}:4443" : null;
View::share('nonce', $nonce = Vite::useCspNonce());
// * For VueJS dev tool to execute
$jsDev = config('app.debug') ? "'unsafe-eval' 'unsafe-inline'" : "'nonce-{$nonce}' 'strict-dynamic'";
$scheme = config('app.env') === 'local' ? 'http' : 'https';
$reportUri = Route::has('cspReportUri') ? \sprintf(' report-uri %s ;', \parse_url(route('cspReportUri'), \PHP_URL_PATH)) : '';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains;');
// Utilisation de array_filter pour ne pas inclure les valeurs null en production
$cspPolicies = [
"default-src 'none';",
"manifest-src 'self';",
"connect-src 'self' blob: " . implode(' ', array_filter([
$viteDevSocket,
$viteDevOrigin,
'https://yl.s-web.fr'
])) . ';',
"img-src 'self' data: blob: " . implode(' ', array_filter([
$viteDevOrigin,
])) . ';',
"object-src 'self';",
"frame-src 'self' " . implode(' ', [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/'
]) . ';',
"font-src 'self' data: " . implode(' ', array_filter([
$viteDevOrigin,
'https://fonts.gstatic.com' // <-- Point-virgule retiré ici
])) . ';',
"style-src 'self' 'unsafe-inline' " . implode(' ', array_filter([
'https://cdnjs.cloudflare.com',
'https://fonts.googleapis.com',
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
$viteDevOrigin,
])) . ';',
"script-src 'self' {$jsDev} " . implode(' ', array_filter([
'https:',
'blob:',
'https://www.googletagmanager.com',
'https://cdnjs.cloudflare.com',
"$scheme://www.google-analytics.com",
$viteDevOrigin,
])) . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
return $response;
}
private function removeUnwantedHeaders(array $headerList): void
{
foreach ($headerList as $header) {
header_remove($header);
}
}
}

View file

@ -6,13 +6,13 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
* @var array<int, string>
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];

View file

@ -9,7 +9,7 @@ class TrustHosts extends Middleware
/**
* Get the host patterns that should be trusted.
*
* @return array
* @return array<int, string|null>
*/
public function hosts()
{

View file

@ -2,23 +2,27 @@
namespace App\Http\Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string|null
* @var array<int, string>|string|null
*/
protected $proxies;
protected $proxies = '*';
/**
* The headers that should be used to detect proxies.
*
* @var int
* @var integer
*/
protected $headers = Request::HEADER_X_FORWARDED_ALL;
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |
Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -0,0 +1,15 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Routing\Middleware\ValidateSignature as Middleware;
class ValidateSignature extends Middleware
{
/**
* The names of the query string parameters that should be ignored.
*
* @var array<int, string>
*/
protected $except = [];
}

View file

@ -6,13 +6,12 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
* @var array<int, string>
*/
protected $except = [
//
'/csp-report'
];
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class isAdmin
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$role = Auth::user()->role;
if ($role === 1 || $role === 2) {
return $next($request);
}
abort(404, "Cette page n'existe pas.");
}
}

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