Compare commits

..

9 commits

Author SHA1 Message Date
Jérémy Munsch
2093fc32e3
WIP (A tester MANQUE MESBR): Add CRM V3 + Remove Arcadia Stuff + Some refactor + Win List Save comments + MAIL_ADMIN check on boot + add Swal + Vues blades mis à jour (pas dans basic example) 2022-03-18 14:44:37 +01:00
Jérémy Munsch
fbcb8bc5bf
Customer and User separation 2022-03-16 10:47:17 +01:00
Jérémy Munsch
0457681ef8
Instant Win refactorize + add Big prize always at then end of the game 2022-03-11 14:52:48 +01:00
Jérémy Munsch
1ae5307b55
phpcbf pass 2022-03-11 09:59:44 +01:00
Jérémy Munsch
2831d422c9
Refactorize Settings BO save
- require composer kwaadpepper/enum
- User->role is now an enum
- Add isLocal and isAdmin to user model
- changed multiple fields casts on model Settings
- added validation for settings
2022-03-11 09:59:40 +01:00
Jérémy Munsch
189f2feb29
Upgrade Flash message module 2022-03-10 13:57:09 +01:00
Jérémy Munsch
bbb175ad61
Fully remove 'teaser_end_activity' from settings 2022-03-10 13:56:56 +01:00
Jérémy Munsch
0781344016
composer update 2022-03-10 13:35:58 +01:00
Jérémy Munsch
b0f0763351
Force add project color 2022-03-10 13:34:00 +01:00
625 changed files with 13445 additions and 47742 deletions

View file

@ -1,5 +0,0 @@
# 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
indent_size = 4
indent_style = space
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
[*.md]
@ -13,9 +13,3 @@ trim_trailing_whitespace = false
[*.{yml,yaml}]
indent_size = 2
[docker-compose.yml]
indent_size = 4
[*.vue]
indent_size = 2

View file

@ -5,11 +5,9 @@ APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=dev-mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
@ -17,51 +15,24 @@ 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=dev-mailpit
MAIL_PORT=1025
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
MAIL_ADMIN=digital@ldc.fr
# Sentry
SENTRY_LARAVEL_DSN=
# MATOMO
MATOMO_SITE_ID=
MAIL_ADMIN=informatique.internet@ldc.fr
# 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,11 +1,5 @@
* text=auto eol=lf
*.blade.php diff=html
*.css diff=css
*.html diff=html
*.md diff=markdown
*.php diff=php
/.github export-ignore
* text=auto
*.css linguist-vendored
*.scss linguist-vendored
*.js linguist-vendored
CHANGELOG.md export-ignore
.styleci.yml export-ignore

55
.gitignore vendored
View file

@ -1,46 +1,29 @@
.env
.env.backup
.env.production
.phpunit.result.cache
.stylelintcache
/.fleet
/.idea
/.phpunit.cache
/.vscode
public/front/*
/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
auth.json
docker-compose.override.yml
/resources/views/front/basic
.env
.env.dev
.env.prod
.env.backup
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc
/resources/theme
# https://lab.s-web.fr/ldc-web/projets/gen-game-web/-/issues/17
public/fichiers
package-lock.json
composer.lock
resources/front
.vscode
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

View file

@ -1,11 +0,0 @@
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
View file

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

View file

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

13
.styleci.yml Normal file
View file

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

View file

@ -1,43 +0,0 @@
{
"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
View file

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

View file

@ -1,59 +1,61 @@
# GEN GAME WEB
<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>
## Installation
<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>
``` 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
```
## About Laravel
## Création du compte Admin
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:
``` Bash
php artisan uwil:user:create
```
- [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).
## Ajouter Parametrage du jeu
Laravel is accessible, powerful, and provides tools required for large, robust applications.
**Les lots sont à ajouter avant de générer les instants gagants**. En effet les instants gagants sont liés à des lots.
## Learning Laravel
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.
## Générer des coupons et les mails
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.
``` Bash
artisan queue:work
```
## Laravel Sponsors
En cas de manipulation pourrie, penser a vider le cache pour supprimer le lock
sur le job de generation des codes.
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).
## THEME
### Premium Partners
### Instalation
- **[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)**
Pour installer le theme bien **mettre à jour les submodules avec git**,
`git submodule update --init --recursive`
## Contributing
puis utiliser la commande `php artisan uwil:theme:switch NOM_DU_THEME` et `php artisan optimize:clear && php artisan optimize`
Thank you for considering contributing to the Laravel framework! The contribution guide can be found in the [Laravel documentation](https://laravel.com/docs/contributions).
Pour mettre a jour la liste des themes :
## Code of Conduct
### Mise à jour
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).
**Note** : cette partie n'est pas encore certaine.
## Security Vulnerabilities
- Mettre à jour `git submodule update --remote --merge resources/theme`
Ou
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.
``` Bash
cd resources/themes;
git pull origin main;
cd ../..;
git add resources/themes
```
## License
Puis commit.
The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).

View file

@ -1,188 +0,0 @@
<?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

@ -1,46 +0,0 @@
<?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());
}
}

View file

@ -1,65 +0,0 @@
<?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}"
);
}
}
}

View file

@ -1,61 +0,0 @@
<?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

@ -0,0 +1,112 @@
<?php
namespace App\Console\Commands;
use App\Enums\UserRole;
use App\Models\User;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Kwaadpepper\Enum\Rules\EnumIsValidRule;
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 = 'Create an admin user';
/**
* Execute the console command.
*
* @return integer
*/
public function handle()
{
$name = null;
while (is_null($name)) {
try {
$tmp = $this->ask('What is the name?', 'Equipe Web');
$validator = Validator::make(['name' => $tmp], [
'name' => 'required|min:5,max:255'
]);
$validator->validate();
$name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$email = null;
while (is_null($email)) {
try {
$tmp = $this->ask('What is the email?', 'equipe.web@ldc.fr');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|unique:users,email|email:rfc,dns'
]);
$validator->validate();
$email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$role = null;
while (is_null($role)) {
try {
$tmp = $this->ask('Type the wanted user role', sprintf(
'%d (%s), %d (%s)',
UserRole::admin()->value,
UserRole::admin()->label,
UserRole::brand()->value,
UserRole::brand()->label
));
$validator = Validator::make(['role' => $tmp], [
'role' => [
'required', new EnumIsValidRule(UserRole::class),
sprintf('in:%d,%d', UserRole::admin()->value, UserRole::brand()->value)
]
]);
$validator->validate();
$role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid role %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
} //end while
$password = $this->secret('What is the password?');
$user = new User();
$user->name = $name;
$user->email = $email;
$user->password = Hash::make($password);
$user->role = $role;
$user->saveOrFail();
$this->info('User created.');
return 0;
}
}

View file

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

View file

@ -0,0 +1,192 @@
<?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

@ -1,35 +0,0 @@
<?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

@ -1,77 +0,0 @@
<?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

@ -1,231 +0,0 @@
<?php
namespace App\Console\Commands;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
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,7 +2,6 @@
namespace App\Console;
use App\Jobs\DeleteOlds\DeleteOldCustomers;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@ -23,9 +22,6 @@ 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

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

View file

@ -1,25 +0,0 @@
<?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'
];
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self legals()
* @method static self charte()
* @method static self reglement()
*/
final class ContentPageType extends BaseEnumRoutable
{
/**
* Enum values
*
* @return array
*/
protected static function values(): array
{
return [
'legals' => 1,
'charte' => 2,
'reglement' => 3
];
}
/**
* Enum values
*
* @return array
*/
protected static function labels(): array
{
return [
'legals' => trans('Mentions légales'),
'charte' => trans('Charte'),
'reglement' => trans('Règlement')
];
}
}

View file

@ -1,30 +0,0 @@
<?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

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

View file

@ -1,25 +0,0 @@
<?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

@ -1,23 +0,0 @@
<?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',
];
}

