Merge branch '1.0.0' into 'develop'

Init with base project laravel 10

See merge request ldc-web/projets/tregalette!1
This commit is contained in:
Vincent BRIDIER 2023-12-15 15:53:55 +00:00
commit dc22123dbb
937 changed files with 308303 additions and 0 deletions

5
.browserslistrc Normal file
View file

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

2
.dockerignore Normal file
View file

@ -0,0 +1,2 @@
.git
*.md

21
.editorconfig Normal file
View file

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

66
.env.example Normal file
View file

@ -0,0 +1,66 @@
APP_NAME=Laravel
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_USERNAME=root
DB_PASSWORD=
BROADCAST_DRIVER=log
CACHE_DRIVER=file
FILESYSTEM_DISK=local
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MAIL_MAILER=smtp
MAIL_HOST=mailpit
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="${APP_NAME}"
#CRM
CRM_ENDPOINT=
CRM_TOKEN=
CRM_PASSWORD=
#MESBR
MESBR_REF_SITE=
MESBR_REF_CAMP=
MESBR_ENDPOINT=
# SENTRY
SENTRY_LARAVEL_DSN=
# CONTACT MAILS
MAIL_CONTACT_RECLAIM=
MAIL_CONTACT_INFO=
MAIL_CONTACT_ASSOCIATIVE=
MAIL_CONTACT_PARTNERSHIP=
MAIL_CONTACT_COMMERCIAL=
MAIL_CONTACT_VARIOUS=
# MATOMO
MATOMO_SITE_ID=
# GOOGLE
# - RECAPTCHA
GOOGLE_RECAPTCHA_PUBLIC=
GOOGLE_RECAPTCHA_PRIVATE=
GOOGLE_RECAPTCHA_VERSION=
# ADMIN SECURE
# - API KEY
LDC_WEB_API_KEY=""

62
.eslintrc.json Normal file
View file

@ -0,0 +1,62 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:vue/vue3-strongly-recommended",
"@vue/eslint-config-typescript"
],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
"no-undef": "off"
}
},
{
"files": ["*.vue"],
"rules": {
"indent": "off"
}
}
],
"parser": "vue-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue", "@typescript-eslint"],
"ignorePatterns": [
"webpack.mix.js",
"**/*.php",
"**/*.svg",
"**/vendor/*.js",
"**/vendor/**/*.js",
"**/vendor/**/*.scss"
],
"rules": {
"indent": ["error", 4, { "SwitchCase": 1 }],
"linebreak-style": ["error", "unix"],
"quotes": ["error", "double"],
"semi": ["error", "always"],
"vue/script-indent": ["error", 2, { "baseIndent": 0, "switchCase": 1 }],
"vue/one-component-per-file": "off"
},
"settings": {
"import/resolver": {
"node": {
"extensions": [".js", ".vue", ".ts", ".d.ts"]
},
"alias": {
"extensions": [".vue", ".js", ".ts", ".scss", ".d.ts"],
"map": [
["~", "./resources/assets"],
["@", "./node_modules"]
]
}
}
}
}

11
.gitattributes vendored Normal file
View file

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

38
.gitignore vendored Normal file
View file

@ -0,0 +1,38 @@
.env
.env.backup
.env.production
.phpunit.result.cache
.stylelintcache
/.fleet
/.idea
/.phpunit.cache
/.vscode
/node_modules
/bootstrap
/public/build
/public/cache
/public/hot
/public/sitemap.xml
/public/storage
/storage/*.key
/vendor
auth.json
docker-compose.override.yml
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
.npmrc
# Ignore public favicon
/public/android-chrome-192x192.png
/public/android-chrome-512x512.png
/public/apple-touch-icon.png
/public/browserconfig.xml
/public/browserconfig.xml
/public/favicon-16x16.png
/public/favicon-32x32.png
/public/favicon.ico
/public/mstile-150x150.png
/public/safari-pinned-tab.svg
/public/site.webmanifest

24
.gitlab-ci.yml Normal file
View file

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

View file

@ -0,0 +1,11 @@
## Comportement constaté
## Comportement attendu
## Solution envisagée
## Étapes pour la reproduction du bug
*

View file

@ -0,0 +1,7 @@
## Présentation du besoin
## Implémentation
## Maquettes, documentations...

45
.stylelintrc.json Normal file
View file

@ -0,0 +1,45 @@
{
"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": true,
"selector-max-id": null,
"color-no-invalid-hex": true,
"order/properties-alphabetical-order": null,
"max-nesting-depth": 5,
"selector-max-compound-selectors": 5,
"declaration-colon-newline-after": null,
"value-list-comma-newline-after": null,
"scss/double-slash-comment-empty-line-before": [
"always",
{
"ignore": [
"between-comments",
"stylelint-commands",
"inside-block"
]
}
]
}
}

20
.vscode/extensions.json vendored Normal file
View file

@ -0,0 +1,20 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"rvest.vs-code-prettier-eslint",
"onecentlin.laravel-extension-pack",
"johnpapa.vscode-peacock",
"xdebug.php-debug",
"neilbrayfield.php-docblocker",
"bmewburn.vscode-intelephense-client",
"valeryanm.vscode-phpsab",
"syler.sass-indented",
"sibiraj-s.vscode-scss-formatter",
"mrmlnc.vscode-scss",
"stylelint.vscode-stylelint",
"vue.vscode-typescript-vue-plugin",
"wscats.vue",
"vue.volar",
"misterj.vue-volar-extention-pack"
]
}

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

@ -0,0 +1,25 @@
{
"workbench.colorCustomizations": {
"activityBar.activeBackground": "#8149de",
"activityBar.background": "#8149de",
"activityBar.foreground": "#e7e7e7",
"activityBar.inactiveForeground": "#e7e7e799",
"activityBarBadge.background": "#e28f5d",
"activityBarBadge.foreground": "#15202b",
"commandCenter.border": "#e7e7e799",
"sash.hoverBorder": "#8149de",
"statusBar.background": "#6526ce",
"statusBar.foreground": "#e7e7e7",
"statusBarItem.hoverBackground": "#8149de",
"statusBarItem.remoteBackground": "#6526ce",
"statusBarItem.remoteForeground": "#e7e7e7",
"titleBar.activeBackground": "#6526ce",
"titleBar.activeForeground": "#e7e7e7",
"titleBar.inactiveBackground": "#6526ce99",
"titleBar.inactiveForeground": "#e7e7e799"
},
"peacock.remoteColor": "#6526ce",
"intelephense.environment.phpVersion": "8.2.0",
"intelephense.completion.fullyQualifyGlobalConstantsAndFunctions": true,
"intelephense.phpdoc.useFullyQualifiedNames": true
}

5
Dockerfile Normal file
View file

@ -0,0 +1,5 @@
FROM registry.s-web.fr/ldc-web/projets/ldc-web-images-docker/ldcweb-production-8.2:latest
COPY --chown=www-data:www-data . /app/srcs
USER root
RUN chmod -R 755 /app/srcs/storage
USER www-data

View file

@ -0,0 +1,179 @@
<?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) ??
static::retrieveTrashed($credentials);
try {
/** @var \LDC\LDCCRM\CrmApiV2 */
$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
* @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)
{
/** @var \Illuminate\Database\Eloquent\Model $model */
if (\get_class($model) !== $this->usedModel) {
throw new \RuntimeException('Cannot authenticate any thing else than ' . $this->usedModel);
}
try {
/** @var \LDC\LDCCRM\CrmApiV2 */
$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 (!$model->hashcode or !CrmCustomer::find($model->hashcode)) {
$model->forceDelete();
return false;
}
if ($e->getApiError()->equals(ApiError::authFailed())) {
return false;
}
// ! Assert Authenticable model must be valided (MustValidate trait).
if ($e->getApiError()->equals(ApiError::customerNotActivated())) {
Session::flash('warning', \trans('Vous devez activer votre compte'));
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 \Illuminate\Database\Eloquent\Model $model */
$model = $this->usedModel::firstOrNew([
'email' => $crmCustomer->email
]);
$model->hashcode = $crmCustomer->hashcode;
$model->email_verified_at = $crmCustomer->status ? now() : null;
$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 $customer */
return $query->withTrashed()->first();
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Auth;
use App\Models\Customer;
use Illuminate\Support\Str;
use Illuminate\Auth\EloquentUserProvider;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
/**
* Overload EloquentUserProvider to handle
* Soft deleted Customers
*/
class ReactivateCustomerProvider extends EloquentUserProvider implements UserProvider
{
/**
* Retrieve a customer by the given credentials.
*
* @param array $credentials
* @return \Illuminate\Contracts\Auth\Authenticatable|null
* @throws \ErrorException If cannot connect to LDAP.
*/
public function retrieveByCredentials(array $credentials)
{
// * 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 $customer */
return $query->withTrashed()->first();
}
/**
* Validate a customer against the given credentials.
*
* @param \Illuminate\Contracts\Auth\Authenticatable $customer
* @param array $credentials
* @return boolean
* @throws \ErrorException If model provided is not a Customer or connection to server fails.
*/
public function validateCredentials(Authenticatable $customer, array $credentials)
{
/** @var \App\Models\Customer $customer */
if ($customer instanceof Customer) {
throw new \ErrorException('Cannot authenticate any thing else than ' . Customer::class);
}
$plain = $credentials['password'];
/** @var \App\Models\Customer $customer */
if ($this->hasher->check($plain, $customer->getAuthPassword())) {
// * Restore customer
return boolval($customer->restore());
}
return false;
}
}

View file

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

View file

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Jobs\GamesStatusUpdate;
use Illuminate\Console\Command;
class UpdateGamesStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:updatestatus:games';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update games status according to the publish and unpublish dates';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
\dispatch(new GamesStatusUpdate());
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Console\Commands;
use App\Jobs\NewsStatusUpdate;
use Illuminate\Console\Command;
class UpdateNewsStatus extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:updatestatus:news';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Update news status according to the publish and unpublish dates';
/**
* Execute the console command.
*
* @return void
*/
public function handle()
{
\dispatch(new NewsStatusUpdate());
}
}

View file

@ -0,0 +1,231 @@
<?php
namespace App\Console\Commands;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
class UserCreate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'uwil:user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend admin user';
/**
* The first name of the new user.
*
* @var string
*/
protected $first_name;
/**
* The last name of the new user.
*
* @var string
*/
protected $last_name;
/**
* The email of the new user.
*
* @var string
*/
protected $email;
/**
* The password of the new user.
*
* @var string
*/
protected $password;
/**
* The role of the new user.
*
* @var string
*/
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::visitor->label()],
RoleEnum::visitor->label(),
$maxAttempts = null,
$allowMultipleSelections = false
);
if ($tmp === RoleEnum::conceptor->label()) {
$tmp = RoleEnum::conceptor;
}
if ($tmp === RoleEnum::admin->label()) {
$tmp = RoleEnum::admin;
}
if ($tmp === RoleEnum::visitor->label()) {
$tmp = RoleEnum::visitor;
}
$validator = Validator::make(['role' => $tmp], [
'role' => 'required'
]);
$validator->validate();
$this->role = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please choose from the selection',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
} //end try
}; //end while
}
}

35
app/Console/Kernel.php Normal file
View file

@ -0,0 +1,35 @@
<?php
namespace App\Console;
use App\Jobs\GamesStatusUpdate;
use App\Jobs\NewsStatusUpdate;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->job(NewsStatusUpdate::class)->everyMinute();
$schedule->job(GamesStatusUpdate::class)->everyMinute();
}
/**
* Register the commands for the application.
*
* @return void
*/
protected function commands()
{
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}
}

View file

@ -0,0 +1,46 @@
<?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
*/
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

@ -0,0 +1,29 @@
<?php
namespace App\Enums\Contact;
use App\Traits\Enums\BaseEnum;
enum ObjectEnum: int
{
use BaseEnum;
case reclaim = 1;
case info = 2;
case associativeRequest = 3;
case parternershipRequest = 4;
case commercialRequest = 5;
case variousRequest = 6;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::reclaim->name => 'Réclamation',
self::info->name => 'Informations produits / emballage',
self::associativeRequest->name => 'Demandes associatives',
self::parternershipRequest->name => 'Demandes de partenariat',
self::commercialRequest->name => 'Demandes commerciales',
self::variousRequest->name => 'Demandes diverses'
];
}

623
app/Enums/Country.php Normal file
View file