38
app/Enums/UserRole.php Normal file
View file

@ -0,0 +1,38 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnumRoutable;
/**
* @method static self admin()
* @method static self brand()
*/
final class UserRole extends BaseEnumRoutable
{
/**
* Enum values
*
* @return array
*/
protected static function values(): array
{
return [
'admin' => 0,
'brand' => 1
];
}
/**
* Enum labels
*
* @return array
*/
protected static function labels(): array
{
return [
'admin' => trans('enums.admin'),
'brand' => trans('enums.brand')
];
}
}

View file

@ -1,25 +0,0 @@
<?php
namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum;
enum RoleEnum: int
{
use BaseEnum;
case conceptor = 1;
case admin = 2;
case 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,47 +2,46 @@
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Session\TokenMismatchException;
use Kwaadpepper\ExceptionHandler\Exceptions\ExceptionHandler;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of exception types with their corresponding custom log levels.
*
* @var array<class-string<\Throwable>, \Psr\Log\LogLevel::*>
*/
protected $levels = [];
/**
* A list of the exception types that are not reported.
*
* @var array<int, class-string<\Throwable>>
* @var array
*/
protected $dontReport = [];
protected $dontReport = [
RedirectTo::class
];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
* A list of the inputs that are never flashed for validation exceptions.
*
* @var array<int, string>
* @var array
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
* Report or log an exception.
*
* @param \Throwable $e
* @return void
*
* @throws \Exception .
*/
public function register()
public function report(Throwable $e)
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
if (\get_class($e) === TokenMismatchException::class) {
abort(\request()->routeIs('bo.*') ?
response()->redirectToRoute('bo.dashboard') :
response()->redirectToRoute('home'));
}
parent::report($e);
}
}

View file

@ -1,71 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Models\ActivityLog;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
class ActivityLogController extends Controller
{
use ChangesModelOrder;
use UpdateModelPublished;
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
$this->authorize('isConceptor');
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = ActivityLog::query()->with('user');
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'model_class',
);
}
$searchFields = \implode(', ', [
trans('validation.custom.target'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$activitylogModels = $this->paginate($query);
return view('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

@ -1,72 +0,0 @@
<?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

@ -1,33 +0,0 @@
<?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,160 +3,55 @@
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\Http\Requests\SaveBannedIpListRequest;
use App\Models\BannedIp;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class BannedIpController extends Controller
{
/**
* Create the controller instance.
* Show banned ips list
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function __construct()
public function index()
{
$this->authorizeResource(BannedIp::class);
$ips = BannedIp::all();
return view('bo.banned-ip.index', compact('ips'));
}
/**
* Display a listing of the resource.
* Ajax save data banned ips
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @param SaveBannedIpListRequest $request
* @return array
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function saveDatas(SaveBannedIpListRequest $request)
{
/** @var \Illuminate\Database\Eloquent\Builder $bannedipModels */
$bannedipModels = BannedIp::query();
/** @var \Illuminate\Support\Collection $ips */
$ips = \collect($request->ips);
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$bannedipModels,
$search,
null,
'ip',
);
}
$searchFields = trans('validation.custom.ip');
// * Delete all
BannedIp::query()->delete();
/** 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'));
}
/**
* 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
{
return view('back::pages.banned-ips.create', [
'bannedipModel' => $banned_ip,
]);
}
/**
* 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'))]
));
$ips->map(function (string $ip) {
(new BannedIp(['ip' => $ip]))->saveOrFail();
});
return \response()->json(BannedIp::all());
}
/**
* Show the form for editing the specified resource.
* Ajax destroy ip
*
* @param \App\Models\BannedIp $banned_ip
* @return \Illuminate\Contracts\View\View
* @param Request $request
* @return string
*/
public function edit(BannedIp $banned_ip): \Illuminate\Contracts\View\View
public function destroy(Request $request)
{
return view('back::pages.banned-ips.edit', [
'bannedipModel' => $banned_ip,
]);
}
$ip = BannedIp::where("ip", $request->ip);
$ip->delete();
/**
* 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'))]
));
});
return "Suppression terminée !";
}
}

View file

@ -1,135 +0,0 @@
<?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

@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\CodeSearchRequest;
use App\Http\Requests\Bo\ExportCodesRequest;
use App\Http\Requests\Bo\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

@ -0,0 +1,49 @@
<?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

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\UpdateContentRequest;
use App\Models\Content;
class ContentController extends Controller
{
/**
* Manage content
*
* @param Content $content
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
*/
public function manage(Content $content)
{
return view('bo.content.manage', compact('content'));
}
/**
* Content update
*
* @param UpdateContentRequest $request
* @param Content $content
* @return \Illuminate\Http\RedirectResponse
*/
public function manageSubmit(UpdateContentRequest $request, Content $content)
{
$content->fill($request->validated());
if ($content->saveOrFail()) {
return redirect()->route('bo.contents.manage', $content)
->with('success', 'Enregistré !');
}
return back();
}
}

View file

@ -6,77 +6,72 @@ 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
{
/**
* Dashboard Page
* Show dashboard page
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index(): \Illuminate\Contracts\View\View
public function index()
{
// * 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;
};
// Pour les stats.
$settings = app(Settings::class);
$participations = Participation::selectRaw("COUNT(*) AS count, DATE(created_at) AS date")
->groupByRaw("DATE(created_at)")->get();
//phpcs:ignore Generic.Files.LineLength.TooLong
$participationsUniquePerDay = Participation::selectRaw("DISTINCT customer_id AS customer, COUNT(*) AS count, DATE(created_at) AS date")
->groupByRaw("DATE(created_at), customer")->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 customer_id AS customer, COUNT(*) AS count")
->groupBy("customer")->orderBy("customer")->get();
/** @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);
// Données calculées à afficher.
$totalParticipation = Participation::count();
if (count($participationsUnique) > 0) {
$avgParticipCustomer = round($totalParticipation / count($participationsUnique), 1);
} else {
$avgParticipCustomer = 0;
}
// * 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));
// Tableau des dates comprises dans la table participation.
foreach ($participations as $participation) {
$datesParticipations[Carbon::parse($participation->date)->format('d/m/Y')] = $participation->count;
}
return view('back::pages.dashboard.index', compact(
// 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(
'dates',
'count',
'countUnique',
'participationsTotal',
'avgParticipUser',
'totalParticipation',
'avgParticipCustomer',
'codesEan',
'settings'
));

View file

@ -1,180 +0,0 @@
<?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

@ -0,0 +1,59 @@
<?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

@ -1,170 +0,0 @@
<?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

@ -0,0 +1,51 @@
<?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

@ -0,0 +1,47 @@
<?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

@ -3,180 +3,152 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Resources\InstantWinResource;
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\Carbon;
use Illuminate\Support\Facades\DB;
class InstantWinController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(InstantWin::class);
}
/**
* Display a listing of the resource.
* View Instant gagnants page
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index(): \Illuminate\Contracts\View\View
public function index()
{
$instantWinModelList = InstantWin::query()->orderBy('available_at')->get();
// * Can generate only if there are none already generate and prizes on database
$canGenerate = InstantWin::count() ? false : (Prize::sum('quantity') ? true : false);
return view('back::pages.instant-win.index', [
'instantWinModelList' => $instantWinModelList
$instantWin = InstantWin::orderBy('appear_date')->get();
return view('bo.instant-win.index', [
'can_generate' => $canGenerate,
'instant_win' => new InstantWinResource($instantWin)
]);
}
/**
* Returns generated instant win.
* Generate Instant wins
*
* @return \Illuminate\Http\JsonResponse
*/
public function generate(): JsonResponse
public function instantWinGenerate(): JsonResponse
{
$this->authorize('generate', InstantWin::class);
/** @var \App\Models\Settings $settings */
$settings = app(Settings::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 \Illuminate\Support\Carbon $start_date */
$start_date = $settings->start_date;
/** @var \Illuminate\Support\Carbon $end_date */
$end_date = $settings->end_date;
/** @var \App\Models\Settings */
$settings = app(Settings::class);
$prizeModelList = Prize::all();
$lots = Prize::orderBy('id')->get();
/** @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();
/** @var \Illuminate\Support\Collection $daterange all days of the game period */
$daterange = collect($start_date->range($end_date)->toArray());
/** @var \Illuminate\Support\Collection $dateGlobal all randomized instant wins */
$dateGlobal = collect();
/** @var \Illuminate\Support\Collection $lotsGlobal All randomized lots */
$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);
}
});
$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);
// * Assign Lots on collection
foreach ($lots as $lot) {
for ($i = $lot->quantity; $i > 0; $i--) {
$lotsGlobal->push($lot);
}
}
// * 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();
});
$daysRunningGame = clone $daterange;
// * Create Instant wins on collection
for ($i = $lotsGlobal->count(); $i > 0; $i--) {
// * Get each day once then randomize
if ($daysRunningGame->count()) {
/** @var Carbon $dateTime */
$dateTime = $daysRunningGame->shift();
} else {
/** @var Carbon $dateTime */
$dateTime = $daterange->random();
}
// * If end of loop and there are less lots then days
if ($i === 1 and $lotsGlobal->count() < $daysRunningGame->count()) {
// * Make the last instant win on the last day
/** @var Carbon $dateTime */
$dateTime = $daterange->random();
}
$dateGlobal->push(Carbon::parse(sprintf(
'%s %s',
$dateTime->format('Y-m-d'),
$this->getRandomWiningHour()
)));
}//end for
// * Sort dates
$dateGlobal = $dateGlobal->sortBy(function (Carbon $date) {
return $date->getTimestamp();
});
// * Reserve big prize (the first should be ID 1) and last Instant win for the end
/** @var \App\Models\Prize $bigPrize */
$bigPrize = $lotsGlobal->shift();
/** @var Carbon $lastInstantWin */
$lastInstantWin = $dateGlobal->pop();
// * Shuffle Instant wins
// ! Initialize mt rand seed !!
\mt_srand();
$lotsGlobal = $lotsGlobal->shuffle(\mt_rand(0, 99999));
// * Shuffle Lots
// ! Initialize mt rand seed !!
\mt_srand();
$dateGlobal = $dateGlobal->shuffle(\mt_rand(0, 99999));
// ! Return empty if no prizes are available
if (!$bigPrize) {
return \response()->json();
}
$instantWinsLots = $dateGlobal->map(function (Carbon $instantWinDate) use ($lotsGlobal) {
return [
'date' => $instantWinDate,
'lot' => $lotsGlobal->shift()
];
});
// * Save Instant wins
DB::transaction(function () use ($instantWinsLots, $lastInstantWin, $bigPrize) {
$instantWinsLots->map(function (array $instantWinLot) {
$instantWin = new InstantWin();
$instantWin->appear_date = $instantWinLot['date'];
$instantWin->prize()->associate($instantWinLot['lot']);
$instantWin->saveOrFail();
});
// * Output instant wins to ajax response
return response()->json(InstantWin::all());
// * Insert last Instant win (big prize)
$instantWin = new InstantWin();
$instantWin->appear_date = $lastInstantWin;
$instantWin->prize()->associate($bigPrize);
$instantWin->saveOrFail();
});
return \response()->json(new InstantWinResource(InstantWin::orderBy('appear_date')->get()));
}
/**
* Generate instant win.
* Generate a random hour between 06:00 and 22:59
*
* 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
* @return string
*/
private function generateArray(Collection $lotsGlobal, Collection $dateGlobal, int $prizesDone): Collection
private function getRandomWiningHour(): string
{
// 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);
}
// * 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;
// ! Initialize mt rand seed !!
\mt_srand();
return Carbon::parse('22:59')
->subMinutes(\mt_rand(0, (23 - 6) * 60 - 1))->format('H:i');
}
}
/**
* @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

@ -1,88 +0,0 @@
<?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

@ -1,176 +0,0 @@
<?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

@ -0,0 +1,94 @@
<?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->label_one = $prize->label_one;
$p->label_many = $prize->label_many;
$p->quantity = $prize->quantity;
$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->label_one = $prize->label_one;
$p->label_many = $prize->label_many;
$p->quantity = $prize->quantity;
$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

@ -1,190 +0,0 @@
<?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

@ -0,0 +1,154 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\StoreProductRequest;
use App\Http\Requests\Bo\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\Bo\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\Bo\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,176 +3,135 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Ranges\StoreRangeRequest;
use App\Http\Requests\Bo\Ranges\UpdateRangeRequest;
use App\Http\Requests\Bo\StoreRangeRequest;
use App\Http\Requests\Bo\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.
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function __construct()
public function index()
{
$this->authorizeResource(Range::class);
$ranges = Range::paginate();
return view('bo.ranges.index', compact('ranges'));
}
/**
* Display a listing of the resource.
* Show the form for creating a new resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function index(Request $request): \Illuminate\Contracts\View\View
public function create()
{
/** @var \Illuminate\Database\Eloquent\Builder $rangeModels */
$rangeModels = Range::query();
return view('bo.ranges.create');
}
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$rangeModels,
$search,
null,
'name',
);
/**
* 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();
}
$searchFields = Str::ucfirst(__('validation.attributes.name'));
/** Sort columns with a query */
$this->sortQuery($rangeModels);
/** Custom pagination */
$rangeModels = $this->paginate($rangeModels);
return view('back::pages.ranges.index', compact('rangeModels', 'search', 'searchFields'));
return \response()->redirectToRoute('bo.ranges.index')
->with('success', 'La gamme à bien été enregistré !');
}
/**
* Display the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function show(Range $range): \Illuminate\Contracts\View\View
public function show(Range $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'))]
));
});
return view('bo.ranges.view', compact('range'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Range $range
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Http\Response
*/
public function edit(Range $range): \Illuminate\Contracts\View\View
public function edit(Range $range)
{
return view('back::pages.ranges.edit', [
'rangeModel' => $range
]);
return view('bo.ranges.edit', compact('range'));
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Ranges\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
* @param \App\Http\Requests\UpdateRangeRequest $request
* @param \App\Models\Range $range
* @return \Illuminate\Http\Response
*/
public function update(UpdateRangeRequest $request, Range $range): \Illuminate\Http\RedirectResponse
public function update(UpdateRangeRequest $request, Range $range)
{
return DB::transaction(function () use ($request, $range) {
$range->fill($request->validated());
if ($range->saveOrFail()) {
return redirect()
->route('bo.ranges.edit', $range)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(__('models.classes.range'))]
));
$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 redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_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 !');
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Range $range
* @return \Illuminate\Http\RedirectResponse
* @return \Illuminate\Http\Response
*/
public function destroy(Range $range): \Illuminate\Http\RedirectResponse
public function destroy(Range $range)
{
if ($range->products()->count()) {
return back()->withError(
'Cette gamme est associé a des produits, la supression n\'est pas possible'
);
}
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'))]
));
});
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 !');
}
}