@ -0,0 +1,623 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum Country: int
{
use BaseEnum;
case afghanistan = 1;
case alandIslands = 2;
case albania = 3;
case algeria = 4;
case americanSamoa = 5;
case andorra = 6;
case angola = 7;
case anguilla = 8;
case antarctica = 9;
case antiguaAndBarbuda = 10;
case argentina = 11;
case armenia = 12;
case aruba = 13;
case australia = 14;
case austria = 15;
case azerbaijan = 16;
case bahrain = 17;
case bahamas = 18;
case bangladesh = 19;
case barbados = 20;
case belarus = 21;
case belgium = 22;
case belize = 23;
case benin = 24;
case bermuda = 25;
case bhutan = 26;
case boliviaPlurinationalStateOf = 27;
case bonaireSintEustatiusAndSaba = 28;
case bosniaAndHerzegovina = 29;
case botswana = 30;
case bouvetIsland = 31;
case brazil = 32;
case britishIndianOceanTerritory = 33;
case bruneiDarussalam = 34;
case bulgaria = 35;
case burkinaFaso = 36;
case burundi = 37;
case cambodia = 38;
case cameroon = 39;
case canada = 40;
case capeVerde = 41;
case caymanIslands = 42;
case centralAfricanRepublic = 43;
case chad = 44;
case chile = 45;
case china = 46;
case christmasIsland = 47;
case cocosKeelingIslands = 48;
case colombia = 49;
case comoros = 50;
case congo = 51;
case congoTheDemocraticRepublicOfThe = 52;
case cookIslands = 53;
case costaRica = 54;
case coteDivoire = 55;
case croatia = 56;
case cuba = 57;
case curacao = 58;
case cyprus = 59;
case czechRepublic = 60;
case denmark = 61;
case djibouti = 62;
case dominica = 63;
case dominicanRepublic = 64;
case ecuador = 65;
case egypt = 66;
case elSalvador = 67;
case equatorialGuinea = 68;
case eritrea = 69;
case estonia = 70;
case ethiopia = 71;
case falklandIslandsMalvinas = 72;
case faroeIslands = 73;
case fiji = 74;
case finland = 75;
case france = 76;
case frenchGuiana = 77;
case frenchPolynesia = 78;
case frenchSouthernTerritories = 79;
case gabon = 80;
case gambia = 81;
case georgia = 82;
case germany = 83;
case ghana = 84;
case gibraltar = 85;
case greece = 86;
case greenland = 87;
case grenada = 88;
case guadeloupe = 89;
case guam = 90;
case guatemala = 91;
case guernsey = 92;
case guinea = 93;
case guineaBissau = 94;
case guyana = 95;
case haiti = 96;
case heardIslandAndMcdonaldIslands = 97;
case holySeeVaticanCityState = 98;
case honduras = 99;
case hongKong = 100;
case hungary = 101;
case iceland = 102;
case india = 103;
case indonesia = 104;
case iranIslamicRepublicOf = 105;
case iraq = 106;
case ireland = 107;
case isleOfMan = 108;
case israel = 109;
case italy = 110;
case jamaica = 111;
case japan = 112;
case jersey = 113;
case jordan = 114;
case kazakhstan = 115;
case kenya = 116;
case kiribati = 117;
case koreaDemocraticPeoplesRepublicOf = 118;
case koreaRepublicOf = 119;
case kuwait = 120;
case kyrgyzstan = 121;
case laoPeoplesDemocraticRepublic = 122;
case latvia = 123;
case lebanon = 124;
case lesotho = 125;
case liberia = 126;
case libya = 127;
case liechtenstein = 128;
case lithuania = 129;
case luxembourg = 130;
case macao = 131;
case macedoniaTheFormerYugoslavRepublicOf = 132;
case madagascar = 133;
case malawi = 134;
case malaysia = 135;
case maldives = 136;
case mali = 137;
case malta = 138;
case marshallIslands = 139;
case martinique = 140;
case mauritania = 141;
case mauritius = 142;
case mayotte = 143;
case mexico = 144;
case micronesiaFederatedStatesOf = 145;
case moldovaRepublicOf = 146;
case monaco = 147;
case mongolia = 148;
case montenegro = 149;
case montserrat = 150;
case morocco = 151;
case mozambique = 152;
case myanmar = 153;
case namibia = 154;
case nauru = 155;
case nepal = 156;
case netherlands = 157;
case newCaledonia = 158;
case newZealand = 159;
case nicaragua = 160;
case niger = 161;
case nigeria = 162;
case niue = 163;
case norfolkIsland = 164;
case northernMarianaIslands = 165;
case norway = 166;
case oman = 167;
case pakistan = 168;
case palau = 169;
case palestineStateOf = 170;
case panama = 171;
case papuaNewGuinea = 172;
case paraguay = 173;
case peru = 174;
case philippines = 175;
case pitcairn = 176;
case poland = 177;
case portugal = 178;
case puertoRico = 179;
case qatar = 180;
case reunion = 181;
case romania = 182;
case russianFederation = 183;
case rwanda = 184;
case saintBarthelemy = 185;
case saintHelenaAscensionAndTristanDaCunha = 186;
case saintKittsAndNevis = 187;
case saintLucia = 188;
case saintMartinFrenchPart = 189;
case saintPierreAndMiquelon = 190;
case saintVincentAndTheGrenadines = 191;
case samoa = 192;
case sanMarino = 193;
case saoTomeAndPrincipe = 194;
case saudiArabia = 195;
case senegal = 196;
case serbia = 197;
case seychelles = 198;
case sierraLeone = 199;
case singapore = 200;
case sintMaartenDutchPart = 201;
case slovakia = 202;
case slovenia = 203;
case solomonIslands = 204;
case somalia = 205;
case southAfrica = 206;
case southGeorgiaAndTheSouthSandwichIslands = 207;
case southSudan = 208;
case spain = 209;
case sriLanka = 210;
case sudan = 211;
case suriname = 212;
case svalbardAndJanMayen = 213;
case swaziland = 214;
case sweden = 215;
case switzerland = 216;
case syrianArabRepublic = 217;
case taiwanProvinceOfChina = 218;
case tajikistan = 219;
case tanzaniaUnitedRepublicOf = 220;
case thailand = 221;
case timorLeste = 222;
case togo = 223;
case tokelau = 224;
case tonga = 225;
case trinidadAndTobago = 226;
case tunisia = 227;
case turkey = 228;
case turkmenistan = 229;
case turksAndCaicosIslands = 230;
case tuvalu = 231;
case uganda = 232;
case ukraine = 233;
case unitedArabEmirates = 234;
case unitedKingdom = 235;
case unitedStates = 236;
case unitedStatesMinorOutlyingIslands = 237;
case uruguay = 238;
case uzbekistan = 239;
case vanuatu = 240;
case venezuelaBolivarianRepublicOf = 241;
case vietNam = 242;
case virginIslandsBritish = 243;
case virginIslandsUs = 244;
case wallisAndFutuna = 245;
case westernSahara = 246;
case yemen = 247;
case zambia = 248;
case zimbabwe = 249;
/**
* Optionnal labels definition
*/
private const LABELS = [
self::afghanistan->name => 'Afghanistan',
self::alandIslands->name => 'Åland Islands',
self::albania->name => 'Albania',
self::algeria->name => 'Algeria',
self::americanSamoa->name => 'American Samoa',
self::andorra->name => 'Andorra',
self::angola->name => 'Angola',
self::anguilla->name => 'Anguilla',
self::antarctica->name => 'Antarctica',
self::antiguaAndBarbuda->name => 'Antigua and Barbuda',
self::argentina->name => 'Argentina',
self::armenia->name => 'Armenia',
self::aruba->name => 'Aruba',
self::australia->name => 'Australia',
self::austria->name => 'Austria',
self::azerbaijan->name => 'Azerbaijan',
self::bahrain->name => 'Bahrain',
self::bahamas->name => 'Bahamas',
self::bangladesh->name => 'Bangladesh',
self::barbados->name => 'Barbados',
self::belarus->name => 'Belarus',
self::belgium->name => 'Belgium',
self::belize->name => 'Belize',
self::benin->name => 'Benin',
self::bermuda->name => 'Bermuda',
self::bhutan->name => 'Bhutan',
self::boliviaPlurinationalStateOf->name => 'Bolivia, Plurinational State of',
self::bonaireSintEustatiusAndSaba->name => 'Bonaire, Sint Eustatius and Saba',
self::bosniaAndHerzegovina->name => 'Bosnia and Herzegovina',
self::botswana->name => 'Botswana',
self::bouvetIsland->name => 'Bouvet Island',
self::brazil->name => 'Brazil',
self::britishIndianOceanTerritory->name => 'British Indian Ocean Territory',
self::bruneiDarussalam->name => 'Brunei Darussalam',
self::bulgaria->name => 'Bulgaria',
self::burkinaFaso->name => 'Burkina Faso',
self::burundi->name => 'Burundi',
self::cambodia->name => 'Cambodia',
self::cameroon->name => 'Cameroon',
self::canada->name => 'Canada',
self::capeVerde->name => 'Cape Verde',
self::caymanIslands->name => 'Cayman Islands',
self::centralAfricanRepublic->name => 'Central African Republic',
self::chad->name => 'Chad',
self::chile->name => 'Chile',
self::china->name => 'China',
self::christmasIsland->name => 'Christmas Island',
self::cocosKeelingIslands->name => 'Cocos (Keeling) Islands',
self::colombia->name => 'Colombia',
self::comoros->name => 'Comoros',
self::congo->name => 'Congo',
self::congoTheDemocraticRepublicOfThe->name => 'Congo, the Democratic Republic of the',
self::cookIslands->name => 'Cook Islands',
self::costaRica->name => 'Costa Rica',
self::coteDivoire->name => 'Côte d\'Ivoire',
self::croatia->name => 'Croatia',
self::cuba->name => 'Cuba',
self::curacao->name => 'Curaçao',
self::cyprus->name => 'Cyprus',
self::czechRepublic->name => 'Czech Republic',
self::denmark->name => 'Denmark',
self::djibouti->name => 'Djibouti',
self::dominica->name => 'Dominica',
self::dominicanRepublic->name => 'Dominican Republic',
self::ecuador->name => 'Ecuador',
self::egypt->name => 'Egypt',
self::elSalvador->name => 'El Salvador',
self::equatorialGuinea->name => 'Equatorial Guinea',
self::eritrea->name => 'Eritrea',
self::estonia->name => 'Estonia',
self::ethiopia->name => 'Ethiopia',
self::falklandIslandsMalvinas->name => 'Falkland Islands (Malvinas)',
self::faroeIslands->name => 'Faroe Islands',
self::fiji->name => 'Fiji',
self::finland->name => 'Finland',
self::france->name => 'France',
self::frenchGuiana->name => 'French Guiana',
self::frenchPolynesia->name => 'French Polynesia',
self::frenchSouthernTerritories->name => 'French Southern Territories',
self::gabon->name => 'Gabon',
self::gambia->name => 'Gambia',
self::georgia->name => 'Georgia',
self::germany->name => 'Germany',
self::ghana->name => 'Ghana',
self::gibraltar->name => 'Gibraltar',
self::greece->name => 'Greece',
self::greenland->name => 'Greenland',
self::grenada->name => 'Grenada',
self::guadeloupe->name => 'Guadeloupe',
self::guam->name => 'Guam',
self::guatemala->name => 'Guatemala',
self::guernsey->name => 'Guernsey',
self::guinea->name => 'Guinea',
self::guineaBissau->name => 'GuineaBissau',
self::guyana->name => 'Guyana',
self::haiti->name => 'Haiti',
self::heardIslandAndMcdonaldIslands->name => 'Heard Island and McDonald Islands',
self::holySeeVaticanCityState->name => 'Holy See (Vatican City State)',
self::honduras->name => 'Honduras',
self::hongKong->name => 'Hong Kong',
self::hungary->name => 'Hungary',
self::iceland->name => 'Iceland',
self::india->name => 'India',
self::indonesia->name => 'Indonesia',
self::iranIslamicRepublicOf->name => 'Iran, Islamic Republic of',
self::iraq->name => 'Iraq',
self::ireland->name => 'Ireland',
self::isleOfMan->name => 'Isle of Man',
self::israel->name => 'Israel',
self::italy->name => 'Italy',
self::jamaica->name => 'Jamaica',
self::japan->name => 'Japan',
self::jersey->name => 'Jersey',
self::jordan->name => 'Jordan',
self::kazakhstan->name => 'Kazakhstan',
self::kenya->name => 'Kenya',
self::kiribati->name => 'Kiribati',
self::koreaDemocraticPeoplesRepublicOf->name => 'Korea, Democratic People\'s Republic of',
self::koreaRepublicOf->name => 'Korea, Republic of',
self::kuwait->name => 'Kuwait',
self::kyrgyzstan->name => 'Kyrgyzstan',
self::laoPeoplesDemocraticRepublic->name => 'Lao People\'s Democratic Republic',
self::latvia->name => 'Latvia',
self::lebanon->name => 'Lebanon',
self::lesotho->name => 'Lesotho',
self::liberia->name => 'Liberia',
self::libya->name => 'Libya',
self::liechtenstein->name => 'Liechtenstein',
self::lithuania->name => 'Lithuania',
self::luxembourg->name => 'Luxembourg',
self::macao->name => 'Macao',
self::macedoniaTheFormerYugoslavRepublicOf->name => 'Macedonia, the Former Yugoslav Republic of',
self::madagascar->name => 'Madagascar',
self::malawi->name => 'Malawi',
self::malaysia->name => 'Malaysia',
self::maldives->name => 'Maldives',
self::mali->name => 'Mali',
self::malta->name => 'Malta',
self::marshallIslands->name => 'Marshall Islands',
self::martinique->name => 'Martinique',
self::mauritania->name => 'Mauritania',
self::mauritius->name => 'Mauritius',
self::mayotte->name => 'Mayotte',
self::mexico->name => 'Mexico',
self::micronesiaFederatedStatesOf->name => 'Micronesia, Federated States of',
self::moldovaRepublicOf->name => 'Moldova, Republic of',
self::monaco->name => 'Monaco',
self::mongolia->name => 'Mongolia',
self::montenegro->name => 'Montenegro',
self::montserrat->name => 'Montserrat',
self::morocco->name => 'Morocco',
self::mozambique->name => 'Mozambique',
self::myanmar->name => 'Myanmar',
self::namibia->name => 'Namibia',
self::nauru->name => 'Nauru',
self::nepal->name => 'Nepal',
self::netherlands->name => 'Netherlands',
self::newCaledonia->name => 'New Caledonia',
self::newZealand->name => 'New Zealand',
self::nicaragua->name => 'Nicaragua',
self::niger->name => 'Niger',
self::nigeria->name => 'Nigeria',
self::niue->name => 'Niue',
self::norfolkIsland->name => 'Norfolk Island',
self::northernMarianaIslands->name => 'Northern Mariana Islands',
self::norway->name => 'Norway',
self::oman->name => 'Oman',
self::pakistan->name => 'Pakistan',
self::palau->name => 'Palau',
self::palestineStateOf->name => 'Palestine, State of',
self::panama->name => 'Panama',
self::papuaNewGuinea->name => 'Papua New Guinea',
self::paraguay->name => 'Paraguay',
self::peru->name => 'Peru',
self::philippines->name => 'Philippines',
self::pitcairn->name => 'Pitcairn',
self::poland->name => 'Poland',
self::portugal->name => 'Portugal',
self::puertoRico->name => 'Puerto Rico',
self::qatar->name => 'Qatar',
self::reunion->name => 'Réunion',
self::romania->name => 'Romania',
self::russianFederation->name => 'Russian Federation',
self::rwanda->name => 'Rwanda',
self::saintBarthelemy->name => 'Saint Barthélemy',
self::saintHelenaAscensionAndTristanDaCunha->name => 'Saint Helena, Ascension and Tristan da Cunha',
self::saintKittsAndNevis->name => 'Saint Kitts and Nevis',
self::saintLucia->name => 'Saint Lucia',
self::saintMartinFrenchPart->name => 'Saint Martin (French part)',
self::saintPierreAndMiquelon->name => 'Saint Pierre and Miquelon',
self::saintVincentAndTheGrenadines->name => 'Saint Vincent and the Grenadines',
self::samoa->name => 'Samoa',
self::sanMarino->name => 'San Marino',
self::saoTomeAndPrincipe->name => 'Sao Tome and Principe',
self::saudiArabia->name => 'Saudi Arabia',
self::senegal->name => 'Senegal',
self::serbia->name => 'Serbia',
self::seychelles->name => 'Seychelles',
self::sierraLeone->name => 'Sierra Leone',
self::singapore->name => 'Singapore',
self::sintMaartenDutchPart->name => 'Sint Maarten (Dutch part)',
self::slovakia->name => 'Slovakia',
self::slovenia->name => 'Slovenia',
self::solomonIslands->name => 'Solomon Islands',
self::somalia->name => 'Somalia',
self::southAfrica->name => 'South Africa',
self::southGeorgiaAndTheSouthSandwichIslands->name => 'South Georgia and the South Sandwich Islands',
self::southSudan->name => 'South Sudan',
self::spain->name => 'Spain',
self::sriLanka->name => 'Sri Lanka',
self::sudan->name => 'Sudan',
self::suriname->name => 'Suriname',
self::svalbardAndJanMayen->name => 'Svalbard and Jan Mayen',
self::swaziland->name => 'Swaziland',
self::sweden->name => 'Sweden',
self::switzerland->name => 'Switzerland',
self::syrianArabRepublic->name => 'Syrian Arab Republic',
self::taiwanProvinceOfChina->name => 'Taiwan, Province of China',
self::tajikistan->name => 'Tajikistan',
self::tanzaniaUnitedRepublicOf->name => 'Tanzania, United Republic of',
self::thailand->name => 'Thailand',
self::timorLeste->name => 'TimorLeste',
self::togo->name => 'Togo',
self::tokelau->name => 'Tokelau',
self::tonga->name => 'Tonga',
self::trinidadAndTobago->name => 'Trinidad and Tobago',
self::tunisia->name => 'Tunisia',
self::turkey->name => 'Turkey',
self::turkmenistan->name => 'Turkmenistan',
self::turksAndCaicosIslands->name => 'Turks and Caicos Islands',
self::tuvalu->name => 'Tuvalu',
self::uganda->name => 'Uganda',
self::ukraine->name => 'Ukraine',
self::unitedArabEmirates->name => 'United Arab Emirates',
self::unitedKingdom->name => 'United Kingdom',
self::unitedStates->name => 'United States',
self::unitedStatesMinorOutlyingIslands->name => 'United States Minor Outlying Islands',
self::uruguay->name => 'Uruguay',
self::uzbekistan->name => 'Uzbekistan',
self::vanuatu->name => 'Vanuatu',
self::venezuelaBolivarianRepublicOf->name => 'Venezuela, Bolivarian Republic of',
self::vietNam->name => 'Viet Nam',
self::virginIslandsBritish->name => 'Virgin Islands, British',
self::virginIslandsUs->name => 'Virgin Islands, U.S.',
self::wallisAndFutuna->name => 'Wallis and Futuna',
self::westernSahara->name => 'Western Sahara',
self::yemen->name => 'Yemen',
self::zambia->name => 'Zambia',
self::zimbabwe->name => 'Zimbabwe',
];
/**
* Get Valid Shipping Countries
*
* @return array
*/
public static function validShippingCountries(): array
{
return [
self::france,
/**
* // * DROM COM .
// self::guadeloupe,
// self::martinique,
// self::frenchGuiana,
// self::reunion,
// self::saintPierreAndMiquelon,
// self::mayotte,
// self::saintBarthelemy,
// self::saintMartinFrenchPart,
// self::wallisAndFutuna,
// self::frenchPolynesia,
// self::newCaledonia
*/
];
}
/**
* Get Valid Shipping Countries
*
* @return array
*/
public static function validBillingCountries(): array
{
return [
self::france,
// * DROM COM.
self::guadeloupe,
self::martinique,
self::frenchGuiana,
self::reunion,
self::saintPierreAndMiquelon,
self::mayotte,
self::saintBarthelemy,
self::saintMartinFrenchPart,
self::wallisAndFutuna,
self::frenchPolynesia,
self::newCaledonia
];
}
/**
* Get Alpha 2 ISO code of valid shipping countries
*
* @return array
*/
public static function getValidShippingCountriesIsoAplha2Codes(): array
{
return ['FR'];
}
/**
* Get Alpha 2 ISO code of valid billing countries
*
* @return array
*/
public static function getValidBillingCountriesIsoAplha2Codes(): array
{
return \array_values(self::ISOCODES);
}
/**
* Custom added ISOCODES definition
*/
private const ISOCODES = [
self::france->name => 'FR',
self::guadeloupe->name => 'GP',
self::martinique->name => 'MQ',
self::frenchGuiana->name => 'GF',
self::reunion->name => 'RE',
self::saintPierreAndMiquelon->name => 'PM',
self::mayotte->name => 'YT',
self::saintBarthelemy->name => 'BL',
self::saintMartinFrenchPart->name => 'MF',
self::wallisAndFutuna->name => 'WF',
self::frenchPolynesia->name => 'PF',
self::newCaledonia->name => 'NC',
];
/**
* Get ISOCODE
*
* @return string
*/
public function iso(): string
{
return self::ISOCODES[$this->name];
}
/**
* Get enum ISOCODES
*
* @return array<string>
*/
public static function toIsoCodes(): array
{
return self::ISOCODES;
}
}

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

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

View file

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

View file

@ -0,0 +1,29 @@
<?php
namespace App\Enums\Products;
use App\Traits\Enums\BaseEnum;
enum NutriscoreEnum: int
{
use BaseEnum;
case a = 1;
case b = 2;
case c = 3;
case d = 4;
case e = 5;
case none = 99;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::a->name => 'A',
self::b->name => 'B',
self::c->name => 'C',
self::d->name => 'D',
self::e->name => 'E',
self::none->name => 'Aucun',
];
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Enums;
use App\Traits\Enums\BaseEnum;
enum StaticPageType: int
{
use BaseEnum;
case contact = 1;
case legals = 2;
case confidentiality = 3;
case home = 4;
case ranges = 5;
case range = 6;
case klub = 7;
case klub_register = 8;
case history = 9;
case commitments = 10;
case news = 11;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::contact->name => 'Contact',
self::legals->name => 'Mentions légales',
self::confidentiality->name => 'Confidentialité',
self::home->name => 'Accueil',
self::ranges->name => 'Nos gammes',
self::range->name => 'Une gamme',
self::klub->name => 'Le Klub',
self::klub_register->name => 'Le Klub - Inscription',
self::history->name => 'Notre Histoire',
self::commitments->name => 'Nos Engagements',
self::news->name => 'Actualités',
];
}

View file

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

View file