View file

@ -1,269 +0,0 @@
<?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,101 +3,52 @@
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\Http\Requests\Bo\UpdateSettingsRequest;
use App\Models\Settings;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Session;
class SettingsController extends Controller
{
/**
* Create the controller instance.
* Show edit settings page
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function __construct()
public function manage()
{
$this->authorizeResource(Settings::class);
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
return view('bo.settings.manage', compact('settings'));
}
/**
* Show the form for editing the specified resource.
* Save settings
*
* @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
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Settings\UpdateSettingsRequest $request
* @param \App\Models\Settings $settings
* @param UpdateSettingsRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateSettingsRequest $request, Settings $settings): \Illuminate\Http\RedirectResponse
public function manageSubmit(UpdateSettingsRequest $request)
{
return DB::transaction(function () use ($request, $settings) {
$data = $request->validated();
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
// * 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($request->validated());
$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'))]
));
});
}
/**
* 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()
);
$redirectClose = $request->redirect_close;
// Check du lien de redirection après fermeture.
if (strpos($redirectClose, 'https://') === false && strpos($redirectClose, 'http://') === false) {
$redirectClose = 'https://' . $redirectClose;
}
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;
$settings->redirect_close = $redirectClose;
if ($settings->saveOrFail()) {
$settingsGlobal = app(Settings::class)
->select('name', 'company', 'analytics', 'start_date', 'end_date')->first();
Cache::put('settingsGlobal', $settingsGlobal);
Session::flash('success', 'Modifications enregistrés !');
}
return \back();
}
}

View file

@ -1,91 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\StaticPages\UpdateStaticPageRequest;
use App\Models\StaticPage;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class StaticPageController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(StaticPage::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $query */
$query = StaticPage::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$query,
$search,
null,
'title',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.title'),
]);
/** Sort columns with a query */
$this->sortQuery($query);
/** Custom pagination */
$staticPageModelList = $this->paginate($query);
return view('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

@ -1,169 +0,0 @@
<?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

@ -0,0 +1,65 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\UpdateWinInfosRequest;
use App\Models\InstantWin;
use App\Models\Participation;
use App\Models\Settings;
use Illuminate\Support\Facades\Cache;
class WinnersListController extends Controller
{
/**
* Show Instant win list
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
$participations = Participation::whereHas('instantWin')->with('code')
->with('instantWin.prize')->with('customer')->with('codeEan')->get();
$settings = Settings::select('use_ean_codes', 'winning_proof')->first();
// * Generate code to show up in front + format date + customer info
$participations = $participations->map(function (Participation $participation) {
$participation->created = $participation->created_at->format('d/m/Y à H:i');
$participation->code->code = $participation->code->code_unique;
$participation->code->code = $participation->code->code_unique;
$participation->customer->setRawAttributes(['data' => Cache::remember(
'winnerList' . $participation->customer->id,
90,
function () use ($participation) {
/** @var \LDC\LDCCRM\Models\Customer|null $crmCustomer */
$crmCustomer = $participation->customer->apiData;
$data = [];
if ($crmCustomer) {
$countryLabel = $crmCustomer->country->label;
$data = collect($crmCustomer)->all();
$data['country'] = $countryLabel;
}
return $data;
}
)]);
return $participation;
});
return view('bo.winners-list.index', compact('participations', 'settings'));
}
/**
* Update Instant win info
*
* @param UpdateWinInfosRequest $request
* @param InstantWin $instantWin
* @return \Illuminate\Http\JsonResponse
*/
public function updateInstantWinInfos(UpdateWinInfosRequest $request, InstantWin $instantWin)
{
$instantWin->done = $request->done;
$instantWin->comment = $request->comment;
if ($instantWin->save()) {
return response()->json();
}
return response()->json([], 500);
}
}

View file

@ -2,267 +2,14 @@
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,46 +0,0 @@
<?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

@ -1,85 +0,0 @@
<?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

@ -1,107 +0,0 @@
<?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

@ -1,157 +0,0 @@
<?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

@ -1,122 +0,0 @@
<?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

@ -1,148 +0,0 @@
<?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

@ -3,173 +3,52 @@
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 App\Http\Requests\Fo\CompleteInfosRequest;
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;
use LDC\LDCCRM\Exceptions\ValidationException;
class CustomerController extends Controller
{
/** @var \App\Repositories\Fo\CrmRepository $crmRepository */
private $crmRepository;
/**
* ParticipateController
* Show customer complete form
*
* @return void
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function __construct()
public function completeInfos()
{
$this->crmRepository = new CrmRepository();
/** @var \App\Models\Customer $customer */
$customer = auth('customer')->user();
return view('front.basic.complete-infos', ['customer' => $customer]);
}
/**
* Verify profil info form
* Update account info
*
* @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
* @param CompleteInfosRequest $request
* @return \Illuminate\Http\RedirectResponse
* @throws IlluminateValidationException In case of any error validation or within the crm.
*/
public function profilVerifySubmit(VerifyInformationsRequest $request): RedirectResponse
public function completeInfosSubmit(CompleteInfosRequest $request)
{
// ! Pas de Gate/Policies Ici volontairement, vu avec Vincent
/** @var \App\Models\Customer $customer */
$customer = auth('customer')->user();
try {
return DB::transaction(function () use ($request) {
// * --- FILL AND SAVE CUSTOMER
/** @var \App\Models\Customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
// * 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'));
$crmCustomer->fill($request->validated());
$crmCustomer->save();
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')
]);
Session::remove('needCrmInfoUpdate');
return redirect()->route('saisir-code');
} catch (ApiErrorException | ValidationException $e) {
return back()->withErrors($e->getMessage());
} catch (\Exception | \TypeError | CrmException $e) {
\report($e);
} //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);
}
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
}
}

View file

@ -1,90 +0,0 @@
<?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,29 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\Settings;
class EndPageController extends Controller
{
/**
* Ended game page
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
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 &&
$settings->close_page_active_date > now()
) {
return view('front.basic.end', compact('settings'));
}
return redirect()->route('home');
}
}

View file

@ -0,0 +1,301 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\ParticipateRequest;
use App\Mail\NotifAdmin;
use App\Mail\NotifWinAdmin;
use App\Mail\PrizeWon;
use App\Mail\ReductionWon;
use App\Models\BannedIp;
use App\Models\Customer;
use App\Models\InstantWin;
use App\Models\Participation;
use App\Models\Reduction;
use LDCWeb\MesBR;
use App\Models\Settings;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class EnterCodeController extends Controller
{
/**
* Show enter code form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
/** @var \App\Models\Customer|null $customer */
$customer = auth('customer')->user();
// * Only auth customer can access
if (!auth('customer')->check()) {
return redirect()->route('home');
}
$nbParticipations = Participation::whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->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)
{
try {
return DB::transaction(function () use ($request) {
// Récupération des données à utilisées.
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
/** @var \App\Models\Customer $customer */
$customer = auth('customer')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
// * Cas IP bannie.
if (BannedIp::where('ip', app('clientIp'))->exists()) {
return view('front.basic.participation-exhausted', compact('settings'));
}
// * Cas nombre de participation maximale atteint.
$nbParticipations = Participation::whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->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->code_unique_id = $request->code->id;
$participation->code_ean_id = $request->code_ean ? $request->code_ean->id : null;
$participation->ip = app('clientIp');
$participation->customer()->associate($customer);
$participation->saveOrFail();
// * Gestion des instants gagnants.
$userAlreadyWon = Participation::has('instantWin')
->whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->id);
})->first();
// * Si l'utilisateur actuel à déjà gagné un gros lot, pas d'instant gagnant.
if ($userAlreadyWon) {
return $this->serveCouponWin($settings, $participation, $crmCustomer);
}
/** @var \App\Models\InstantWin|null */
$instantWin = InstantWin::where('appear_date', '<', now())
->whereDoesntHave('participation')->orderBy('appear_date', 'ASC')
->lockForUpdate()->first();
if ($instantWin) {
// Ici si fail car unique dupliqué catch.
try {
// Gain d'un gros lot.
return $this->instantWin(
$settings,
$participation,
$crmCustomer,
$instantWin
);
} catch (\ErrorException $e) {
report($e);
// Unbind instant gagnant.
$participation->instantWin()->deleteOrFail();
} //end try
}
return $this->serveCouponWin(
$settings,
$participation,
$crmCustomer
);
});
} catch (\ErrorException $e) {
report($e);
return back()
->withErrors('Une erreur est survenue, merci de réessayez ultérieurement.');
}//end try
}
/**
* Handle instant wins
*
* @param Settings $settings
* @param Participation $participation
* @param CrmCustomer $crmCustomer
* @param InstantWin $instantWin
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
private function instantWin(
Settings $settings,
Participation &$participation,
CrmCustomer $crmCustomer,
InstantWin $instantWin
) {
$participation->instantWin()->save($instantWin);
Mail::to($crmCustomer->email)->send(new PrizeWon($crmCustomer, $instantWin->prize, $settings));
$mails = collect($settings->notification_emails);
// ? Is settings.notification_emails empty ?
if (!$mails->count()) {
// ! Send to null (will send to MAIL_ADMIN and print warning in mail object)
$mails = [null];
}
foreach ($mails as $mail) {
Mail::to($mail)->send(new NotifWinAdmin(
$crmCustomer,
$settings,
$participation
));
}
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
* @param CrmCustomer $crmCustomer
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
* @throws \ErrorException If no web coupons are available.
*/
private function serveCouponWin(Settings $settings, Participation $participation, CrmCustomer $crmCustomer)
{
// Si le jeu est 100% gagnant.
if ($settings->reductions_activated) {
if (!($coupon = $this->getRandomCoupon($settings))) {
throw new \ErrorException('No web coupons are available');
}
$reduction = $this->updateCoupon(
$settings,
$coupon['web_coupon_identifiant'],
$participation,
$crmCustomer
);
if (!$reduction) {
$participation->deleteOrFail();
abort(back()->withErrors('Une erreur est survenue, merci de réessayez ultérieurement.'));
}
Mail::to($crmCustomer->email)
->send(new ReductionWon($crmCustomer, $coupon['face_value'], $settings, $reduction));
return view('front.basic.reduction', compact('crmCoupon', 'coupon'));
} else {
return view('front.basic.loose');
}
}
/**
* Get a random coupon
*
* @param Settings $settings
* @return array|null
*/
private function getRandomCoupon(Settings $settings): ?array
{
$webCoupons = $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] ?? null;
}
}
/**
* Update a coupon
*
* FIXME: When mes BR will be updated.
*
* @param Settings $settings
* @param integer $couponId
* @param Participation $participation
* @param CrmCustomer $crmCustomer
* @return \App\Models\Reduction|null
*/
private function updateCoupon(
Settings $settings,
int $couponId,
Participation $participation,
CrmCustomer $crmCustomer
) {
$reduction = null;
$mesBR = new MesBR(config('mesbr.endpoint'), $settings->reductions_site, $settings->reductions_campaign);
if ($crmUser->status === 'OK') {
$mesBR->updateContact($crmUser->user);
$result = $mesBR->updatePanier($crmUser->user, $couponId);
if ($result["status"] === "OK") {
$reduction = new Reduction();
$reduction->link = $result["link"];
$reduction->verification = $result["verif"];
$reduction->basket_id = $result["basketId"];
$reduction->saveOrFail();
$participation->reduction_id = $reduction->id;
$participation->saveOrFail();
} 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;
}
/**
* Show Wining proof form
*
* @return \Illuminate\Http\Response
*/
public function formWinningProof()
{
/** @var \App\Models\Customer|null $customer */
$customer = auth('customer')->user();
// * Only auth customer can access
if (!auth('customer')->check() or \get_class($customer) === Customer::class) {
return redirect()->route('home');
}
$participation = Participation::whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->id);
})->whereHas('instantWin')->where([
['sticker', null],
['ticket', null],
['barcode', null]
])->first();
if ($participation) {
return view('front.basic.winning-proof', compact('participation'));
}
return redirect()->route('home');
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\FaqQuestion;
use App\Models\FaqTheme;
class FAQController extends Controller
{
/**
* Faq page
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
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,21 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
class GainsController extends Controller
{
/**
* Gains page
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
if (view()->exists('front.basic.gains')) {
return view('front.basic.gains');
}
return redirect()->route('home');
}
}

View file

@ -3,93 +3,16 @@
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
* Home page
*
* @return \Illuminate\Contracts\View\View
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function home(): \Illuminate\Contracts\View\View
public function home()
{
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');
return view('front.basic.home');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
class HowToPlayController extends Controller
{
/**
* How to play page
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
if (view()->exists('front.basic.how-to-play')) {
return view('front.basic.how-to-play');
}
return redirect()->route('home');
}
}

View file

@ -0,0 +1,443 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\AuthenticateRequest;
use App\Http\Requests\Fo\RegisterRequest;
use App\Http\Requests\Fo\UpdatePasswordRequest;
use App\Http\Requests\Fo\ForgotPasswordRequest;
use App\Lib\ToolboxHelper;
use App\Mail\PasswordReset;
use App\Mail\VerifyEmail;
use App\Models\Customer;
use App\Models\Settings;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Validator;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class LoginController extends Controller
{
/**
* Show login form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function loginForm()
{
if (!ToolboxHelper::isLoginAccessAllowed()) {
abort(\response()->redirectToRoute('home'));
}
return view('front.basic.login');
}
/**
* Authenticate user
*
* @param AuthenticateRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function loginFormSubmit(AuthenticateRequest $request)
{
$this->authenticateUser($request);
$this->authenticateCustomer($request);
return back()->withInput($request->except('password'))
->withErrors('Les identifiants entrés sont incorrects.');
}
/**
* Log out
*
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
public function logout()
{
auth('web')->logout();
auth('customer')->logout();
return redirect()->route('home');
}
/**
* Confirm Email
*
* @param mixed $verifyToken
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function confirmEmail($verifyToken)
{
$result = false;
/** @var \App\Models\Customer $customer */
if (!($customer = Customer::select('hashcode')->where('email_verify_token', $verifyToken)->first())) {
return view('front.basic.confirm-email', compact('result'));
}
try {
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
if ($crmCustomer->activate()) {
$result = true;
}
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerAlreadyActive())) {
return view('front.auth.activate')
->with(
'message',
'Votre compte est déjà activé, vous pouvez dès à présent vous connecter avec celui-ci.'
);
}
// ! Une erreur critique est survenue avec la CRM.
report($e);
} catch (CrmException $e) {
// ! Une erreur critique est survenue avec la CRM.
report($e);
} //end try
return view('front.basic.confirm-email', compact('result'));
}
/**
* Forgot Password form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function forgotPassword()
{
return view('front.basic.forgot-password');
}
/**
* forgotPassword submit
*
* @param ForgotPasswordRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function forgotPasswordSubmit(ForgotPasswordRequest $request)
{
if (!($crmCustomer = $this->getCustomerFromCrm($request->email))) {
return back()->withInput($request->only('email'))
->withErrors('Le mail que vous avez entré n\'est pas enregistré dans nos services.');
}
/** @var \App\Models\Customer $customer */
$customer = $this->createLocalCustomer($crmCustomer);
$customer->password_token = Str::uuid();
$customer->saveOrFail();
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
Mail::to($request->email)->send(new PasswordReset($customer, $settings));
return redirect()->route('login')
->with('success', 'Un mail de réinitialisation de votre mot de passe vous a été envoyé(e).');
}
/**
* Forgot password form show
*
* @param Request $request
* @param mixed $token
* @return \Illuminate\Http\RedirectResponse
*/
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. ');
}
if (!Customer::where('password_token', $token)->exists()) {
return redirect()->route('forgot-password', ['tokenValid' => false])
->withErrors('Le lien de changement du mot de passe est incorrect.');
}
return view('front.basic.forgot-password', [
'tokenValid' => true,
'token' => $token
]);
}
/**
* Change password form submit
*
* @param UpdatePasswordRequest $request
* @param mixed $token
* @return \Illuminate\Http\RedirectResponse
*/
public function forgotPasswordResetSubmit(UpdatePasswordRequest $request, $token)
{
/** @var \App\Models\Customer $customer */
if (!$request->hasValidSignature(false) or !($customer = Customer::where('password_token', $token)->first())) {
return redirect()->route('forgot-password', ['tokenValid' => false])
->withErrors('Le lien de changement du mot de passe est incorrect.');
}
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
try {
// Ne devrait pas survenir, $crm->login throws ApiError::authFailed() .
if ($crm->login($customer->email, $request->password)) {
// Mauvais identifiants.
return back()->withErrors('Vous ne pouvez pas utiliser un de vos anciens mot de passe.');
}
// phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
} catch (ApiErrorException $e) {
// * Continue
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM.
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
}
try {
if (!($crmCustomer = $crm->getCustomer($customer->hashcode))) {
return back()->withErrors('Changement impossible, compte inexistant.');
}
$crmCustomer->password = $request->password;
$crmCustomer->save();
$customer->password_token = null;
$customer->saveOrFail();
return redirect()->route('login')
->with('success', 'Vous pouvez désormais vous connecter avec votre nouveau mot de passe.');
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} catch (ValidationException $e) {
return back()->withErrors(sprintf('Nouveau mot de passe : %s', $e->getMessage()));
}
}
/**
* Try Login User if exists (email)
*
* Does nothing if email does not exists in users table.
*
* @param AuthenticateRequest $request
* @return void
* @throws \Symfony\Component\HttpKernel\Exception\HttpException If user is logged in.
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException If redirect route is not found.
*/
private function authenticateUser(AuthenticateRequest $request)
{
if (
User::whereEmail($request->email)->exists() and
\auth('web')->attempt([
'email' => $request->email,
'password' => $request->password
], $request->remember)
) {
abort(redirect()->route('bo.dashboard'));
}
}
/**
* Try Login Customer if exists (email)
*
* Does nothing if email does not exists in customers table.
*
* @param AuthenticateRequest $request
* @return void
* @throws \Symfony\Component\HttpKernel\Exception\HttpException If user is logged in.
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException If redirect route is not found.
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.Missing
*/
private function authenticateCustomer(AuthenticateRequest $request)
{
/** @var \App\Models\Customer|null $customerLocal */
$customer = Customer::where('email', $request->email)->first();
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
try {
$crm->login($request->email, $request->password);
if (!($crmCustomer = $this->getCustomerFromCrm($request->email))) {
abort(back()->withInput($request->except('password'))
->withErrors('Une erreur est survenue veuillez réssayer plus tard.'));
}
// ? Test is user data valid ?
try {
/**
* On va tester de mettre a jour uniquement les champs
* requis pour mes br afin de detecter si ils doivent etre mis à jour
* la validation va se faire par la crm (ou le client).
*/
$crmCustomer->fill([
'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address,
'postcode' => $crmCustomer->postcode,
'country' => $crmCustomer->country,
'city' => $crmCustomer->city
]);
// * On valide les données selon les critères de registerController
$rules = collect((new RegisterRequest())->rules());
$validator = Validator::make(
[
'birth_date' => $crmCustomer->birth_date,
'address' => $crmCustomer->address,
'postcode' => $crmCustomer->postcode,
'country' => $crmCustomer->country->value,
'city' => $crmCustomer->city
],
[
'birth_date' => $rules->get('birth_date'),
'address' => $rules->get('address'),
'postcode' => $rules->get('postcode'),
'country' => $rules->get('country'),
'city' => $rules->get('city')
]
);
if ($validator->fails()) {
throw new ValidationException();
} else {
Session::remove('needCrmInfoUpdate', true);
}
} catch (ValidationException $e) {
Session::push('needCrmInfoUpdate', true);
} catch (ApiErrorException $e) {
if (!$e->getApiError()->equals(ApiError::validationFailed())) {
throw $e;
}
Session::push('needCrmInfoUpdate', true);
} //end try
// * Si pas de compte local mais existe en CRM.
if (!$customer) {
$customer = $this->createLocalCustomer($crmCustomer);
}
abort($this->processLogin($customer, $request->remember));
} catch (ApiErrorException $e) {
abort($this->handleLoginApiErrorException($e, $request));
} catch (CrmException $e) {
\report($e);
abort(back()->withInput($request->except('password'))
->withErrors('La connexion est impossible pour le moment, veuillez réessayer plus tard.'));
} //end try
}
/**
* Handle Api error exception
*
* @param ApiErrorException $e
* @param AuthenticateRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
private function handleLoginApiErrorException(ApiErrorException $e, AuthenticateRequest $request)
{
/** @var Customers|null */
$customer = Customer::where('email', $request->email)->first();
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
switch ($e->getApiError()) {
case ApiError::authFailed():
// * Mauvais identifiants
return back()->withInput($request->except('password'))
->withErrors('Email ou mot de passe incorrect.');
case ApiError::customerNotActivated():
// * Le compte n'est pas activé
try {
if (!($crmCustomer = $crm->getCustomer($request->email))) {
Log::critical(sprintf('Customer %s could not be retrieved from crm', $request->email));
return back()->withErrors(
__('Une erreur est survenue, merci de réessayer ultérieurement.')
);
}
if (!$customer) {
$customer = $this->createLocalCustomer($crmCustomer);
}
$tokenMail = Str::uuid();
Mail::to($request->email)->send(new VerifyEmail($crmCustomer, $tokenMail));
$customer->email_verify_token = $tokenMail;
$customer->saveOrFail();
return back()
->withErrors(
__('Votre compte n\'est pas activé, vérifiez dans vos emails pour y trouver
le mail de confirmation. Un lien d\'activation vous a été renvoyé.')
);
} catch (CrmException | ApiErrorException $e) {
report($e);
// ! Une erreur critique est survenue avec la CRM
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} //end try
default:
report($e);
// ! Une erreur non gérée
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} //end switch
}
/**
* Get Local customer using email and update info from crm.
*
* @param string $email
* @return CrmCustomer|null
*/
private function getCustomerFromCrm(string $email): ?CrmCustomer
{
try {
return CrmCustomer::findWithEmail($email);
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::modelNotFound())) {
return null;
}
abort(back()->withErrors($e->getMessage()));
} catch (CrmException $e) {
report($e);
abort(back()->withErrors('Une erreur est survenue'));
}
}
/**
* Create a local customer
*
* @param CrmCustomer $crmCustomer
* @return Customer
*/
private function createLocalCustomer(CrmCustomer $crmCustomer): Customer
{
/** @var \App\Models\Customer $customer */
$customer = Customer::firstOrNew([
'email' => $crmCustomer->email
]);
$customer->hashcode = $crmCustomer->hashcode;
$customer->saveOrFail();
return $customer;
}
/**
* Login a customer
*
* @param Customer $customer
* @param boolean $remember
* @return \Illuminate\Routing\Redirector|\Illuminate\Http\RedirectResponse
*/
private function processLogin(Customer $customer, bool $remember)
{
/** @var \Illuminate\Auth\SessionGuard */
$guard = auth('customer');
$guard->login($customer, $remember);
$url = Session::pull('url.intended');
Session::forget('url.intended');
Session::save();
return $url ? redirect()->to($url) : response()->redirectToRoute('saisir-code');
}
}