@ -0,0 +1,23 @@
<?php
namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum;
enum RoleEnum: int
{
use BaseEnum;
case conceptor = 1;
case admin = 2;
case visitor = 3;
/**
* Optionnal labels definition.
*/
private const LABELS = [
self::conceptor->name => 'Concepteur',
self::admin->name => 'Administrateur',
self::visitor->name => 'Visiteur',
];
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as 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>>
*/
protected $dontReport = [];
/**
* A list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->reportable(function (Throwable $e) {
if (app()->bound('sentry')) {
app('sentry')->captureException($e);
}
});
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\CommitmentPages\UpdateCommitmentPageRequest;
use App\Models\CommitmentPage;
use App\Models\Picto;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class CommitmentPageController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(CommitmentPage::class);
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\CommitmentPage $commitmentPage
* @return \Illuminate\Contracts\View\View
*/
public function edit(CommitmentPage $commitmentPage): \Illuminate\Contracts\View\View
{
$pictoModels = Picto::all();
return view('bo.pages.commitment_pages.edit', [
'commitmentPageModel' => $commitmentPage,
'pictoModels' => $pictoModels
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\CommitmentPages\UpdateCommitmentPageRequest $request
* @param \App\Models\CommitmentPage $commitmentPage
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateCommitmentPageRequest $request,
CommitmentPage $commitmentPage
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $commitmentPage) {
$commitmentPage->fill($request->safe()->except(['pictos']));
$commitmentPage->pictos()->sync(collect($request->validated('pictos'))->pluck('id'));
if ($commitmentPage->saveOrFail()) {
return redirect()->route('bo.commitmentpages.edit', $commitmentPage)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.commitment_page', 1))
]));
}
return redirect()->route('bo.commitmentpages.edit', $commitmentPage)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.commitment_page', 1))
]));
});
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\View;
class DashboardController extends Controller
{
/**
* Dashboard Page
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): View
{
return view('bo.pages.dashboard');
}
}

View file

@ -0,0 +1,169 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Games\StoreGameRequest;
use App\Http\Requests\Bo\Games\UpdateGameRequest;
use App\Models\Game;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class GameController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Game::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 $gameModels */
$gameModels = Game::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$gameModels,
$search,
null,
'title_line1',
'title_line2',
'text'
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.custom.title_line1')),
trans('validation.custom.title_line2'),
trans('validation.attributes.text')
]);
/** Sort columns with a query */
$this->sortQuery($gameModels);
/** Custom pagination */
$gameModels = $this->paginate($gameModels);
return view('bo.pages.games.index', compact('gameModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Game $game
* @return \Illuminate\Contracts\View\View
*/
public function create(Game $game): \Illuminate\Contracts\View\View
{
return view('bo.pages.games.create', ['gameModel' => $game]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Games\StoreGameRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreGameRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$game = new Game();
$game->fill($request->validated());
if ($game->saveOrFail()) {
return redirect()->route('bo.games.edit', $game)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Game $game
* @return \Illuminate\Contracts\View\View
*/
public function edit(Game $game): \Illuminate\Contracts\View\View
{
return view('bo.pages.games.edit', ['gameModel' => $game]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Games\UpdateGameRequest $request
* @param \App\Models\Game $game
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateGameRequest $request, Game $game): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $game) {
$game->fill($request->validated());
if ($game->saveOrFail()) {
return redirect()->route('bo.games.edit', $game)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
}
return redirect()->route('bo.games.edit', $game)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Game $game
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Game $game): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($game) {
if ($game->deleteOrFail()) {
return redirect()->route('bo.games.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.game', 1))
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Game $game
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Game $game): \Illuminate\Contracts\View\View
{
return $this->create($game->replicate());
}
}

View file

@ -0,0 +1,168 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\HistoricalMilestones\StoreHistoricalMilestoneRequest;
use App\Http\Requests\Bo\HistoricalMilestones\UpdateHistoricalMilestoneRequest;
use App\Models\HistoricalMilestone;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class HistoricalMilestoneController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(HistoricalMilestone::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 $historicalMilestoneModels */
$historicalMilestoneModels = HistoricalMilestone::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$historicalMilestoneModels,
$search,
null,
'text',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.text')),
]);
/** Sort columns with a query */
$this->sortQuery($historicalMilestoneModels);
/** Custom pagination */
$historicalMilestoneModels = $this->paginate($historicalMilestoneModels);
return view(
'bo.pages.historicalmilestones.index',
compact('historicalMilestoneModels', 'search', 'searchFields')
);
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\HistoricalMilestone $historicalMilestone
* @return \Illuminate\Contracts\View\View
*/
public function create(HistoricalMilestone $historicalMilestone): \Illuminate\Contracts\View\View
{
return view('bo.pages.historicalmilestones.create', ['historicalMilestoneModel' => $historicalMilestone]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\HistoricalMilestone\StoreHistoricalMilestoneRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreHistoricalMilestoneRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$historicalMilestone = new HistoricalMilestone();
$historicalMilestone->fill($request->validated());
if ($historicalMilestone->saveOrFail()) {
return redirect()->route('bo.historicalmilestones.edit', $historicalMilestone)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\HistoricalMilestone $historicalMilestone
* @return \Illuminate\Contracts\View\View
*/
public function edit(HistoricalMilestone $historicalMilestone): \Illuminate\Contracts\View\View
{
return view('bo.pages.historicalmilestones.edit', ['historicalMilestoneModel' => $historicalMilestone]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\HistoricalMilestone\UpdateHistoricalMilestoneRequest $request
* @param \App\Models\HistoricalMilestone $historicalMilestone
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateHistoricalMilestoneRequest $request,
HistoricalMilestone $historicalMilestone
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $historicalMilestone) {
$historicalMilestone->fill($request->validated());
if ($historicalMilestone->saveOrFail()) {
return redirect()->route('bo.historicalmilestones.edit', $historicalMilestone)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
}
return redirect()->route('bo.historicalmilestones.edit', $historicalMilestone)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\HistoricalMilestone $historicalMilestone
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(HistoricalMilestone $historicalMilestone): \Illuminate\Http\RedirectResponse
{
if ($historicalMilestone->deleteOrFail()) {
return redirect()->route('bo.historicalmilestones.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.historicalMilestone'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\HistoricalMilestone $historicalMilestone
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(HistoricalMilestone $historicalMilestone): \Illuminate\Contracts\View\View
{
return $this->create($historicalMilestone->replicate());
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\KlubBlocs\UpdateKlubBlocRequest;
use App\Models\KlubBloc;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class KlubBlocController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(KlubBloc::class);
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\KlubBloc $klubBloc
* @return \Illuminate\Contracts\View\View
*/
public function edit(KlubBloc $klubBloc): \Illuminate\Contracts\View\View
{
return view('bo.pages.klub_blocs.edit', ['klubBlocModel' => $klubBloc]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\KlubBlocs\UpdateKlubBlocRequest $request
* @param \App\Models\KlubBloc $klubBloc
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateKlubBlocRequest $request,
KlubBloc $klubBloc
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $klubBloc) {
$klubBloc->fill($request->validated());
if ($klubBloc->saveOrFail()) {
return redirect()->route('bo.klubblocs.edit', $klubBloc)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.klub_bloc'))
]));
}
return redirect()->route('bo.klubblocs.edit', $klubBloc)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.klub_bloc'))
]));
});
}
}

View file

@ -0,0 +1,171 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\LocalCommitments\StoreLocalCommitmentRequest;
use App\Http\Requests\Bo\LocalCommitments\UpdateLocalCommitmentRequest;
use App\Models\LocalCommitment;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class LocalCommitmentController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(LocalCommitment::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 $localCommitmentModels */
$localCommitmentModels = LocalCommitment::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$localCommitmentModels,
$search,
null,
'title',
'text'
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.title')),
trans('validation.attributes.text')
]);
/** Sort columns with a query */
$this->sortQuery($localCommitmentModels);
/** Custom pagination */
$localCommitmentModels = $this->paginate($localCommitmentModels);
return view('bo.pages.local_commitments.index', compact('localCommitmentModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\LocalCommitment $localCommitment
* @return \Illuminate\Contracts\View\View
*/
public function create(LocalCommitment $localCommitment): \Illuminate\Contracts\View\View
{
return view('bo.pages.local_commitments.create', ['localCommitmentModel' => $localCommitment]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\LocalCommitments\StoreLocalCommitmentRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreLocalCommitmentRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$localCommitment = new LocalCommitment();
$localCommitment->fill($request->validated());
if ($localCommitment->saveOrFail()) {
return redirect()->route('bo.localcommitments.edit', $localCommitment)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\LocalCommitment $localCommitment
* @return \Illuminate\Contracts\View\View
*/
public function edit(LocalCommitment $localCommitment): \Illuminate\Contracts\View\View
{
return view('bo.pages.local_commitments.edit', ['localCommitmentModel' => $localCommitment]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\LocalCommitments\UpdateLocalCommitmentRequest $request
* @param \App\Models\LocalCommitment $localCommitment
* @return \Illuminate\Http\RedirectResponse
*/
public function update(
UpdateLocalCommitmentRequest $request,
LocalCommitment $localCommitment
): \Illuminate\Http\RedirectResponse {
return DB::transaction(function () use ($request, $localCommitment) {
$localCommitment->fill($request->validated());
if ($localCommitment->saveOrFail()) {
return redirect()->route('bo.localcommitments.edit', $localCommitment)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
}
return redirect()->route('bo.localcommitments.edit', $localCommitment)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\LocalCommitment $localCommitment
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(LocalCommitment $localCommitment): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($localCommitment) {
if ($localCommitment->deleteOrFail()) {
return redirect()->route('bo.localcommitments.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(trans_choice('models.classes.local_commitment', 1))
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\LocalCommitment $localCommitment
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(LocalCommitment $localCommitment): \Illuminate\Contracts\View\View
{
return $this->create($localCommitment->replicate());
}
}

View file

@ -0,0 +1,163 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\News\StoreNewsRequest;
use App\Http\Requests\Bo\News\UpdateNewsRequest;
use App\Models\News;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class NewsController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(News::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 $newsModels */
$newsModels = News::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$newsModels,
$search,
null,
'text',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.attributes.text')),
]);
/** Sort columns with a query */
$this->sortQuery($newsModels);
/** Custom pagination */
$newsModels = $this->paginate($newsModels);
return view('bo.pages.news.index', compact('newsModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function create(News $news): \Illuminate\Contracts\View\View
{
return view('bo.pages.news.create', ['newsModel' => $news]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\News\StoreNewsRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StoreNewsRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$news = new News();
$news->fill($request->validated());
if ($news->saveOrFail()) {
return redirect()->route('bo.news.edit', $news)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function edit(News $news): \Illuminate\Contracts\View\View
{
return view('bo.pages.news.edit', ['newsModel' => $news]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\News\UpdateNewsRequest $request
* @param \App\Models\News $news
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateNewsRequest $request, News $news): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $news) {
$news->fill($request->validated());
if ($news->saveOrFail()) {
return redirect()->route('bo.news.edit', $news)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->route('bo.news.edit', $news)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\News $news
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(News $news): \Illuminate\Http\RedirectResponse
{
if ($news->deleteOrFail()) {
return redirect()->route('bo.news.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.news'))
]));
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\News $news
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(News $news): \Illuminate\Contracts\View\View
{
return $this->create($news->replicate());
}
}

View file

@ -0,0 +1,172 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Pictos\StorePictoRequest;
use App\Http\Requests\Bo\Pictos\UpdatePictoRequest;
use App\Models\Picto;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class PictoController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Picto::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 $pictoModels */
$pictoModels = Picto::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$pictoModels,
$search,
null,
'label',
'picture_alt',
'picture_title'
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.custom.label')),
trans('validation.custom.attribute_alt'),
trans('validation.custom.attribute_title')
]);
/** Sort columns with a query */
$this->sortQuery($pictoModels);
/** Custom pagination */
$pictoModels = $this->paginate($pictoModels);
return view('bo.pages.pictos.index', compact('pictoModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Picto $picto
* @return \Illuminate\Contracts\View\View
*/
public function create(Picto $picto): \Illuminate\Contracts\View\View
{
return view('bo.pages.pictos.create', ['pictoModel' => $picto]);
}
/**
* Store a newly created resource in storage.
*
* @param \App\Http\Requests\Bo\Pictos\StorePictoRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function store(StorePictoRequest $request): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request) {
$picto = new Picto();
$picto->fill($request->validated());
if ($picto->saveOrFail()) {
return redirect()->route('bo.pictos.edit', $picto)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
});
}
/**
* Show the form for editing the specified resource.
*
* @param App\Models\Picto $picto
* @return \Illuminate\Contracts\View\View
*/
public function edit(Picto $picto): \Illuminate\Contracts\View\View
{
return view('bo.pages.pictos.edit', ['pictoModel' => $picto]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Pictos\UpdatePictoRequest $request
* @param \App\Models\Picto $picto
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdatePictoRequest $request, Picto $picto): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $picto) {
$picto->fill($request->validated());
if ($picto->saveOrFail()) {
return redirect()->route('bo.pictos.edit', $picto)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
}
return redirect()->route('bo.pictos.edit', $picto)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Models\Picto $picto
* @return \Illuminate\Http\RedirectResponse
*/
public function destroy(Picto $picto): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($picto) {
$picto->products()->detach();
$picto->commitmentPages()->detach();
if ($picto->deleteOrFail()) {
return redirect()->route('bo.pictos.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.picto'))
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Picto $picto
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Picto $picto): \Illuminate\Contracts\View\View
{
return $this->create($picto->replicate());
}
}

View file

@ -0,0 +1,189 @@
<?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\Picto;
use App\Models\Product;
use App\Models\Range;
use App\Traits\Controllers\ChangesModelOrder;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ProductController extends Controller
{
use UpdateModelPublished;
use ChangesModelOrder;
/**
* 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();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$productModels,
$search,
null,
'label',
'description',
);
}
$searchFields = \implode(', ', [
Str::ucfirst(trans('validation.custom.label')),
trans('validation.custom.description'),
]);
/** Sort columns with a query */
$this->sortQuery($productModels);
/** Custom pagination */
$productModels = $this->paginate($productModels);
return view('bo.pages.products.index', compact('productModels', 'search', 'searchFields'));
}
/**
* Show the form for creating a new resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function create(Product $product): \Illuminate\Contracts\View\View
{
$rangeModels = Range::all();
$pictoModels = Picto::all();
return view('bo.pages.products.create', [
'productModel' => $product,
'rangeModels' => $rangeModels,
'pictoModels' => $pictoModels
]);
}
/**
* 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->safe()->except(['pictos']));
if ($product->saveOrFail()) {
$product->pictos()->attach(collect($request->validated('pictos'))->pluck('id'));
return redirect()->route('bo.products.edit', $product)
->with('success', __('crud.messages.has_been_created', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_created', [
'model' => Str::ucfirst(__('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();
$pictoModels = Picto::all();
return view('bo.pages.products.edit', [
'productModel' => $product,
'rangeModels' => $rangeModels,
'pictoModels' => $pictoModels
]);
}
/**
* 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->safe()->except(['pictos']));
$product->pictos()->sync(collect($request->validated('pictos'))->pluck('id'));
if ($product->saveOrFail()) {
return redirect()->route('bo.products.edit', $product)
->with('success', __('crud.messages.has_been_updated', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->route('bo.products.edit', $product)
->with('error', __('crud.messages.cannot_be_updated', [
'model' => Str::ucfirst(__('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) {
$product->pictos()->detach();
if ($product->deleteOrFail()) {
return redirect()->route('bo.products.index')
->with('success', __('crud.messages.has_been_deleted', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
}
return redirect()->back()
->with('error', __('crud.messages.cannot_be_deleted', [
'model' => Str::ucfirst(__('models.classes.product'))
]));
});
}
/**
* Duplicate the specified resource.
*
* @param \App\Models\Product $product
* @return \Illuminate\Contracts\View\View
*/
public function duplicate(Product $product): \Illuminate\Contracts\View\View
{
return $this->create($product->replicate());
}
}

View file

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

View file

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

View file

@ -0,0 +1,169 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Users\StoreUserRequest;
use App\Http\Requests\Bo\Users\UpdateUserRequest;
use App\Models\User;
use App\Traits\Controllers\UpdateModelPublished;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class UserController extends Controller
{
use UpdateModelPublished;
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(User::class);
}
/**
* Display a listing of the resource.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Contracts\View\View
*/
public function index(Request $request): \Illuminate\Contracts\View\View
{
/** @var \Illuminate\Database\Eloquent\Builder $userModels */
$userModels = User::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$userModels,
$search,
null,
'first_name',
'last_name',
'email',
'role',
);
}
$searchFields = \implode(', ', [
trans('validation.attributes.first_name'),
trans('validation.attributes.last_name'),
trans('validation.attributes.email'),
trans('validation.attributes.role'),
]);
/** Sort columns with a query */
$this->sortQuery($userModels);
/** Custom pagination */
$userModels = $this->paginate($userModels);
return view('bo.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('bo.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('bo.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,241 @@
<?php
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\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 ValidatesRequests;
/**
* Gives the redirection path for FO Auth controllers
*
* @return string
*/
protected function authLoggedRedirectPath(): string
{
return response()->redirectToRoute('fo.klub.home')->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->getModel()->has(Str::remove('_id', $field))) {
$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
{
if (
count($query->getModel()->getCasts()) and // Check if the model has casts.
isset($query->getModel()->getCasts()[$field]) and // Check if there is cast about specific field.
enum_exists($query->getModel()->getCasts()[$field]) // Check if the cast is an enum.
) {
$cases = collect($query->getModel()->getCasts()[$field]::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 (
!isset($ignore) or
(isset($ignore) and !in_array(request()->sort_col, $ignore))
) {
$query = $query->orderBy(\sprintf("$table.%s", request()->sort_col), request()->sort_way);
}
} elseif (Schema::hasColumn($table, 'order')) {
$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

@ -0,0 +1,47 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\View\View;
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
*/
public function showConfirmForm(): View
{
if (Auth::guard('frontend')->check()) {
return \response()->redirectToRoute('home');
}
return view('fo.auth.passwords.confirm');
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return $this->authLoggedRedirectPath();
}
}

View file

@ -0,0 +1,85 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Contracts\Auth\PasswordBroker;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Password;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails;
/**
* Message to user
*
* @var string
* @phpcs:disable Generic.Files.LineLength.TooLong
*/
private $message = 'Si votre compte existe vous devriez recevoir un message avec un lien pour réinitialiser votre mot de passe';
//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('fo.auth.passwords.email');
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker(): PasswordBroker
{
return Password::broker('customers');
}
/**
* Get the response for a successful password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe.
*
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkResponse()
{
return back()->with('info', trans($this->message));
}
/**
* Get the response for a failed password reset link.
* ! Dans les deux cas on ne divulgue pas si un user existe.
*
* @param \Illuminate\Http\Request $request
* @param string $response
* @return \Illuminate\Http\RedirectResponse
*/
protected function sendResetLinkFailedResponse(Request $request, string $response)
{
// ! Ne pas notifier que l'utilisateur n'existe pas.
if ($response === 'passwords.user') {
return back()->with('info', trans($this->message));
}
// * Renvoyer les autres message d'erreur style throttle.
return back()
->withInput($request->only('email'))
->withErrors(['email' => trans($response)]);
}
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Fo\Klub\MesBrController;
use App\Models\Game;
use App\Providers\RouteServiceProvider;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Foundation\Auth\ThrottlesLogins;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
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
{
// * Fetch Coupon list (campaign wall)
$brList = Cache::remember('allbrs', 60, fn () => MesBrController::getStaticBr());
$gameModel = Game::query()->where('published', true)->first();
return view('fo.auth.login', compact('brList', 'gameModel'));
}
/**
* The user has been authenticated.
*
* @param \Illuminate\Http\Request $request
* @param \Illuminate\Database\Eloquent\Model $user
* @return void
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing, Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
*/
protected function authenticated(Request $request, Model $user): void
{
$user->last_connection = \now();
$user->saveOrFail();
Session::flash('info', trans('Vous êtes à présent connecté'));
}
/**
* 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('Vous êtes à présent déconnecté'));
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return response()->redirectToIntended()->getTargetUrl();
}
}

View file

@ -0,0 +1,190 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\CreateAccountRequest;
use App\Models\Customer;
use App\Models\StaticPage;
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;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Enums\Country;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as ExceptionsValidationException;
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()
{
$staticPageModel = StaticPage::query()->where('type', StaticPageType::klub_register)->first();
return view('fo.auth.register', compact('staticPageModel'));
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
* @throws \Illuminate\Validation\ValidationException If data is incorrect.
*/
protected function validator(array $data): ValidationValidator
{
$data['ok_cgv'] = \request()->boolean('ok_cgv');
$data['phone'] = $data['phone'] ?? null;
$data['opt_in'] = ($data['opt_in'] ?? false) ? true : false;
$request = new CreateAccountRequest();
$validateData = Validator::make(
$data,
$request->rules(),
$request->messages(),
$request->attributes()
);
if ($validateData) {
return $validateData;
} else {
try {
(new CrmCustomer())->fill($this->getOnlyCrmData($data));
} catch (ExceptionsValidationException $e) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
}
}
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\Customer
* @throws \Illuminate\Validation\ValidationException If there are validation errors.
* @throws \RuntimeException On unhandled api Error.
*/
protected function create(array $data): Customer
{
$data['country'] = Country::france();
$data['ok_cgv'] = \request()->boolean('ok_cgv');
$data['phone'] = $data['phone'] ?? null;
$data['opt_in'] = ($data['opt_in'] ?? false) ? true : false;
try {
return DB::transaction(function () use ($data) {
$crmCustomer = new CrmCustomer($this->getOnlyCrmData($data));
// * Country Is forced to france
$crmCustomer->country = Country::france();
$crmCustomer->save();
// * Enregistrement du Hashcode
/** @var \App\Models\Customer $customer */
$customer = new Customer(
Arr::only($data, (new \ReflectionClass(\App\Models\Customer::class))
->getProperty('fillable')->getDefaultValue())
);
$customer->hashcode = $crmCustomer->hashcode;
// ! 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('Votre compte à bien été crée, vous devez valider votre adresse email, un email de confirmation vous à été envoyé.'));
}
return $customer;
});
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::customerExists())) {
throw ValidationException::withMessages([
'error' => 'Un compte est déjà associé à cette adresse email.'
]);
}
if ($e->getApiError()->equals(ApiError::validationFailed())) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
}
report($e);
Log::error(__CLASS__ . '::create reached end of possibilities.');
throw new \RuntimeException(__CLASS__ . '::create reached end of possibilities.');
} catch (ExceptionsValidationException $e) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
} //end try
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath()
{
return $this->authLoggedRedirectPath();
}
/**
* Get only data used in crm
*
* @param array $data
* @return array
*/
private function getOnlyCrmData(array $data): array
{
return \array_intersect_key(
$data,
[
'firstname' => '',
'lastname' => '',
'birth_date' => '',
'address' => '',
'postcode' => '',
'country' => '',
'city' => '',
'phone' => '',
'email' => '',
'password' => '',
'opt_in' => '',
]
);
}
}

View file

@ -0,0 +1,124 @@
<?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\Contracts\View\View;
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');
}
/**
* 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\Factory|\Illuminate\View\View
*/
public function showResetForm(Request $request): View
{
$token = $request->route()->parameter('token');
return view('fo.auth.passwords.reset')->with(
['token' => $token, 'email' => $request->email]
);
}
/**
* Reset the given user's password.
*
* @param mixed $customer
* @param mixed $password
* @return void
* @throws \Illuminate\Validation\ValidationException If something goes wrong.
*/
protected function resetPassword(mixed $customer, mixed $password): void
{
/** @var \App\Models\Customer\Customer $user */
/** @var string $password */
/** @var \LDC\LDCCRM\CrmApiV2 */
$crm = app('crmAPI');
try {
if (!($crmCustomer = $crm->getCustomer($customer->hashcode))) {
throw ValidationException::withMessages([
'error' => 'Changement impossible, compte inexistant.'
]);
}
$crmCustomer->password = $password;
$crmCustomer->save();
} catch (\TypeError | CrmException | ApiErrorException $e) {
report($e);
throw ValidationException::withMessages([
'error' => 'Une erreur est survenue, merci de réessayer ultérieurement.'
]);
} catch (ExceptionsValidationException $e) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
}
$customer->setRememberToken(Str::random(60));
$customer->save();
event(new PasswordReset($customer));
$this->guard()->login($customer);
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return $this->authLoggedRedirectPath();
}
/**
* Get the broker to be used during password reset.
*
* @return \Illuminate\Contracts\Auth\PasswordBroker
*/
public function broker(): PasswordBroker
{
return Password::broker('customers');
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers\Fo\Auth;
use App\Http\Controllers\Controller;
use App\Models\Customer;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Auth\Events\Verified;
use Illuminate\Contracts\View\View;
use Illuminate\Foundation\Auth\VerifiesEmails;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException;
class VerificationController extends Controller
{
use VerifiesEmails;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth:frontend');
$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('fo.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 = $request->user('frontend');
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(
'Votre compte est bien activé'
));
}
/**
* Get the post register / login redirect path.
*
* @return string
*/
public function redirectPath(): string
{
return $this->authLoggedRedirectPath();
}
/**
* 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(
'Une erreur est survenue'
))->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(
'Une erreur est survenue'
))->throwResponse();
}
}
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\CommitmentPage;
use App\Models\LocalCommitment;
use App\Models\StaticPage;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class CommitmentController extends Controller
{
/**
* Show a specific resource.
*
* @return \Illuminate\Contracts\View\View
*/
public function show(): \Illuminate\Contracts\View\View
{
// * There should be only one always (Should be a static page nonetheless)
$commitmentPage = CommitmentPage::query()
->with('pictos', fn (BelongsToMany $q) => $q->where('published', true))->firstOrFail();
/** @var \App\Models\StaticPage $staticPageModel */
$staticPage = StaticPage::query()->where('type', StaticPageType::commitments)->firstOrFail();
/** @var \App\Models\LocalCommitment $localCommitmentModel */
$localCommitments = LocalCommitment::query()
->where('published', true)
->limit(2)->get();
return view('fo.pages.commitments', [
'staticPageModel' => $staticPage,
'localCommitmentList' => $localCommitments,
'commitmentPageModel' => $commitmentPage,
]);
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\Contact\ObjectEnum;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\ContactFormRequest;
use App\Mail\SendContactMail;
use App\Models\Product;
use App\Models\StaticPage;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Mail;
class ContactController extends Controller
{
/**
* Display contact from
*
* @return \Illuminate\Contracts\View\View
*/
public function contact(): View
{
$products = Product::all();
$staticPageModel = StaticPage::query()->where('type', StaticPageType::contact)->first();
return view('fo.pages.contact', compact('products', 'staticPageModel'));
}
/**
* Submit contact form
*
* @param \App\Http\Requests\Fo\ContactFormRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function contactSubmit(ContactFormRequest $request): RedirectResponse
{
// * Cast expected types
$validated = $request->validated();
Arr::set($validated, 'type', ObjectEnum::make(Arr::get($validated, 'type')));
Arr::set($validated, 'product', Product::query()
->where('id', Arr::get($validated, 'product'))->first());
$expiration_date = Arr::get($validated, 'expiration_date');
Arr::set($validated, 'expiration_date', $expiration_date ?
Carbon::createFromFormat('Y-m-d', $expiration_date) : null);
Mail::send(new SendContactMail((object)$validated));
return redirect()->route('fo.contact')
->with(
'success',
trans(
'Bonjour, votre message a bien été transmis. Cordialement, l’équipe :app_name.',
[
'app_name' => config('app.name')
]
)
);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\KlubBloc;
use App\Models\News;
use App\Models\Range;
use App\Models\StaticPage;
class HomeController extends Controller
{
/**
* Display the homepage.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
$rangeModels = Range::all();
$klubBloc = KlubBloc::first();
$newsModels = News::query()->where('published', true)->orderBy('created_at', 'desc')->limit(6)->get();
$staticPageModel = StaticPage::query()->where('type', StaticPageType::home)->first();
return view('fo.pages.home', compact('staticPageModel', 'rangeModels', 'klubBloc', 'newsModels'));
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace App\Http\Controllers\Fo\Klub;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\UpdateAccountRequest;
use App\Http\Requests\Fo\Customers\UpdatePasswordRequest;
use App\Models\Customer;
use Illuminate\Auth\Events\PasswordReset;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;
use LDC\LDCCRM\Enums\ApiError;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
class CustomerController extends Controller
{
/**
* Display Klub Customer Homepage
*
* @return \Illuminate\Contracts\View\View
*/
public function profile(): View
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
return view('fo.pages.klub.profile', compact('crmCustomer'));
}
/**
* Update Customer profile
*
* @param \App\Http\Requests\Fo\Customers\UpdateAccountRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function profileUpdate(UpdateAccountRequest $request): RedirectResponse
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
return $this->updateCrmCustomerInfo($customer, $request->validated()) ?
back()->with('success', trans('Vos informations ont bien été enregistrées.')) :
back()->withInput()->withErrors(trans('Une erreur est survenue, merci de réessayer ultérieurement.'));
}
/**
* Show update customer password form
*
* @return \Illuminate\Contracts\View\View|\Illuminate\Contracts\View\Factory
*/
public function password()
{
return view('fo.auth.passwords.change');
}
/**
* Update customer password
*
* @param \App\Http\Requests\Fo\Customers\UpdatePasswordRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function passwordSubmit(UpdatePasswordRequest $request): RedirectResponse
{
/** @var \App\Models\Customer */
$customer = \auth('frontend')->user();
if ($this->updateCrmCustomerInfo($customer, ['password' => $request->get('password')])) {
event(new PasswordReset($customer));
return back()->with('success', trans('Vos informations ont bien été enregistrées.'));
}
return back()->withInput()->withErrors(trans('Une erreur est survenue, merci de réessayer ultérieurement.'));
}
/**
* Updates user info
*
* @param \App\Models\Customer $customer
* @param array $data
* @return boolean
* @throws \Illuminate\Validation\ValidationException If something failed.
*/
private function updateCrmCustomerInfo(Customer $customer, array $data)
{
return DB::transaction(function () use ($customer, $data) {
try {
if (\array_key_exists('email', $data)) {
$customer->email = $data['email'];
$customer->saveOrFail();
}
return $customer->updateApiData($data);
} catch (CrmValidationException $e) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
} catch (ApiErrorException $e) {
if ($e->getApiError()->equals(ApiError::validationFailed())) {
throw ValidationException::withMessages([
'error' => $e->getMessage()
]);
}
report($e);
return false;
} //end try
});
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Controllers\Fo\Klub;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\Customers\CrmQuestionsAnswersRequest;
use App\Models\Customer;
use App\Models\Game;
use App\Models\StaticPage;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use LDC\LDCCRM\Exceptions\ApiErrorException;
use LDC\LDCCRM\Exceptions\CrmException;
use LDC\LDCCRM\Exceptions\ValidationException as CrmValidationException;
use LDC\LDCCRM\Models\Answer;
use LDC\LDCCRM\Models\Question;
class KlubController extends Controller
{
/**
* Display Klub Customer Homepage
*
* @return \Illuminate\Contracts\View\View
*/
public function home(): View
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
// * Fetch Coupon list (campaign wall)
$brList = Cache::remember('allbrs', 60, fn () => MesBrController::getStaticBr());
// * There should always be a published game
$game = Game::query()->where('published', true)->first();
$staticPageModel = StaticPage::query()->where('type', StaticPageType::klub)->first();
return view('fo.pages.klub.home', compact('crmCustomer', 'brList', 'game', 'staticPageModel'));
}
/**
* Display questions
*
* @return \Illuminate\Contracts\View\View
*/
public function questions(): View
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer */
$crmCustomer = $customer->apiData;
$questions = Customer::cacheCrm(
$customer->hashcode,
'questions',
fn () => Question::unansweredBy($crmCustomer)
);
return view('fo.pages.klub.questions', compact('crmCustomer', 'questions'));
}
/**
* Save Customer answers to Crm questions
*
* @param \App\Http\Requests\Fo\Customers\CrmQuestionsAnswersRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function questionsSubmit(CrmQuestionsAnswersRequest $request): RedirectResponse
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer */
$crmCustomer = $customer->apiData;
$questions = Customer::cacheCrm(
$customer->hashcode,
'questions',
fn () => Question::unansweredBy($crmCustomer)
);
$responses = $request->post('question');
// * int cast on ids
\array_walk_recursive($responses, function (&$id) {
$id = intval($id);
});
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
$responses = Arr::mapWithKeys($questions, fn ($question, $index) => [
optional(Arr::get($questions, $index))->id => Arr::get($responses, $index)
]);
try {
foreach ($responses as $repId => $reponse) {
Answer::setQuestionAnswers($customer->hashcode, $repId, Arr::wrap($reponse));
}
} catch (ApiErrorException | CrmValidationException | CrmException $e) {
report($e);
return back()->withErrors(trans('Une erreur est survenue, merci de réessayer ultérieurement.'));
} finally {
// * Clear Customer cache
Customer::clearCrmCached($customer->hashcode, 'questions');
}
return \response()->redirectToRoute('fo.klub.home')
->with('success', trans(
'Merci pour vos réponses, nous les avons bien pris en compte, vous pouvez maintenant profiter du Klub.'
));
}
}

View file

@ -0,0 +1,274 @@
<?php
namespace App\Http\Controllers\Fo\Klub;
use App\Http\Controllers\Controller;
use App\Http\Requests\Fo\MesBr\PrintCouponsRequest;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Symfony\Component\HttpFoundation\StreamedResponse;
class MesBrController extends Controller
{
/**
* Show customer BR list
*
* @return \Illuminate\Contracts\View\View
*/
public function showCustomerBrList(): View
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer $crmCustomer */
$crmCustomer = $customer->apiData;
$mesBr = $this->fetchActiveBr($crmCustomer);
return view('fo.pages.klub.mesbr', compact('crmCustomer', 'mesBr'));
}
/**
* Fetch PDF BR
*
* @param \App\Http\Requests\Fo\MesBr\PrintCouponsRequest $request
* @return \Symfony\Component\HttpFoundation\StreamedResponse
*/
public function fetchBrPdf(PrintCouponsRequest $request): StreamedResponse
{
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
$pdf = $this->fetchBrPdfFromMesBr($customer, $request->post('coupon'));
if ($pdf === null) {
\back()->withErrors([
'coupon' => trans('Impossible de récupérer le fichier pdf')
])->throwResponse();
}
return response()->streamDownload(function () use ($pdf) {
echo $pdf;
}, trans('mes-bons-de-reduction-:date.pdf', [
'date' => now()->isoFormat('DD-MM-Y-HH\hmm')
]), [
'Cache-Control' => 'public, must-revalidate, max-age=0',
'Content-Description' => 'File Transfer',
'Content-Type' => 'application/pdf',
'Expires' => 'Sat, 26 Jul 1997 05:00:00 GMT',
'Last-Modified' => gmdate('D, d M Y H:i:s') . ' GMT',
// For glory.
'X-Generator' => 'mPDF'
]);
}
/**
* Fetch coupons PDF
*
* @param \App\Models\Customer $customer
* @param array $coupons
* @return string|null
*/
private function fetchBrPdfFromMesBr(\App\Models\Customer $customer, array $coupons): string|null
{
/** @var \LDCWEB\MesBR */
$mesBr = app('mesbr');
$message = null;
try {
$res = $mesBr->printCoupons($customer->hashcode, $coupons);
if (!$res) {
$message = "ERROR MesBR API : printCoupons on api failed {$customer->hashcode}";
} elseif (Arr::get((array)$res, 'status') === 'KO') {
$message = Arr::get(
(array)$res,
'message',
"ERROR MesBR API : printCoupons on api failed (no message) {$customer->hashcode}"
);
}
if ($message === null) {
return \base64_decode(strval(optional($res)->pdf));
}
report(new \RuntimeException($message));
return null;
} catch (\Exception $e) {
report($e);
return null;
} //end try
}
/**
* Fetch customer BRs
*
* This should be an object like
*
* +"status": "OK"
* +"name": "Test tregalette"
* +"limited_printing": 0
* +"total": 15
* +"available_amount": 10.5
* +"total_amount": 10.5
* +"coupons": array{
* +"code": "2950675055365"
* +"name": "Webcoupon 1€ Filet de canard Confit"
* +"value": 1
* +"used": false
* +"desktop_image_available": "https://dev.mesbr.eu/coupons/96kjV1ZwGb7Z4m8wyzBCuurEFPlSLHFaAGVWuO8F.png"
* +"desktop_image_unavailable": "https://dev.mesbr.eu/coupons/K7BSVFOCs2K1XCi78wR2YgEfGyFH6AJthyupTEYz.png"
* +"mobile_image_available": "https://dev.mesbr.eu/coupons/KE3YodVeDPSG7pVzPQZhPEpVGlxwjTPWCJ8WGN29.png"
* +"mobile_image_unavailable": "https://dev.mesbr.eu/coupons/FOFhdT0d2X1AURQfYm9O7ac2ca9SGUk3IHCTWJiZ.png"
* }
*
* @param \LDC\LDCCRM\Models\Customer $customer
* @return object|null
*/
private function fetchActiveBr(\LDC\LDCCRM\Models\Customer $customer): object|null
{
if (!$this->createOrUpdateMesBrCustomer($customer)) {
return null;
}
/** @var \LDCWEB\MesBR */
$mesBr = app('mesbr');
$message = null;
try {
$res = $mesBr->getMurCustomer($customer->hashcode);
if (!$res) {
$message = "ERROR MesBR API : getMurCustomer on api failed {$customer->hashcode}";
} elseif (Arr::get((array)$res, 'status') === 'KO') {
$message = Arr::get(
(array)$res,
'message',
"ERROR MesBR API : getMurCustomer on api failed (no message) {$customer->hashcode}"
);
}
if ($message === null) {
// * Replace local picture urls for dev Env
if (config('app.env') === 'local') {
$res = self::replaceCouponsPictureHost($res);
}
return $res;
}
report(new \RuntimeException($message));
return null;
} catch (\Exception $e) {
report($e);
return null;
} //end try
}
/**
* Report for sure true if the customer is set in mesBr
*
* @param \LDC\LDCCRM\Models\Customer $customer
* @return boolean
*/
private function createOrUpdateMesBrCustomer(\LDC\LDCCRM\Models\Customer $customer): bool
{
// * Ajout du customer sur mes BR
/** @var \LDCWEB\MesBR */
$mesBr = app('mesbr');
$message = null;
/**
* Observed behavior,
* calling multiple times createCustomer results in
* create or update the customer.
*/
try {
// * Create (or update) the customer
$res = $mesBr->createCustomer($customer->hashcode, $customer->email, 'France');
if (!$res) {
$message = "ERROR MesBR API : createCustomer on api failed {$customer->hashcode}";
} elseif (Arr::get((array)$res, 'status') === 'KO') {
$message = Arr::get(
(array)$res,
'message',
"ERROR MesBR API : createCustomer on api failed (no message) {$customer->hashcode}"
);
}
if ($message === null) {
return true;
}
report(new \RuntimeException($message));
return false;
} catch (\Exception $e) {
report($e);
return false;
} //end try
}
// * STATIC Fetch BR
/**
* Static method to fetch br List
*
* ! see fetchActiveBr for output object definition
*
* @return object|null
*/
public static function getStaticBr(): object|null
{
/** @var \LDCWEB\MesBR */
$mesBr = app('mesbr');
$message = null;
try {
$res = $mesBr->getMur();
if (!$res) {
$message = 'ERROR MesBR API : getMur on api failed';
} elseif (Arr::get((array)$res, 'status') === 'KO') {
$message = Arr::get(
(array)$res,
'message',
'ERROR MesBR API : getMur on api failed (no message)'
);
}
if ($message === null) {
// * Replace local picture urls for dev Env
if (config('app.env') === 'local') {
$res = self::replaceCouponsPictureHost($res);
}
return $res;
}
report(new \RuntimeException($message));
return null;
} catch (\Exception $e) {
report($e);
return null;
} //end try
}
/**
* Replace coupons picture host
*
* @param object $res
* @return object
*/
private static function replaceCouponsPictureHost(object $res): object
{
$replaceUrl = fn (?string $url) => $url ? Str::replace(
'https://dev.mesbr.eu',
config('mesbr.endpoint'),
$url
) : $url;
$res->coupons = collect($res->coupons)->map(fn (object $coupon) =>
(object)array_merge((array)$coupon, [
'desktop_image_available' => $replaceUrl($coupon->desktop_image_available),
'desktop_image_unavailable' => $replaceUrl($coupon->desktop_image_unavailable),
'mobile_image_available' => $replaceUrl($coupon->mobile_image_available),
'mobile_image_unavailable' => $replaceUrl($coupon->mobile_image_unavailable),
]))->all();
return $res;
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Http\Controllers\Controller;
use App\Lib\Helpers\CollectionHelper;
use App\Models\KlubBloc;
use App\Models\Product;
use App\Models\Range;
class ProductController extends Controller
{
/**
* Show a specific resource.
*
* @param string $slug
* @return \Illuminate\Contracts\View\View
*/
public function show(string $slug): \Illuminate\Contracts\View\View
{
$productModel = Product::query()->where('slug', $slug)->where('published', true)->firstOrFail();
$productModels = Product::query()->where('slug', '!=', $slug)->where('published', true)->get()->random(2);
$klubBloc = KlubBloc::first();
$rangeModels = Range::query()->whereNot('id', $productModel->range->id)->get()->random(2);
return view('fo.pages.products.show', compact('productModel', 'productModels', 'klubBloc', 'rangeModels'));
}
/**
* Get filtered products.
*
* @return \Illuminate\Http\JsonResponse
*/
public function productsFiltered(): \Illuminate\Http\JsonResponse
{
$productModels = Product::query()
->where('published', true)
->where('range_id', request()->range_id)
->orderBy('updated_at', 'desc')
->get();
$productModels = CollectionHelper::paginateManually($productModels, 9, request()->page);
return response()->json($productModels);
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\News;
use App\Models\Product;
use App\Models\Range;
use App\Models\StaticPage;
class RangeController extends Controller
{
/**
* Display the homepage.
*
* @return \Illuminate\Contracts\View\View
*/
public function index(): \Illuminate\Contracts\View\View
{
$rangeModels = Range::query()->get();
$staticPageModel = StaticPage::query()->where('type', StaticPageType::ranges)->first();
return view('fo.pages.ranges.index', compact('rangeModels', 'staticPageModel'));
}
/**
* Show a specific resource.
*
* @param string $slug
* @return \Illuminate\Contracts\View\View
*/
public function show(string $slug): \Illuminate\Contracts\View\View
{
$rangeModel = Range::query()->where('slug', $slug)->firstOrFail();
$rangeModels = Range::query()->where('slug', '!=', $slug)->get();
$productModels = Product::query()->where('range_id', $rangeModel->id)
->where('published', true)
->orderBy('order', 'asc')
->paginate(9);
$staticPageModel = StaticPage::query()->where('type', StaticPageType::range)->first();
$newsModels = News::query()
->where('published', true)
->orderBy('created_at', 'desc')
->limit(6)
->get();
return view('fo.pages.ranges.show', compact(
'rangeModel',
'rangeModels',
'productModels',
'staticPageModel',
'newsModels'
));
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace App\Http\Controllers\Fo;
use App\Enums\StaticPageType;
use App\Http\Controllers\Controller;
use App\Models\HistoricalMilestone;
use App\Models\News;
use App\Models\Range;
use App\Models\StaticPage;
class StaticPageController extends Controller
{
/**
* Legal show
*
* @param \App\Models\StaticPage $legalPage
* @return \Illuminate\Contracts\View\View
*/
public function legals(StaticPage $legalPage)
{
$legalPage = StaticPage::query()->where('type', StaticPageType::legals)->get();
return view('fo.pages.legals', ['staticPageModel' => $legalPage[0]]);
}
/**
* Policies show
*
* @param \App\Models\StaticPage $policiesPage
* @return \Illuminate\Contracts\View\View
*/
public function policies(StaticPage $policiesPage)
{
$policiesPage = StaticPage::query()->where('type', StaticPageType::confidentiality)->get();
return view('fo.pages.legals', ['staticPageModel' => $policiesPage[0]]);
}
/**
* HistoricalMilestone show
*
* @return \Illuminate\Contracts\View\View
*/
public function ourHistory()
{
$newsModels = News::query()
->where('published', true)
->orderBy('created_at', 'desc')
->limit(6)
->get();
$rangeModels = Range::all();
$milestoneModels = HistoricalMilestone::orderBy('year', 'asc')->where('published', true)->get();
$staticPageModel = StaticPage::query()->where('type', StaticPageType::history)->first();
return view('fo.pages.history', compact('staticPageModel', 'newsModels', 'rangeModels', 'milestoneModels'));
}
}

70
app/Http/Kernel.php Normal file
View file

@ -0,0 +1,70 @@
<?php
namespace App\Http;
use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
* These middleware are run during every request to your application.
*
* @var array<int, class-string|string>
*/
protected $middleware = [
\App\Http\Middleware\TrustHosts::class,
\App\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class,
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
/**
* The application's route middleware groups.
*
* @var array<string, array<int, class-string|string>>
*/
protected $middlewareGroups = [
'web' => [
'htmlsanitize',
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class
],
'api' => [
\Illuminate\Routing\Middleware\ThrottleRequests::class . ':api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array<string, class-string|string>
*/
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,
'crm_questions' => \App\Http\Middleware\CrmMiddlewareQuestions::class,
];
}

View file

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

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Middleware;
use App\Models\Customer;
use Illuminate\Http\Request;
use LDC\LDCCRM\Models\Question;
use Symfony\Component\HttpFoundation\Response;
class CrmMiddlewareQuestions
{
/**
* 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
/** @var \App\Models\Customer|null $user */
if (
\auth('frontend')->check() and
$user = \auth('frontend')->user() and
$user->last_connection !== null
) {
/** @var \LDC\LDCCRM\Models\Customer */
$crmCustomer = $user->apiData;
$questions = Customer::cacheCrm(
$user->hashcode,
'questions',
fn () => Question::unansweredBy($crmCustomer)
);
if (count($questions)) {
return \response()->redirectToRoute('fo.klub.questions');
}
}
return $next($request);
}
}

View file

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

View file

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

View file

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

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
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
*/
public function handle(Request $request, \Closure $next, ...$guards): Response
{
// 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;
}
return \redirect()->route('bo.dashboard');
default:
throw new \RuntimeException("Unhandled guard `{$guard}` .");
}
}
}
return $next($request);
}
}

View file

@ -0,0 +1,118 @@
<?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
{
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var array
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
/**
* 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
// * For Vite DEV
$host = \parse_url(\config('app.url'), \PHP_URL_HOST);
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;');
$cspPolicies = [
"default-src 'none';",
"manifest-src 'self';",
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' " . implode(' ', [
// * VITE DEV
config('app.debug') ? "wss://{$host}:4443" : '',
config('app.debug') ? "https://{$host}:4443" : '',
'https://yl.s-web.fr'
]) . ';',
"img-src * data: blob:;",
'frame-src ' . implode(' ', [
'https://www.google.com/recaptcha/',
'https://recaptcha.google.com/recaptcha/',
'https://www.google.com/maps/'
]) . ';',
"font-src 'self' data: " . \implode(' ', [
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
'https://fonts.gstatic.com;'
]) . ';',
// ! Using CKEditor will must have unsafe-inline style
// phpcs:ignore Generic.Files.LineLength.TooLong
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
'https://cdnjs.cloudflare.com',
// * File Manager
'https://fonts.googleapis.com',
'https://cdn.jsdelivr.net',
'https://use.fontawesome.com',
'https://fonts.googleapis.com',
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
]) . ';',
// ! If you dont use ckeditor use this
// "style-src 'self' 'nonce-$nonce';",
// phpcs:ignore Generic.Files.LineLength.TooLong
"script-src 'self' {$jsDev} " . \implode(' ', [
'https: blob:',
'https://www.googletagmanager.com',
'https://cdnjs.cloudflare.com',
"$scheme://www.google-analytics.com",
// * VITE DEV
config('app.debug') ? "https://{$host}:4443" : '',
]) . ';'
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\';');
return $response;
}
/**
* Remove unwanted headers
*
* @param array $headerList
* @return void
*/
private function removeUnwantedHeaders(array $headerList): void
{
foreach ($headerList as $header) {
header_remove($header);
}
}
}

View file

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

View file

@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array<int, string|null>
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View file

@ -0,0 +1,28 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array<int, string>|string|null
*/
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;
}

View file

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

View file

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

View file

@ -0,0 +1,60 @@
<?php
namespace App\Http\Requests\Bo\CommitmentPages;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCommitmentPageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'quality_title' => 'required|string|between:3,255',
'quality_text' => 'required|string|min:15',
'product1_title' => 'required|string|between:3,255',
'product1_text' => 'required|string|min:15',
'product2_title' => 'required|string|between:3,255',
'product2_text' => 'required|string|min:15',
'environment_title' => 'required|string|between:3,255',
'environment_text' => 'required|string|min:15',
'pictos' => 'nullable|array',
'pictos.*.id' => 'required|exists:pictos,id',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'quality_title' => trans('validation.custom.quality_title'),
'quality_text' => trans('validation.custom.quality_text'),
'product1_title' => trans('validation.custom.productx_title', ['x' => 1]),
'product1_text' => trans('validation.custom.productx_text', ['x' => 1]),
'product2_title' => trans('validation.custom.productx_title', ['x' => 2]),
'product2_text' => trans('validation.custom.productx_text', ['x' => 2]),
'environment_title' => trans('validation.custom.environment_title'),
'environment_text' => trans('validation.custom.environment_text'),
'pictos' => trans('validation.custom.picto'),
'pictos.*.id' => trans('validation.custom.picto_id'),
];
}
}

View file

@ -0,0 +1,119 @@
<?php
namespace App\Http\Requests\Bo\Games;
use App\Traits\Requests\HasPicture;
use Closure;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class StoreGameRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false,
'button_status' => $this->button_status ? true : false,
'button_new_tab' => $this->button_status ? true : false,
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'title_line1' => 'required|string|between:3,255',
'title_line2' => 'required|string|between:3,255',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'text' => [
'required',
'string',
'min:15',
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function (string $attribute, mixed $value, Closure $fail) {
// phpcs:enable
if (Str::wordCount(\strip_tags($value)) > 32) {
$fail(trans('validation.max_words', [
'attribute' => trans('validation.attributes.text'),
'count' => '32'
]));
}
}
],
'button_status' => 'required|boolean',
'button_label' => 'required|string|between:3,255',
'button_url' => 'required|url',
'button_new_tab' => 'required|boolean',
'published' => 'required|boolean',
'publish_at' => 'nullable|date',
'unpublish_at' => 'nullable|date|after:publish_at',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 585, 415, 585, 415, false)
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'title_line1' => trans('validation.custom.title_line1'),
'title_line2' => trans('validation.custom.title_line2'),
'picture_alt' => trans('validation.custom.attribute_alt'),
'picture_title' => trans('validation.custom.attribute_title'),
'text' => trans('validation.attributes.text'),
'button_status' => trans(':field1 :inter:field2', [
'field1' => trans('crud.minicrud.visibility_label'),
'inter' => trans('validation.custom.of_the.male'),
'field2' => trans('validation.custom.button'),
]),
'button_label' => trans(':field :inter:model', [
'field' => trans('validation.custom.label'),
'inter' => trans('validation.custom.of_the.male'),
'model' => trans('validation.custom.button'),
]),
'button_url' => trans(':field :inter:model', [
'field' => trans('validation.custom.url'),
'inter' => trans('validation.custom.of_the.male'),
'model' => trans('validation.custom.button'),
]),
'button_new_tab' => trans(':field1 :inter:field2', [
'field1' => trans('validation.custom.target'),
'inter' => trans('validation.custom.of_the.male'),
'field2' => trans('validation.custom.button'),
]),
'published' => trans('validation.custom.activated'),
'publish_at' => trans('validation.custom.publish_at'),
'unpublish_at' => trans('validation.custom.unpublish_at'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Games;
class UpdateGameRequest extends StoreGameRequest
{
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Requests\Bo\HistoricalMilestones;
use App\Traits\Requests\HasPicture;
use Closure;
use Illuminate\Support\Str;
use Illuminate\Foundation\Http\FormRequest;
class StoreHistoricalMilestoneRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false,
'button_status' => $this->button_status ? true : false,
'button_new_tab' => $this->button_new_tab ? true : false
]);
$this->mergePicture('picture1');
$this->mergePicture('picture2');
$this->mergePicture('picture3');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'year' => 'required|integer|min:1901|max:2155',
'text' => [
'required',
'string',
'min:10',
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function (string $attribute, mixed $value, Closure $fail) {
// phpcs:enable
if (Str::wordCount(\strip_tags($value)) > 15) {
$fail(trans('validation.max_words', [
'attribute' => trans('validation.attributes.text'),
'count' => '15'
]));
}
}
],
'picture1_alt' => 'required|string|between:3,255',
'picture1_title' => 'required|string|between:3,255',
'picture2_alt' => 'required_with:picture2|nullable|string|between:3,255',
'picture2_title' => 'required_with:picture2|nullable|string|between:3,255',
'picture3_alt' => 'required_with:picture3|nullable|string|between:3,255',
'picture3_title' => 'required_with:picture3|nullable|string|between:3,255',
'button_label' => 'required_if:button_status,true|nullable|string|between:3,35',
'button_url' => 'required_if:button_status,true|nullable|string|between:3,255',
'button_status' => 'required|boolean',
'button_new_tab' => 'required|boolean',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture1', 431, 300, 431, 300, false),
$this->pictureRules('picture2', 431, 300, 431, 300, true),
$this->pictureRules('picture3', 431, 300, 431, 300, true),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'year' => trans('validation.attributes.quality_title'),
'text' => trans('validation.custom.text'),
'picture1_alt' => trans('validation.custom.picturex_alt', ['x' => 1]),
'picture1_title' => trans('validation.custom.picturex_title', ['x' => 1]),
'picture2_alt' => trans('validation.custom.picturex_alt', ['x' => 2]),
'picture2_title' => trans('validation.custom.picturex_title', ['x' => 2]),
'picture3_alt' => trans('validation.custom.picturex_alt', ['x' => 3]),
'picture3_title' => trans('validation.custom.picturex_title', ['x' => 3]),
'button_label' => trans('validation.custom.button_label'),
'button_url' => trans('validation.custom.button_url'),
'button_status' => trans('validation.custom.button_status'),
'button_new_tab' => trans('validation.custom.button_new_tab'),
'published' => trans('validation.custom.published'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\HistoricalMilestones;
class UpdateHistoricalMilestoneRequest extends StoreHistoricalMilestoneRequest
{
}

View file

@ -0,0 +1,62 @@
<?php
namespace App\Http\Requests\Bo\KlubBlocs;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class UpdateKlubBlocRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 640, 637, 640, 637, false)
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'picture_alt' => trans('validation.custom.attribute_alt'),
'picture_title' => trans('validation.custom.attribute_title'),
];
}
}

View file

@ -0,0 +1,107 @@
<?php
namespace App\Http\Requests\Bo\LocalCommitments;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreLocalCommitmentRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture1');
$this->mergePicture('picture2');
$this->mergePicture('picture3');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'title' => 'required|string|between:3,255',
'text' => 'required|string|min:15',
'picture1_alt' => 'required|string|between:3,255',
'picture1_title' => 'required|string|between:3,255',
'picture2_alt' => 'nullable|required_with:picture2|string|between:3,255',
'picture2_title' => 'nullable|required_with:picture2|string|between:3,255',
'picture3_alt' => 'nullable|required_with:picture3|string|between:3,255',
'picture3_title' => 'nullable|required_with:picture3|string|between:3,255',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture1', 587, 429, 587, 429, false),
$this->pictureRules('picture2', 587, 429, 587, 429, true),
$this->pictureRules('picture3', 587, 429, 587, 429, true),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'title' => trans('validation.attributes.title'),
'text' => trans('validation.attributes.text.'),
'picture1_alt' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_alt'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'picture1_title' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_title'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'picture2_alt' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_alt'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'picture2_title' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_title'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'picture3_alt' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_alt'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'picture3_title' => trans(':field1 :inter:model', [
'field1' => trans('validation.custom.attribute_title'),
'inter' => trans('validation.custom.of_the.female'),
'field2' => trans('validation.custom._image')
]),
'published' => trans('validation.custom.activated'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\LocalCommitments;
class UpdateLocalCommitmentRequest extends StoreLocalCommitmentRequest
{
}

View file

@ -0,0 +1,89 @@
<?php
namespace App\Http\Requests\Bo\News;
use App\Traits\Requests\HasPicture;
use Closure;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class StoreNewsRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'text' => [
'required',
'string',
'min:3',
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function (string $attribute, mixed $value, Closure $fail) {
// phpcs:enable
if (Str::wordCount(\strip_tags($value)) > 80) {
$fail(trans('validation.max_words', [
'attribute' => trans('validation.attributes.text'),
'count' => '80'
]));
}
}
],
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'published' => 'required|boolean',
'publish_at' => 'nullable|date',
'unpublish_at' => 'nullable|date|after:publish_at',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 308, 223, 308, 223, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'text' => trans('validation.attributes.text'),
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'published' => trans('validation.custom.activated'),
'publish_at' => trans('validation.custom.publish_at'),
'unpublish_at' => trans('validation.custom.unpublish_at'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\News;
class UpdateNewsRequest extends StoreNewsRequest
{
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Http\Requests\Bo\Pictos;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StorePictoRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'label' => 'required|string|between:3,255',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 85, 85, 85, 85, false)
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'password' => trans('validation.attributes.password'),
'role' => trans('validation.attributes.role'),
'published' => trans('validation.custom.publication')
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Pictos;
class UpdatePictoRequest extends StorePictoRequest
{
}

View file

@ -0,0 +1,102 @@
<?php
namespace App\Http\Requests\Bo\Products;
use App\Enums\Products\NutriscoreEnum;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
class StoreProductRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
$this->mergePicture('picture');
$this->mergePicture('picture_thumbnail');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'seo_title' => 'required|between:45,70',
'seo_description' => 'required|between:50,160',
'label' => 'required|string|between:3,100',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'picture_thumbnail_alt' => 'required|string|between:3,255',
'picture_thumbnail_title' => 'required|string|between:3,255',
'description' => 'required|string|min:15',
'nutriscore' => ['required', new Enum(NutriscoreEnum::class)],
'advice' => 'required|string|min:15',
'ingredients' => 'required|string|min:15',
'nutritionnal_values' => 'required|array',
'nutritionnal_values.100g' => 'required|array',
'nutritionnal_values.100g.*' => 'required|numeric|min:0',
'nutritionnal_values.100g.label' => 'required|string|between:3,255',
'nutritionnal_values.self' => 'required|array',
'nutritionnal_values.self.*' => 'required|numeric|min:0',
'nutritionnal_values.self.label' => 'required|string|between:3,255',
'range_id' => 'required|exists:ranges,id',
'pictos' => 'nullable|array',
'pictos.*.id' => 'required|exists:pictos,id',
'published' => 'required|boolean',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 503, 503, 503, 503, false),
$this->pictureRules('picture_thumbnail', 310, 310, 310, 310, false),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'label' => trans('validation.custom.label'),
'description' => trans('validation.custom.description'),
'picture_alt' => trans('validation.custom.picture_alt'),
'picture_title' => trans('validation.custom.picture_title'),
'picture_thumbnail_alt' => trans('validation.custom.picture_alt'),
'picture_thumbnail_title' => trans('validation.custom.picture_title'),
'nutriscore' => trans('validation.custom.nutriscore'),
'advice' => trans('validation.custom.advice'),
'ingredients' => trans('validation.custom.ingredients'),
'nutritionnal_values' => trans('validation.custom.nutritionnal_values'),
'range_id' => trans('models.classes.range'),
'pictos' => trans('validation.custom.picto'),
'pictos.*.id' => trans('validation.custom.picto_id'),
'published' => trans('validation.custom.activated'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Products;
class UpdateProductRequest extends StoreProductRequest
{
}

View file

@ -0,0 +1,89 @@
<?php
namespace App\Http\Requests\Bo\Ranges;
use App\Traits\Requests\HasPicture;
use Illuminate\Foundation\Http\FormRequest;
class StoreRangeRequest extends FormRequest
{
use HasPicture;
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->mergePicture('picture');
$this->mergePicture('picture_ambiance');
$this->mergePicture('picto1_picture');
$this->mergePicture('picto2_picture');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
$rules = [
'label' => 'required|string|between:3,255',
'picture_alt' => 'required|string|between:3,255',
'picture_title' => 'required|string|between:3,255',
'picture_ambiance_alt' => 'required|string|between:3,255',
'picture_ambiance_title' => 'required|string|between:3,255',
'picto1_picture_alt' => 'required_with:picto1_picture|nullable|string|between:3,255',
'picto1_picture_title' => 'required_with:picto1_picture|nullable|string|between:3,255',
'picto1_line1' => 'required_with:picto1_picture|nullable|string|between:3,15',
'picto1_line2' => 'required_with:picto1_picture|nullable|string|between:3,15',
'picto2_picture_alt' => 'required_with:picto2_picture|nullable|string|between:3,255',
'picto2_picture_title' => 'required_with:picto2_picture|nullable|string|between:3,255',
'picto2_line1' => 'required_with:picto2_picture|nullable|string|between:3,15',
'picto2_line2' => 'required_with:picto2_picture|nullable|string|between:3,15',
];
return \array_merge(
$rules,
$this->pictureRules('picture', 579, 579, 579, 579, false),
$this->pictureRules('picture_ambiance', 333, 333, 333, 333, false),
$this->pictureRules('picto1_picture', 67, 70, 67, 70, true),
$this->pictureRules('picto2_picture', 67, 70, 67, 70, true),
);
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'label' => trans('validation.custom.label'),
'picture_alt' => trans('validation.custom.attribute_alt'),
'picture_title' => trans('validation.attributes.attribute_title'),
'picture_ambiance_alt' => trans('validation.custom.attribute_alt'),
'picture_ambiance_title' => trans('validation.custom.attribute_title'),
'picto1_picture_alt' => trans('validation.custom.attribute_alt'),
'picto1_picture_title' => trans('validation.custom.attribute_title'),
'picto1_line1' => trans('validation.attributes.result_text_under_image'),
'picto1_line2' => trans('validation.attributes.result_text_under_image'),
'picto2_picture_alt' => trans('validation.custom.attribute_alt'),
'picto2_picture_title' => trans('validation.custom.attribute_title'),
'picto2_line1' => trans('validation.attributes.result_text_under_image'),
'picto2_line2' => trans('validation.attributes.result_text_under_image'),
];
}
}

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Requests\Bo\Ranges;
class UpdateRangeRequest extends StoreRangeRequest
{
}

View file

@ -0,0 +1,61 @@
<?php
namespace App\Http\Requests\Bo\StaticPages;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
class UpdateStaticPageRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
$this->merge([
'content' => Str::of($this->post('content'))->value()
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'seo_title' => 'required|min:45|max:70',
'seo_description' => 'required|min:50|max:160',
'title' => 'required|min:3|max:255',
'content' => 'present',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'seo_title' => trans('validation.custom.seo_title'),
'seo_description' => trans('validation.custom.seo_description'),
'title' => trans('validation.attributes.title'),
'content' => trans('validation.custom.content'),
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class UpdateChangeOrderRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation(): void
{
$this->merge([
'direction' => $this->direction === 'up' ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'direction' => 'required|boolean',
];
}
}

View file

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

View file

@ -0,0 +1,74 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Enums\Users\RoleEnum;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\Rules\Password;
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth('backend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return [
'first_name' => 'required|string|min:3|max:255',
'last_name' => 'required|string|min:3|max:255',
'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email',
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
],
'role' => ['required', new Enum(RoleEnum::class)],
'published' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'first_name' => trans('validation.attributes.first_name'),
'last_name' => trans('validation.attributes.last_name'),
'email' => trans('validation.attributes.email'),
'password' => trans('validation.attributes.password'),
'role' => trans('validation.attributes.role'),
'published' => trans('validation.custom.publication')
];
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Http\Requests\Bo\Users;
use Illuminate\Validation\Rules\Password;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'published' => $this->published ? true : false,
'password' => $this->password ? $this->password : $this->route('user')->password,
'password_confirmation' => $this->password_confirmation ?
$this->password_confirmation :
$this->route('user')->password
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules(): array
{
return array_merge(parent::rules(), [
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
'unique:users,email,' . $this->route('user')->id . ',id',
],
'password' => [
'required', 'required_with:password_confirmation', 'confirmed', 'max:255', Password::min(8)
->letters()
->mixedCase()
->numbers()
->symbols()
->uncompromised()
],
]);
}
}

View file

@ -0,0 +1,123 @@
<?php
namespace App\Http\Requests\Fo;
use App\Enums\Contact\ObjectEnum;
use App\Enums\Country;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Str;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
class ContactFormRequest extends FormRequest
{
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
$this->merge([
// * present fields
'first_name' => Str::of($this->post('first_name'))->value(),
'phone' => Str::of($this->post('phone'))->value(),
'address' => Str::of($this->post('address'))->value(),
'city' => Str::of($this->post('city'))->value(),
'postal_code' => Str::of($this->post('postal_code'))->value(),
'message' => Str::of($this->post('message'))->value(),
// * boolean
'terms' => $this->post('terms') ? true : false,
// * integer
'batch_number' => $this->post('batch_number') ? intval($this->post('batch_number')) : null,
]);
}
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
ToolboxHelper::validateRecaptcha($this, \config('services.google.recaptcha.version'));
return [
// * Has to be present fields
'type' => ['required', new Enum(ObjectEnum::class)],
'first_name' => 'present|max:150',
// * obligatoire
'last_name' => 'required|min:2|max:150',
'phone' => 'present|phone:FR|max:25',
// * obligatoire
'email' => 'required|email:rfc,dns,spoof|max:255',
'address' => 'present|max:255',
'city' => 'present|max:255',
'postal_code' => 'present|min:0|postal_code:FR|max:25',
'message' => 'present|min:20|max:35656',
'file' => 'nullable|file|mimes:jpg,jpeg,png,pdf|max:2048',
// * obligatoire
'terms' => 'required|boolean|accepted',
// * Conditional fields
// * obligatoire si type == product
'product' => [
Rule::requiredIf($this->post('type') == ObjectEnum::reclaim->value()),
'nullable',
'exists:products,id'
],
'expiration_date' => [
'nullable',
Rule::prohibitedIf($this->post('type') != ObjectEnum::reclaim->value()),
'date_format:Y-m-d'
],
'batch_number' => [
'nullable',
Rule::prohibitedIf($this->post('type') != ObjectEnum::reclaim->value()),
'digits_between:3,4'
]
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'subject' => trans('validation.attributes.subject'),
'file' => trans('validation.custom.attachment'),
'terms' => trans('validation.attributes.terms'),
'expiration_date' => trans('validation.custom.expiration_date'),
'batch_number' => trans('validation.custom.batch_number'),
'job_title' => trans('validation.custom.job_title'),
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages(): array
{
return [
'postal_code' => trans('validation.custom.postal_code', [
'countries' => Country::france->label(), 'examples' => 72000
]),
'phone.phone' => trans('validation.custom.phone'),
];
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace App\Http\Requests\Fo\Customers;
use App\Rules\IsAdult;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rules\Password;
use LDC\LDCCRM\Enums\Country;
use LDC\LDCCRM\Models\Customer as CrmCustomer;
class CreateAccountRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return Auth::guard('frontend')->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation()
{
$this->merge([
'phone' => $this->phone,
'opt_in' => $this->opt_in ? true : false
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'lastname' => 'required|string|max:255',
'firstname' => 'required|string|max:255',
'phone' => 'nullable|max:255|phone:FR',
'address' => 'required|string|min:1|max:255',
'postcode' => 'required|string|min:1|postal_code:FR',
'city' => 'required|string|min:1|max:255',
'birth_date' => ['required', new IsAdult(trans('Vous devez être majeur pour vous inscrire.'))],
'email' => [
'required', 'email:rfc,dns,spoof', 'max:255',
// * Does email already exists in CRM ?
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function (string $attribute, $value, \Closure $fail) {
if (CrmCustomer::findWithEmail(\strval($value))) {
$fail('Un compte existe déjà avec cette adresse.');
}
},
'unique:customers,email',
],
'password' => [
'required', 'confirmed', Password::min(8)->mixedCase()->numbers()->symbols()
],
'ok_cgv' => 'accepted',
'opt_in' => 'required|boolean',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'lastname' => trans('nom'),
'firstname' => trans('prénom'),
'phone' => trans('téléphone'),
'address' => trans('adresse'),
'postcode' => trans('code postal'),
'city' => trans('ville'),
'birth_date' => trans('date de naissance'),
'email' => trans('email'),
'password' => trans('mot de passe'),
'ok_cgv' => trans('conditions d\'utilisation'),
'opt_in' => trans('newsletter'),
];
}
/**
* Get custom messages for validator errors.
*
* @return array
*/
public function messages()
{
return [
'postal_code' => trans('validation.custom.postal_code', [
'countries' => Country::france()->label, 'examples' => 72000
]),
'phone.phone' => trans('validation.custom.phone'),
'email.unique' => trans('Un compte existe déjà avec cette adresse.'),
'ok_cgv.accepted' => trans('Vous devez accepter les conditions d\'utilisation du site pour continuer.')
];
}
}

View file

@ -0,0 +1,108 @@
<?php
namespace App\Http\Requests\Fo\Customers;
use App\Models\Customer;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Arr;
use Illuminate\Validation\Rule;
use LDC\LDCCRM\Models\Question;
class CrmQuestionsAnswersRequest extends FormRequest
{
/**
* The crm questions that needs to be answered
*
* @var array<\LDC\LDCCRM\Models\Question>
*/
private array $questions = [];
/**
* Prepare the data for validation.
*
* @return void
*/
public function prepareForValidation(): void
{
if (!\auth('frontend')->check()) {
return;
}
/** @var \App\Models\Customer $customer */
$customer = auth('frontend')->user();
/** @var \LDC\LDCCRM\Models\Customer */
$crmCustomer = $customer->apiData;
$this->questions = Customer::cacheCrm(
$customer->hashcode,
'questions',
fn () => Question::unansweredBy($crmCustomer)
);
}
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
if (!\auth('frontend')->check()) {
return false;
}
return (bool)\count($this->questions);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string,\Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
* @throws \RuntimeException If a question type is unhandled.
*/
public function rules(): array
{
$rules = [];
foreach ($this->questions as $questionNumber => $question) {
switch ($question->type->value) {
case \LDC\LDCCRM\Enums\QuestionType::select()->value:
$rules["question.{$questionNumber}"] = ['required', Rule::in(
Arr::map($question->answers, fn ($answer) => $answer->id)
)];
break;
case \LDC\LDCCRM\Enums\QuestionType::radio()->value:
$rules["question.{$questionNumber}"] = ['required', Rule::in(
Arr::map($question->answers, fn ($answer) => $answer->id)
)];
break;
case \LDC\LDCCRM\Enums\QuestionType::checkbox()->value:
$rules["question.{$questionNumber}"] = 'required|array|min:1';
$rules["question.{$questionNumber}.*"] = ['required', Rule::in(
Arr::map($question->answers, fn ($answer) => $answer->id)
)];
break;
default:
throw new \RuntimeException("Unhandled question type {$question->type->value}");
};
} //end foreach
return $rules;
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
return Arr::mapWithKeys($this->questions, function ($question, $index) {
return [
"question.{$index}" => trans('Question :number', ['number' => $index + 1]),
"question.{$index}.*" => trans('Question :number', ['number' => $index + 1]),
];
});
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Http\Requests\Fo\Customers;
use App\Rules\IsAdult;
class UpdateAccountRequest extends CreateAccountRequest
{
/** @var array */
private static $skeleton = [
'lastname',
'firstname',
'phone',
'address',
'postcode',
'city',
'opt_in',
'birth_date'
];
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return array_merge(
\array_intersect_key(
parent::rules(),
\array_flip(self::$skeleton)
),
[
'birth_date' => [
'required',
new IsAdult(trans('Vous devez être majeur pour utiliser le site.'))
],
]
);
}
}

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