View file

@ -1,308 +0,0 @@
<?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,23 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\Range;
class ProduitsController extends Controller
{
/**
* Produits porteurs pages
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
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

@ -0,0 +1,104 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\RegisterRequest;
use App\Mail\VerifyEmail;
use App\Models\Customer;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class RegisterController extends Controller
{
/**
* Register form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function register()
{
return view('front.basic.register-crm');
}
/**
* Register form submit
*
* @param RegisterRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function registerSubmit(RegisterRequest $request)
{
if (!Carbon::parse($request->birth_date)->lessThanOrEqualTo(Carbon::now()->subYears(18))) {
return back()
->withInput($request->except('password'))
->withErrors("Vous devez être majeur pour vous inscrire au jeu.");
}
$crmCustomer = null;
try {
$crmCustomer = new CrmCustomer(\array_intersect_key(
$request->validated(),
[
'firstname' => '',
'lastname' => '',
'birth_date' => '',
'address' => '',
'postcode' => '',
'country' => '',
'city' => '',
'phone' => '',
'email' => '',
'password' => '',
'opt_in' => '',
]
));
$crmCustomer->password = $request->password;
$crmCustomer->save();
$tokenMail = Str::uuid();
Mail::to($request->email)->send(new VerifyEmail($crmCustomer, $tokenMail));
$customer = new Customer();
$customer->email = $crmCustomer->email;
$customer->hashcode = $crmCustomer->hashcode;
$customer->email_verify_token = $tokenMail;
$customer->saveOrFail();
return redirect()->route('login')
->with('success', sprintf(
'%s %s %s %s %s',
'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>',
//phpcs:ignore Generic.Files.LineLength.TooLong
'- L\'activation de votre compte doit se faire dans les 24 heures qui suivent la demande de création.'
));
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerExists())) {
return back()->withErrors('Un compte est déjà associé à cette adresse email.');
}
if ($e->getApiError()->equals(ApiError::validationFailed())) {
return back()->withErrors($e->getMessage());
}
report($e);
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} catch (ValidationException $e) {
return back()->withErrors($e->getMessage());
} catch (\Exception | \TypeError | CrmException $e) {
\report($e);
// ! Une erreur critique est survenue avec la CRM.
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
} //end try
return back()->withErrors('Une erreur est survenue, merci de réessayer ultérieurement.');
}
}

View file

@ -1,57 +0,0 @@
<?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,24 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Models\Settings;
class TeaserController extends Controller
{
/**
* Teaser page
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function index()
{
$settings = app(Settings::class)
->select('start_date', 'end_date', 'activated_teaser')->first();
if ($settings->activated_teaser) {
return view('front.basic.teaser', compact('settings'));
}
return redirect()->route('home');
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\ContentPageType;
use App\Http\Controllers\Controller;
use App\Models\Content;
class ViewContentController extends Controller
{
/**
* Reglement page
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function reglement()
{
$content = Content::findOrFail(ContentPageType::reglement()->value);
return view('front.basic.reglement', compact('content'));
}
/**
* Charte page
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function charte()
{
$content = Content::findOrFail(ContentPageType::charte()->value);
return view('front.basic.charte', compact('content'));
}
/**
* Legals page
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function legals()
{
$content = Content::findOrFail(ContentPageType::legals()->value);
return view('front.basic.legals', compact('content'));
}
}

View file

@ -4,157 +4,113 @@ namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\SubmitWinningProofRequest;
use App\Mail\Won\WinningProofAdminMail;
use App\Mail\WinningProofMail;
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;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
class WinningProofController extends Controller
{
/**
* WinningProofController
* Submit Winning proof
*
* @return void
* @param SubmitWinningProofRequest $request
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function __construct()
public function sendWinningProof(SubmitWinningProofRequest $request)
{
$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'
]);
return DB::transaction(function () use ($request) {
/** @var \App\Models\Customer $customer */
$customer = auth('customer')->user();
/** @var \App\Models\Settings */
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
// On récupère la participation gagnante en attente des infos.
/** @var \App\Models\Participation|null $participation */
$participation = Participation::whereHas('customer', function ($q) use ($customer) {
$q->whereId($customer->id);
})->whereHas('instantWin')->where([
['sticker', null],
['ticket', null],
['barcode', null]
])->first();
if (!$participation) {
return redirect()->route('home');
}
/** @var \Illuminate\Http\UploadedFile $sticker */
$sticker = $request->sticker;
/** @var \Illuminate\Http\UploadedFile $ticket */
$ticket = $request->ticket;
/** @var \Illuminate\Http\UploadedFile $barcode */
$barcode = $request->barcode;
// * Mise à jour utilisateur dans la CRM.
try {
$crmCustomer->fill(\array_intersect_key($request->validated(), [
'firstname' => '',
'lastname' => '',
'address' => '',
'postcode' => '',
'country' => '',
'city' => '',
'phone' => '',
]));
$crmCustomer->save();
} catch (ApiErrorException | ValidationException $e) {
return back()->withErrors($e->getMessage());
} catch (\Exception | \TypeError | CrmException $e) {
\report($e);
} //end try
$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->saveOrFail();
/** @var \App\Models\Settings $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'
$mails = \collect($settings->notification_emails);
// ? Is settings.notification_emails empty ?
if (!$mails->count()) {
// ! Send to null (will send to MAIL_ADMIN and print warning in mail object)
$mails = [null];
}
foreach ($mails as $mail) {
Mail::to($mail)->send(new WinningProofMail(
$stickerPath,
$ticketPath,
$barcodePath,
$participation,
$crmCustomer,
$settings
));
}
return view('front.basic.replay');
});
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed)) {
throw IlluminateValidationException::withMessages([
'error' => $e->getMessage()
]);
}
} catch (\Exception $e) {
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
return back()->withErrors('Une erreur est survenue, merci de réessayez ultérieurement.');
}//end try
}
}

View file

@ -0,0 +1,70 @@
<?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')->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 = $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,13 +11,12 @@ class Kernel extends HttpKernel
*
* These middleware are run during every request to your application.
*
* @var array<int, string>
* @var array
*/
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Fruitcake\Cors\HandleCors::class,
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
@ -26,23 +25,20 @@ class Kernel extends HttpKernel
/**
* The application's route middleware groups.
*
* @var array<string, array<int, string>>
* @var array
*/
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' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
@ -52,20 +48,23 @@ class Kernel extends HttpKernel
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, string>
* @var array
*/
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::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,
'check-game-status' => \App\Http\Middleware\CheckGameStatus::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
'crmInfosMustBeComplete' => \App\Http\Middleware\MustCompleteCrmInfo::class
];
}

View file

@ -3,8 +3,6 @@
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Session;
class Authenticate extends Middleware
{
@ -14,26 +12,10 @@ class Authenticate extends Middleware
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo(Request $request): ?string
protected function redirectTo($request)
{
$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');
if (! $request->expectsJson()) {
return route('login');
}
return null;
}
}

View file

@ -0,0 +1,16 @@
<?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

@ -1,38 +0,0 @@
<?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

@ -1,48 +0,0 @@
<?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

@ -9,7 +9,8 @@ class EncryptCookies extends Middleware
/**
* The names of the cookies that should not be encrypted.
*
* @var array<int, string>
* @var array
*/
protected $except = [];
protected $except = [
];
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Http\Middleware;
use App\Models\Settings;
use Closure;
class GameCheckDate
{
/**
* Handle an incoming request.
*
* @param mixed $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) {
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 && $settings->close_page_active_date > now()) {
return redirect()->route('end');
}
return redirect($settings->redirect_close);
}
return $next($request);
}
}

View file

@ -1,61 +0,0 @@
<?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,25 @@
<?php
namespace App\Http\Middleware;
use Closure;
class IsAdmin
{
/**
* Handle an incoming request.
*
* @param mixed $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var \App\Models\User|\App\Models\Customer|null $user */
$user = auth('web')->user() ?? auth('customer')->user();
if ($user and $user->isLocal()) {
return $next($request);
}
abort(404);
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace App\Http\Middleware;
use Closure;
class IsSuperAdmin
{
/**
* Handle an incoming request.
*
* @param mixed $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var \App\Models\User|\App\Models\Customer|null $user */
$user = auth('web')->user() ?? auth('customer')->user();
if ($user and $user->isAdmin()) {
return $next($request);
}
return redirect()->route('bo.dashboard');
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Session;
class MustCompleteCrmInfo
{
/**
* Handle an incoming request.
*
* @param mixed $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/**
* IF session has need crm completion
* then redirect to completion page
*/
if (
Session::get('needCrmInfoUpdate') and
($request->route() and !\in_array($request->route()->getName(), ['completeInfos', 'completeInfosSubmit']))
) {
return response()->redirectToRoute('completeInfos');
}
return $next($request);
}
}

View file

@ -1,15 +0,0 @@
<?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,40 +2,24 @@
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @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
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null $guard
* @return mixed
*/
public function handle(Request $request, \Closure $next, ...$guards): Response
public function handle(Request $request, Closure $next, $guard = null)
{
// 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}` .");
}
}
if (Auth::guard($guard)->check()) {
return \redirect()->route('login');
}
return $next($request);

View file

@ -1,97 +0,0 @@
<?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

@ -9,10 +9,9 @@ class TrimStrings extends Middleware
/**
* The names of the attributes that should not be trimmed.
*
* @var array<int, string>
* @var array
*/
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<int, string|null>
* @return array
*/
public function hosts()
{

View file

@ -2,7 +2,7 @@
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Fideloper\Proxy\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
@ -10,19 +10,14 @@ class TrustProxies extends Middleware
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
* @var array|string|null
*/
protected $proxies = '*';
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var integer
*/
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;
protected $headers = Request::HEADER_X_FORWARDED_ALL;
}

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