Tmp back overhaul with base project

This commit is contained in:
Jérémy Munsch 2023-11-23 17:31:28 +01:00
parent 5dda42e9da
commit d208854fb0
No known key found for this signature in database
GPG key ID: 18361AF3BFEC0FEC
228 changed files with 5045 additions and 2590 deletions

View file

@ -24,3 +24,11 @@ Choisir un thème dans le dépôt des thèmes `gen-game-web`, puis copier les fi
- SH - Soucis de connexion / déconnexion quand le jeu est terminé et que l'adresse du jeu redirige vers une autre adresse : si on se déconnecte et qu'on revient sur le bo, on est toujours connecté
-> MJ - Pour que le cookie soit delete il ne faut pas renvoyer un 302 juste apres le disconnect
```
## Generation des codes
`artisan queue:work`
En cas de manipulation pourrie, penser a vider le cache pour supprimer le lock
sur le job de generation des codes.

View file

@ -1,4 +0,0 @@
git checkout --orphan master
git add .
git commit -m "Initial commit"
git push

View file

@ -109,8 +109,8 @@ class ExportCodesUniques extends Command
$this->info("L'archive CodesUniques_" . date('dmY') . ".zip a ete creer.");
// Envoi de mail à MAIL_ADMIN avec le nom du ZIP "CodesUniques_". date('dmY') .".zip" .
Mail::to(config('mail.MAIL_ADMIN'))
->send(new MailExportCodesUniques($nbCodeFic, $nbCodeTot, $nbCodeLigne));
Mail::to(config('mail.admin'))
->send(new MailExportCodesUniques($nbCodeFic, $nbCodeTot, $nbCodeLigne));
return 0;
}
@ -210,6 +210,6 @@ class ExportCodesUniques extends Command
Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]);
}
} //end if
}//end foreach
} //end foreach
}
}

View file

@ -12,7 +12,7 @@ class TestMail extends Command
*
* @var string
*/
protected $signature = 'uwil:mail-test';
protected $signature = 'uwil:mail:test';
/**
* The console command description.
@ -24,13 +24,12 @@ class TestMail extends Command
/**
* Execute the console command.
*
* @return integer
* @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');
});
return 0;
}
}

View file

@ -16,7 +16,7 @@ class UserCreate extends Command
*
* @var string
*/
protected $signature = 'user:create';
protected $signature = 'uwil:user:create';
/**
* The console command description.
@ -26,18 +26,18 @@ class UserCreate extends Command
protected $description = 'Create a backend admin user';
/**
* The firstname of the new user.
* The first name of the new user.
*
* @var string
*/
protected $firstname;
protected $first_name;
/**
* The lastname of the new user.
* The last name of the new user.
*
* @var string
*/
protected $lastname;
protected $last_name;
/**
* The email of the new user.
@ -67,11 +67,11 @@ class UserCreate extends Command
*/
public function handle(): int
{
$this->firstname = null;
$this->addFirstname();
$this->first_name = null;
$this->addFirstName();
$this->lastname = null;
$this->addLastname();
$this->last_name = null;
$this->addLastName();
$this->email = null;
$this->addEmail();
@ -83,8 +83,8 @@ class UserCreate extends Command
$this->addRole();
$user = new User([
'firstname' => $this->firstname,
'lastname' => $this->lastname,
'first_name' => $this->first_name,
'last_name' => $this->last_name,
'email' => $this->email,
'password' => $this->password,
'role' => $this->role,
@ -99,23 +99,23 @@ class UserCreate extends Command
}
/**
* Add a firstname to the user.
* Add a first name to the user.
*
* @return void
*/
private function addFirstname(): void
private function addFirstName(): void
{
while (is_null($this->firstname)) {
while (is_null($this->first_name)) {
try {
$tmp = $this->ask('Type the wanted user firstname', 'Admin');
$validator = Validator::make(['firstname' => $tmp], [
'firstname' => 'required|min:3|max:255'
$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->firstname = $tmp;
$this->first_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid firstname %s',
'Please provide a valid first name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
@ -124,23 +124,23 @@ class UserCreate extends Command
}
/**
* Add a lastname to the user.
* Add a last name to the user.
*
* @return void
*/
private function addLastname(): void
private function addLastName(): void
{
while (is_null($this->lastname)) {
while (is_null($this->last_name)) {
try {
$tmp = $this->ask('Type the wanted user lastname', 'Saïtama');
$validator = Validator::make(['lastname' => $tmp], [
'lastname' => 'required|min:3|max:255'
$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->lastname = $tmp;
$this->last_name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid lastname %s',
'Please provide a valid last_name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
@ -200,15 +200,15 @@ class UserCreate extends Command
try {
$tmp = $this->choice(
'Select his role',
[RoleEnum::conceptor->label(), RoleEnum::admin->label()],
RoleEnum::admin->label(),
[RoleEnum::conceptor->name, RoleEnum::admin->name],
RoleEnum::admin->name,
$maxAttempts = null,
$allowMultipleSelections = false
);
if ($tmp === RoleEnum::conceptor->label()) {
if ($tmp === RoleEnum::conceptor->name) {
$tmp = RoleEnum::conceptor;
}
if ($tmp === RoleEnum::admin->label()) {
if ($tmp === RoleEnum::admin->name) {
$tmp = RoleEnum::admin;
}
$validator = Validator::make(['role' => $tmp], [

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];
}
}

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

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

View file

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

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

View file

@ -6,6 +6,7 @@ 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
@ -39,7 +40,7 @@ class LoginController extends Controller
*/
public function showLoginForm(): View
{
return view('back::auth.login');
return view('bo.auth.login');
}
/**
@ -51,4 +52,21 @@ class LoginController extends Controller
{
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

@ -33,13 +33,13 @@ class BannedIpController extends Controller
'ip',
);
}
$searchFields = trans('validation.attributes.ip');
$searchFields = trans('validation.custom.ip');
/** Sort columns with a query */
$this->sortQuery($bannedipModels);
/** Custom pagination */
$bannedipModels = $this->customPaginate($bannedipModels, $request->pagination);
$bannedipModels = $this->paginate($bannedipModels);
return view('back::pages.banned-ips.index', compact('bannedipModels', 'search', 'searchFields'));
}

View file

@ -3,13 +3,14 @@
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\CodeSearchRequest;
use App\Http\Requests\ExportCodesRequest;
use App\Http\Requests\GenerateCodesRequest;
use App\Http\Requests\Bo\CodeSearchRequest;
use App\Http\Requests\Bo\ExportCodesRequest;
use App\Http\Requests\Bo\GenerateCodesRequest;
use App\Jobs\ProcessExportCodesUniques;
use App\Jobs\ProcessUniqueCode;
use App\Models\Code;
use App\Models\Settings;
use Illuminate\Support\Facades\Queue;
use Kwaadpepper\Serial\Exceptions\SerialCasterException;
class CodesController extends Controller
@ -21,11 +22,11 @@ class CodesController extends Controller
*/
public function index(): \Illuminate\Contracts\View\View
{
$settings = Settings::first();
$nbCodeBase = Code::count();
$nbCodeUsed = Code::where('code_used', 1)->count();
$nbCodeExported = Code::where('code_exported', 1)->count();
$nbCodeRemainingToExport = Code::where('code_exported', 0)->count();
$settings = Settings::query()->firstOrFail();
$nbCodeBase = Code::query()->count();
$nbCodeUsed = Code::query()->where('code_used', true)->count();
$nbCodeExported = Code::query()->where('code_exported', true)->count();
$nbCodeRemainingToExport = Code::query()->where('code_exported', false)->count();
return view('back::pages.codes.index', compact(
'settings',
'nbCodeBase',
@ -38,55 +39,67 @@ class CodesController extends Controller
/**
* Run generate codes action
*
* @param GenerateCodesRequest $request
* @param \App\Http\Controllers\Bo\GenerateCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function generate(GenerateCodesRequest $request): \Illuminate\Http\RedirectResponse
{
ProcessUniqueCode::dispatch(
$request->quantity,
$request->code_length
Queue::push(
new ProcessUniqueCode(
intval($request->quantity),
$request->code_length
)
);
return back()->with(
'success',
"La génération à commencée, un mail de notification sera envoyé à l'adresse " .
config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé."
trans(
// phpcs:ignore Generic.Files.LineLength.TooLong
'La tache de génération à été ajoutée, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.',
[
'email' => config('mail.admin')
]
)
);
}
/**
* Run export codes
*
* @param ExportCodesRequest $request
* @param \App\Http\Requests\Bo\ExportCodesRequest $request
* @return \Illuminate\Http\RedirectResponse
*/
public function export(ExportCodesRequest $request): \Illuminate\Http\RedirectResponse
{
ProcessExportCodesUniques::dispatch(
intval($request->nb_code_fic),
intval($request->nb_code_tot),
intval($request->nb_code_ligne)
Queue::push(
new ProcessExportCodesUniques(
intval($request->nb_code_fic),
intval($request->nb_code_tot),
intval($request->nb_code_ligne)
)
);
return back()->with(
'success',
"L'export des codes à commencé, un mail de notification sera envoyé à l'adresse " .
config('mail.MAIL_ADMIN') . " lorsque le traitement sera terminé."
// phpcs:ignore Generic.Files.LineLength.TooLong
'L\'export des codes à commencé, un mail de notification sera envoyé à l\'adresse :email lorsque le traitement sera terminé.',
[
'email' => config('mail.admin')
]
);
}
/**
* Search for a specific code.
*
* @param CodeSearchRequest $request
* @param \App\Http\Requests\Bo\CodeSearchRequest $request
* @return \Illuminate\Contracts\View\View
*/
public function search(CodeSearchRequest $request): \Illuminate\Contracts\View\View
{
$code = null;
try {
$code = Code::whereId(Code::serialToInt($request->code))->first();
$code = Code::query()->where('id', Code::serialToInt($request->code))->first();
} catch (\Throwable $e) {
if (\get_class($e) === SerialCasterException::class) {
$code = false;

View file

@ -1,91 +0,0 @@
<?php
namespace App\Http\Controllers\Bo;
use App\Http\Controllers\Controller;
use App\Http\Requests\Bo\Contents\UpdateContentRequest;
use App\Models\Content;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
class ContentController extends Controller
{
/**
* 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 $bannedipModels */
$contentModels = Content::query();
/** @var string $search Search field */
$search = $request->search;
if ($search) {
$this->searchQuery(
$contentModels,
$search,
null,
'key',
'value'
);
}
$searchFields = \sprintf(
'%s, %s',
trans('validation.attributes.name'),
trans('validation.attributes.value'),
);
/** Sort columns with a query */
$this->sortQuery($contentModels);
/** Custom pagination */
$contentModels = $this->customPaginate($contentModels, $request->pagination);
return view('back::pages.contents.index', compact('contentModels', 'search', 'searchFields'));
}
/**
* Show the form for editing the specified resource.
*
* @param \App\Models\Content $content
* @return \Illuminate\Contracts\View\View
*/
public function edit(Content $content): \Illuminate\Contracts\View\View
{
return view('back::pages.contents.edit', [
'contentModel' => $content,
]);
}
/**
* Update the specified resource in storage.
*
* @param \App\Http\Requests\Bo\Contents\UpdateContentRequest $request
* @param \App\Models\Content $content
* @return \Illuminate\Http\RedirectResponse
*/
public function update(UpdateContentRequest $request, Content $content): \Illuminate\Http\RedirectResponse
{
return DB::transaction(function () use ($request, $content) {
$content->fill($request->validated());
if ($content->saveOrFail()) {
return redirect()
->route('bo.contents.edit', $content)
->with('success', trans(
'crud.messages.has_been_updated',
['model' => Str::singular(trans('models.classes.contents'))]
));
}
return redirect()
->back()
->with('error', trans(
'crud.messages.cannot_be_updated',
['model' => Str::singular(trans('models.classes.contents'))]
));
});
}
}

View file

@ -37,7 +37,7 @@ class FaqQuestionsController extends Controller
}
$searchFields = \sprintf(
'%s, %s ...',
Str::ucfirst(__('validation.attributes.question')),
Str::ucfirst(__('validation.custom.question')),
__('validation.attributes.answer'),
);
@ -45,7 +45,7 @@ class FaqQuestionsController extends Controller
$this->sortQuery($faqQuestionModels);
/** Custom pagination */
$faqQuestionModels = $this->customPaginate($faqQuestionModels, $request->pagination);
$faqQuestionModels = $this->paginate($faqQuestionModels);
return view('back::pages.faq-questions.index', compact('faqQuestionModels', 'search', 'searchFields'));
}

View file

@ -39,7 +39,7 @@ class FaqThemesController extends Controller
$this->sortQuery($faqThemeModels);
/** Custom pagination */
$faqThemeModels = $this->customPaginate($faqThemeModels, $request->pagination);
$faqThemeModels = $this->paginate($faqThemeModels);
return view('back::pages.faq-themes.index', compact('faqThemeModels', 'search', 'searchFields'));
}

View file

@ -43,7 +43,7 @@ class GainViewController extends Controller
$this->sortQuery($gainViewModels);
/** Custom pagination */
$gainViewModels = $this->customPaginate($gainViewModels, $request->pagination);
$gainViewModels = $this->paginate($gainViewModels);
return view('back::pages.gain-views.index', compact('gainViewModels', 'search', 'searchFields'));
}

View file

@ -45,7 +45,7 @@ class PrizesController extends Controller
$this->sortQuery($prizeModels);
/** Custom pagination */
$prizeModels = $this->customPaginate($prizeModels, $request->pagination);
$prizeModels = $this->paginate($prizeModels);
return view('back::pages.prizes.index', compact('prizeModels', 'search', 'searchFields'));
}

View file

@ -47,7 +47,7 @@ class ProductsController extends Controller
$this->sortQuery($productModels);
/** Custom pagination */
$productModels = $this->customPaginate($productModels, $request->pagination);
$productModels = $this->paginate($productModels);
return view('back::pages.products.index', compact('productModels', 'search', 'searchFields'));
}

View file

@ -39,7 +39,7 @@ class RangeController extends Controller
$this->sortQuery($rangeModels);
/** Custom pagination */
$rangeModels = $this->customPaginate($rangeModels, $request->pagination);
$rangeModels = $this->paginate($rangeModels);
return view('back::pages.ranges.index', compact('rangeModels', 'search', 'searchFields'));
}

View file

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

View file

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

View file

@ -24,7 +24,7 @@ class WinnersListController extends Controller
$this->sortQuery($participationModels);
/** Custom pagination */
$participationModels = $this->customPaginate($participationModels, $request->pagination);
$participationModels = $this->paginate($participationModels);
$settings = Settings::select('use_ean_codes', 'winning_proof')->first();

View file

@ -2,20 +2,22 @@
namespace App\Http\Controllers;
use App\Enums\Pagination\ItemsPerPaginationEnum;
use App\Enums\Theme\BootstrapThemeEnum;
use App\Lib\Helpers\ToolboxHelper;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
class Controller extends BaseController
{
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
/**
@ -35,7 +37,7 @@ class Controller extends BaseController
string $search,
callable $searchQuery = null,
string ...$fields
): Builder {
): \Illuminate\Database\Eloquent\Builder {
if (count($fields)) {
// Search using words.
$words = explode(' ', $search);
@ -64,36 +66,85 @@ class Controller extends BaseController
* @param array $fields
* @return \Illuminate\Database\Eloquent\Builder
*/
private function searchOnFields(Builder $query, string $search, array $fields): Builder
{
private function searchOnFields(
Builder $query,
string $search,
array $fields
): \Illuminate\Database\Eloquent\Builder {
$table = $query->getModel()->getTable();
foreach ($fields as $f) {
$f = str_replace('?', '\?', $f);
if (is_array($f)) {
foreach ($fields as $field) {
$field = str_replace('?', '\?', $field);
if (is_array($field)) {
$query->orWhereRaw(
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($f)->map(function ($q) use ($table) {
sprintf("UPPER(CONCAT(%s)) LIKE UPPER(?)", collect($field)->map(function ($q) use ($table) {
return "`$table`.`$q`";
})->implode(', \' \', ')),
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
);
} else {
$query->orWhereRaw(
"UPPER(`$table`.`$f`) LIKE UPPER(?)",
['%' . filter_var($search, FILTER_SANITIZE_STRING) . '%']
['%' . 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;
}
/**
* Sort columns with a 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 = []): Builder
protected function sortQuery(Builder $query, array $ignore = []): \Illuminate\Database\Eloquent\Builder
{
$rName = request()->route()->getName();
$table = $query->getModel()->getTable();
@ -121,6 +172,14 @@ class Controller extends BaseController
$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;
}
@ -128,18 +187,45 @@ class Controller extends BaseController
/**
* Customize pagination with cache or config (default).
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @param integer|null $pagination
* @return \Illuminate\Pagination\LengthAwarePaginator
* @param \Illuminate\Database\Eloquent\Builder $query
* @param \App\Enums\Pagination\ItemsPerPaginationEnum|null $pagination
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
*/
protected function customPaginate(Builder $query, $pagination): LengthAwarePaginator
{
if ($pagination) {
Cache::put('pagination', $pagination);
};
$query = $query->paginate(
(Cache::get('pagination')) ? intval(Cache::get('pagination')) : config('pagination.default')
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

@ -24,6 +24,6 @@ class EndPageController extends Controller
) {
return view('theme::fo.end', compact('settings'));
}
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
}

View file

@ -169,7 +169,7 @@ class EnterCodeController extends Controller
$arcadiaMail = $this->apiCrm->ArcadiaOutgoingMail($crmId, $fields, 'game');
if ($arcadiaMail->status === 'KO') {
Mail::to(config('mail.MAIL_ADMIN'))->send(new CourrierArcadiaError(auth()->user()));
Mail::to(config('mail.admin'))->send(new CourrierArcadiaError(auth()->user()));
}
} //end if
@ -260,7 +260,7 @@ class EnterCodeController extends Controller
$participation->reduction_id = $reduction->id;
$participation->save();
} else {
Mail::to(config('mail.MAIL_ADMIN'))
Mail::to(config('mail.admin'))
->send(new NotifAdmin(\sprintf(
'MESBR : Une erreur est survenue. Code : %s Message : %s',
$result["code"],
@ -287,6 +287,6 @@ class EnterCodeController extends Controller
if ($participation) {
return view('theme::fo.winning-proof', compact('participation'));
}
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
}

View file

@ -18,7 +18,7 @@ class FAQController extends Controller
$questions = FaqQuestion::all();
$themes = FaqTheme::all();
if (count($questions) === 0 && count($themes) === 0) {
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
return view('theme::fo.faq', compact('questions', 'themes'));
}

View file

@ -16,6 +16,6 @@ class GainsController extends Controller
if (view()->exists('theme::fo.gains')) {
return view('theme::fo.gains');
}
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
}

View file

@ -16,6 +16,6 @@ class HowToPlayController extends Controller
if (view()->exists('theme::fo.basic.how-to-play')) {
return view('theme::fo.basic.how-to-play');
}
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
}

View file

@ -88,8 +88,8 @@ class LoginController extends Controller
/** @var \App\Models\Customer $userLocal */
$userLocal = Customer::create([
'crm_id' => $connect->user->crm_id,
'firstname' => $connect->user->firstname,
'lastname' => $connect->user->lastname,
'firstname' => $connect->user->first_name,
'lastname' => $connect->user->last_name,
'birth_date' => $connect->user->birth_date,
'email' => $email,
'password' => Hash::make($password),
@ -106,8 +106,8 @@ class LoginController extends Controller
/** @var \App\Models\Customer $userLocal */
$userLocal = Customer::create([
'crm_id' => $connect->user->crm_id,
'firstname' => $connect->user->firstname,
'lastname' => $connect->user->lastname,
'firstname' => $connect->user->first_name,
'lastname' => $connect->user->last_name,
'birth_date' => $connect->user->birth_date,
'email' => $email,
'password' => Hash::make($password),
@ -149,7 +149,7 @@ class LoginController extends Controller
public function logout()
{
Auth::logout();
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
/**

View file

@ -19,6 +19,6 @@ class TeaserController extends Controller
if ($settings->activated_teaser === 1 && $settings->teaser_end_activity > now()) {
return view('theme::fo.teaser', compact('settings'));
}
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
}

View file

@ -14,7 +14,7 @@ class ViewContentController extends Controller
*/
public function reglement()
{
$content = Content::find(3);
$content = Content::query()->find(3);
return view('theme::fo.reglement', compact('content'));
}

View file

@ -44,7 +44,7 @@ class WinningProofController extends Controller
['barcode', null]
])->has('instantWin')->first();
if (!$participation) {
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
$request->validate([
@ -102,7 +102,7 @@ class WinningProofController extends Controller
if ($update->status !== 'OK') {
Log::warning('Erreur lors de la mise a jour du user (crm_id : ' . $user->crm_id .
') - code : ' . $update->code . ' - message : ' . $update->message);
Mail::to(config('mail.MAIL_ADMIN'))->send(new NotifAdmin(
Mail::to(config('mail.admin'))->send(new NotifAdmin(
'CRM : Erreur lors de la mise a jour du user (crm_id : ' .
$user->crm_id . ') - code : ' . $update->code . ' - message : ' . $update->message
));

View file

@ -37,7 +37,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\App\Http\Middleware\SecureHeaders::class,
\App\Http\Middleware\SecureHeaders::class
],
'api' => [
@ -57,16 +57,15 @@ class Kernel extends HttpKernel
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'auth.session' => \Illuminate\Session\Middleware\AuthenticateSession::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'game-date' => \App\Http\Middleware\GameCheckDate::class,
'is-admin' => \App\Http\Middleware\IsAdmin::class,
'is-super-admin' => \App\Http\Middleware\IsSuperAdmin::class,
're7' => \App\Http\Middleware\Re7Middleware::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'signed' => \App\Http\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'htmlsanitize' => \App\Http\Middleware\HtmlSanitizer::class,

View file

@ -2,9 +2,9 @@
namespace App\Http\Middleware;
use App\Enums\Users\RoleEnum;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class IsAdmin
{
@ -17,10 +17,18 @@ class IsAdmin
*/
public function handle(Request $request, Closure $next)
{
$role = Auth::user()->role;
if ($role === 1 || $role === 2) {
return $next($request);
/** @var \App\Models\User|null */
$user = auth('backend')->user();
if (!$user) {
abort(403, trans('Vous devez être authentifé'));
}
abort(404, "Cette page n'existe pas.");
if ($user->role->value() > RoleEnum::admin->value()) {
return response()->redirectToRoute('bo.home')
->with('warning', trans('Vous n\'avez pas les droits nécessaires pour accéder à cet élément'));
}
return $next($request);
}
}

View file

@ -2,9 +2,9 @@
namespace App\Http\Middleware;
use App\Enums\Users\RoleEnum;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class IsSuperAdmin
{
@ -17,10 +17,18 @@ class IsSuperAdmin
*/
public function handle(Request $request, Closure $next)
{
$role = Auth::user()->role;
if ($role === 1) {
return $next($request);
/** @var \App\Models\User|null */
$user = auth('backend')->user();
if (!$user) {
abort(403, trans('Vous devez être authentifé'));
}
return redirect()->route('bo.dashboard');
if ($user->role->value() > RoleEnum::conceptor->value()) {
return response()->redirectToRoute('bo.home')
->with('warning', trans('Vous n\'avez pas les droits nécessaires pour accéder à cet élément'));
}
return $next($request);
}
}

View file

@ -17,7 +17,7 @@ class Re7Middleware
public function handle(Request $request, Closure $next)
{
if (config('app.env') === 'production') {
return redirect()->route('fo.home');
return redirect()->route('fo.homepage');
}
return $next($request);
}

View file

@ -3,7 +3,6 @@
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;
class RedirectIfAuthenticated
@ -24,10 +23,10 @@ class RedirectIfAuthenticated
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
if (auth($guard)->check()) {
switch ($guard) {
case 'frontend':
return \redirect()->route('fo.home');
return \redirect()->route('fo.homepage');
case 'backend':
if (!$request->routeIs('bo.*')) {
continue;

View file

@ -20,7 +20,7 @@ class TrustProxies extends Middleware
* @var integer
*/
protected $headers =
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_FOR |
Request::HEADER_X_FORWARDED_HOST |
Request::HEADER_X_FORWARDED_PORT |
Request::HEADER_X_FORWARDED_PROTO |

View file

@ -1,7 +1,8 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Bo;
use App\Enums\Users\RoleEnum;
use Illuminate\Foundation\Http\FormRequest;
class CodeSearchRequest extends FormRequest
@ -13,9 +14,9 @@ class CodeSearchRequest extends FormRequest
*/
public function authorize()
{
// ? Is auth and super admin ?
return auth()->check() and
auth()->user()->role === 1;
// ? Is auth and super admin
return auth('backend')->check() and
auth('backend')->user()->role === RoleEnum::conceptor;
}
/**

View file

@ -1,42 +0,0 @@
<?php
namespace App\Http\Requests\Bo\Contents;
use Illuminate\Foundation\Http\FormRequest;
class UpdateContentRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'value' => 'required|string|min:15',
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes(): array
{
return [
'value' => trans('Texte'),
];
}
}

View file

@ -1,7 +1,8 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Bo;
use App\Enums\Users\RoleEnum;
use App\Models\Code;
use Illuminate\Foundation\Http\FormRequest;
@ -15,8 +16,8 @@ class ExportCodesRequest extends FormRequest
public function authorize()
{
/** @var \Illuminate\Database\Eloquent\Builder */
$codesAvailableToExport = Code::whereCodeExported(false);
$lastExportedId = Code::whereCodeExported(true)
$codesAvailableToExport = Code::query()->where('code_exported', false);
$lastExportedId = Code::query()->where('code_exported', true)
->orderBy('id', 'desc')->first()->id ?? null;
if ($lastExportedId) {
$codesAvailableToExport = $codesAvailableToExport
@ -26,8 +27,10 @@ class ExportCodesRequest extends FormRequest
'available_export' => $codesAvailableToExport->count()
]);
// ? Is auth and super admin, also is json ?
return auth()->check() and
auth()->user()->role === 1 and $this->json();
return auth('backend')->check() and
// ? Is auth and super admin
auth('backend')->user()->role === RoleEnum::conceptor
and $this->json();
}
/**

View file

@ -1,7 +1,8 @@
<?php
namespace App\Http\Requests;
namespace App\Http\Requests\Bo;
use App\Enums\Users\RoleEnum;
use Illuminate\Foundation\Http\FormRequest;
class GenerateCodesRequest extends FormRequest
@ -14,7 +15,8 @@ class GenerateCodesRequest extends FormRequest
public function authorize()
{
// ? Is auth and super admin
return auth()->check() and auth()->user()->role === 1;
return auth('backend')->check() and
auth('backend')->user()->role === RoleEnum::conceptor;
}
/**

View file

@ -0,0 +1,46 @@
<?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' => (string)Str::of($this->content)
]);
}
/**
* 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',
];
}
}

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

@ -7,6 +7,7 @@ use App\Mail\JobFailed;
use App\Models\Code;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
@ -14,7 +15,7 @@ use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Mail;
class ProcessExportCodesUniques implements ShouldQueue
class ProcessExportCodesUniques implements ShouldQueue, ShouldBeUnique
{
use Dispatchable;
use InteractsWithQueue;
@ -30,33 +31,12 @@ class ProcessExportCodesUniques implements ShouldQueue
*/
public $timeout = 7200;
/**
* Nombre de codes par fichiers.
*
* @var integer
*/
private $nbCodeFic;
/**
* Nombre total de codes à exporter.
*
* @var integer
*/
private $nbCodeTot;
/**
* Nombre de codes par ligne.
*
* @var integer
*/
private $nbCodeLigne;
/**
* The export file name
*
* @var string
*/
private $exportFileName = null;
protected $exportFileName = null;
/**
* Create a new job instance.
@ -66,11 +46,11 @@ class ProcessExportCodesUniques implements ShouldQueue
* @param integer $nbCodeLigne Nombre de codes par ligne.
* @return void
*/
public function __construct(int $nbCodeFic, int $nbCodeTot, int $nbCodeLigne)
{
$this->nbCodeFic = $nbCodeFic;
$this->nbCodeTot = $nbCodeTot;
$this->nbCodeLigne = $nbCodeLigne;
public function __construct(
protected int $nbCodeFic,
protected int $nbCodeTot,
protected int $nbCodeLigne
) {
}
/**
@ -86,7 +66,8 @@ class ProcessExportCodesUniques implements ShouldQueue
date('dmY_His')
);
$lastExportedCodeId = Code::whereCodeExported(true)
$lastExportedCodeId = Code::query()
->where('code_exported', true)
->orderBy('id', 'desc')->first()->id ?? null;
$filesPath = $this->generateCsvFiles($lastExportedCodeId);
@ -115,12 +96,12 @@ class ProcessExportCodesUniques implements ShouldQueue
}
// * Envoi de mail à MAIL_ADMIN avec le nom du ZIP "CodesUniques_". date('dmY_His') .".zip".
Mail::to(config('mail.MAIL_ADMIN'))
->send(new ExportCodesUniques(
$this->nbCodeFic,
$this->nbCodeTot,
$this->nbCodeLigne
));
Mail::to(config('mail.admin'))
->send(new ExportCodesUniques(
$this->nbCodeFic,
$this->nbCodeTot,
$this->nbCodeLigne
));
}
/**
@ -185,7 +166,7 @@ class ProcessExportCodesUniques implements ShouldQueue
}
// ? Have we reached pagination limit for mass saving ?
if (count($exportedCodesId) === $paginate) {
Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]);
Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]);
$exportedCodesId = [];
}
} //end if
@ -193,7 +174,7 @@ class ProcessExportCodesUniques implements ShouldQueue
// ? Are there exported codes to save ?
if (count($exportedCodesId)) {
Code::whereIn('id', $exportedCodesId)->update(['code_exported' => 1]);
Code::query()->whereIn('id', $exportedCodesId)->update(['code_exported' => true]);
}
return $outPaths;
}
@ -206,10 +187,10 @@ class ProcessExportCodesUniques implements ShouldQueue
*/
public function failed(Exception $exception)
{
Mail::to(config('mail.MAIL_ADMIN'))
->send(new JobFailed("ProcessExportCodeUnique", [
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]));
Mail::to(config('mail.admin'))
->send(new JobFailed("ProcessExportCodeUnique", [
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]));
}
}

View file

@ -6,16 +6,17 @@ use App\Mail\CodeGenerationEnd;
use App\Mail\JobFailed;
use App\Models\Code;
use App\Models\Settings;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\Middleware\WithoutOverlapping;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Mail;
class ProcessUniqueCode implements ShouldQueue
class ProcessUniqueCode implements ShouldQueue, ShouldBeUnique
{
use Dispatchable;
use InteractsWithQueue;
@ -31,101 +32,104 @@ class ProcessUniqueCode implements ShouldQueue
*/
public $timeout = 7200;
/**
* Quantity of code to generate
*
* @var integer
*/
private $quantity;
/**
* Length of code to generate
*
* @var integer
*/
private $length;
/**
* Create a new job instance.
*
* @param integer $quantity
* @param integer $length
* @param integer $amount Amount of code to generate.
* @param integer $length Length of each code to generate.
* @return void
*/
public function __construct(int $quantity, int $length)
public function __construct(protected int $amount, protected int $length)
{
$this->quantity = $quantity;
$this->length = $length;
}
/**
* Execute the job.
* TODO: Mettre tout ce bordel dans une transaction quand les serveur de leurs morts auront été mis à jour Uu.
*
* @return void
*/
public function handle()
{
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
DB::transaction(function () {
// ! Le premier code commence à 1
$last_id = (Code::query()->max('id') ?? 0) + 1;
$maxSQLPlaceholders = 65535;
/** @var \App\Models\Settings $settings */
$settings = app(Settings::class);
$insertColumns = collect(DB::getSchemaBuilder()
->getColumnListing((new Code())->getTable()))
->filter(function ($name) {
// * Ignorer les colonnes avec valeurs par défaut sur la table.
return !in_array($name, ['id', 'code_used', 'code_exported']);
})->count();
$maxSQLPlaceholders = 65535;
/**
* Nombre d'insertions max sur un seule requète
*
* @var integer $perPage
*/
$perPage = $maxSQLPlaceholders / $insertColumns;
$insertColumns = collect(DB::getSchemaBuilder()
->getColumnListing((new Code())->getTable()))
->filter(function ($name) {
// * Ignorer les colonnes avec valeurs par défaut sur la table.
return !in_array($name, ['id', 'code_used', 'code_exported']);
})->count() + 1;
/**
* Nombre de page totale à insérer.
*
* @var integer $totalPages
*/
$totalPages = \ceil($this->quantity / $perPage);
/**
* Nombre d'insertions max sur un seule requète
*
* @var integer $perPage
*/
$perPage = \floor($maxSQLPlaceholders / $insertColumns);
$totalGeneratedInserts = 0;
/**
* Nombre de page totale à insérer.
*
* @var integer $totalPages
*/
$totalPages = \ceil($this->amount / $perPage);
for ($currentPage = 1; $currentPage <= $totalPages; $currentPage++) {
$inserts = [];
$insertToGenerate = min($perPage, $this->quantity - $totalGeneratedInserts);
do {
$inserts[] = [
'date_creation' => now()->format('Y-m-d'),
];
} while ($totalGeneratedInserts < $this->quantity and --$insertToGenerate);
$totalGeneratedInserts = 0;
// * Mass insert
Code::insert($inserts);
for ($currentPage = 1; $currentPage <= $totalPages; $currentPage++) {
$inserts = [];
$insertToGenerate = min($perPage, $this->amount - $totalGeneratedInserts);
do {
$inserts[] = [
'id' => $last_id++,
'date_creation' => now()->format('Y-m-d'),
];
} while (($totalGeneratedInserts < $this->amount) && --$insertToGenerate);
$totalGeneratedInserts += count($inserts);
}
// * Mass insert
Code::query()->insert($inserts);
// * Fin de la génération.
Mail::to(config('mail.MAIL_ADMIN'))
->send(new CodeGenerationEnd($this->quantity, $settings->name));
$totalGeneratedInserts += count($inserts);
}
// * Fin de la génération.
Mail::to(config('mail.admin'))
->send(new CodeGenerationEnd($this->amount, $settings->name));
});
}
/**
* In case of job failure
*
* @param Exception $exception
* @param \Exception $exception
* @return void
*/
public function failed(Exception $exception)
public function failed(\Exception $exception)
{
Mail::to(config('mail.MAIL_ADMIN'))
->send(new JobFailed("ProcessUniqueCode", [
'message' => $exception->getMessage(),
'trace' => $exception->getTraceAsString()
]));
Mail::to(config('mail.admin'))->send(
new JobFailed('ProcessUniqueCode', [
'message' => $exception->getMessage(),
'trace' => $exception->getTrace()
])
);
}
/**
* Get the middleware the job should pass through.
*
* @return array<int, object>
*/
public function middleware(): array
{
return [
(new WithoutOverlapping('code_generation'))
->releaseAfter(60)
];
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace App\Lib\Helpers;
namespace App\Lib;
use Illuminate\Container\Container;
use Illuminate\Pagination\LengthAwarePaginator;

View file

@ -118,7 +118,7 @@ class FileStorageHelper
);
return str_replace('/private/', '', $newPath);
}//end if
} //end if
if (\is_object($file) and $file instanceof \SplFileInfo) {
/** @var \SplFileInfo $file */
$filename = $filename ?: $file->getBaseName();

View file

@ -14,42 +14,13 @@ use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Finder\SplFileInfo;
class ToolboxHelper
{
/**
* Run a terminal command and gives the output
*
* @param array|string $command The command to execute on terminal.
* @param boolean $throw Wether to throw en esception on failure or not.
* @return string|false Return false on failure only if $throw is false.
* @phpcs:disable Squiz.Commenting.FunctionComment.EmptyThrows
* @phpcs:disable Squiz.Commenting.FunctionCommentThrowTag.WrongNumber
* @throws \Illuminate\Process\Exceptions\ProcessTimedOutException
* @throws \Symfony\Component\Process\Exception\LogicException
* @throws \Symfony\Component\Process\Exception\InvalidArgumentException
* @throws \Symfony\Component\Process\Exception\RuntimeException
* @throws \LogicException
* @throws \RuntimeException If the command fails.
*/
public static function runConsoleCommand(array|string $command, bool $throw = true): string | false
{
// phpcs:enable
$process = \Illuminate\Support\Facades\Process::command($command)->run();
if ($process->failed()) {
$code = $process->exitCode();
$ouput = $process->errorOutput();
if ($throw) {
throw new \RuntimeException("Failed to run command exit code {$code} with output \n{$ouput}");
}
return false;
}
return $process->output();
}
/**
* @param array<int|string, mixed> $array1
* @param array<int|string, mixed> $array2
@ -104,7 +75,7 @@ class ToolboxHelper
throw new \RuntimeException();
}
if (\floatval($response->json('score')) < 0.8) {
$fail("Vous n'avez pas obtenu un score assez élevé lors de la validation du recaptcha");
$fail(trans('recaptcha.not_enough_score'));
}
return;
}
@ -117,7 +88,7 @@ class ToolboxHelper
throw new \RuntimeException();
}
} catch (\Exception $e) {
$fail('Erreur lors de la validation du formulaire veuillez réssayer');
$fail(trans('recaptcha.error'));
} //end try
}
]
@ -546,4 +517,24 @@ class ToolboxHelper
->contains(fn (string $class_interface) => $class_interface === $interface)
);
}
/**
* Get validated pagination from request.
*
* @param integer $default
* @param string $field
* @param string $enumPath
* @return integer
*/
public static function getValidatedEnum(int $default, string $field, string $enumPath): int
{
try {
return \intval(Validator::make(
[$field => \request()->get($field)],
[$field => new Enum($enumPath)]
)->validated()[$field]);
} catch (ValidationException $e) {
return $default;
}
}
}

View file

@ -4,6 +4,9 @@ namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Mail\Mailables\Envelope;
use Illuminate\Queue\SerializesModels;
class CodeGenerationEnd extends Mailable
@ -12,41 +15,42 @@ class CodeGenerationEnd extends Mailable
use SerializesModels;
/**
* Number of codes generated
* Create a new job instance.
*
* @var integer
*/
private $quantity;
/**
* The game name
*
* @var string
*/
private $gameName;
/**
* Create a new message instance.
*
* @param integer $quantity
* @param string $gameName
* @param integer $amount Amount of code to generate.
* @param string $gameName The game name.
* @return void
*/
public function __construct(int $quantity, string $gameName)
public function __construct(private int $amount, private string $gameName)
{
$this->quantity = $quantity;
$this->gameName = $gameName;
}
/**
* Build the message.
* Get the message envelope.
*
* @return $this
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function build()
public function envelope(): \Illuminate\Mail\Mailables\Envelope
{
return $this->subject('Fin de génération des codes uniques')
->view('back::mails.generate-end-email')
->with(['quantity' => $this->quantity, 'gameName' => $this->gameName]);
return new Envelope(
to: [new Address(\config('mail.admin'))],
subject: trans('Fin de génération des codes uniques'),
);
}
/**
* Get the message content definition.
*
* @return \Illuminate\Mail\Mailables\Content
*/
public function content(): \Illuminate\Mail\Mailables\Content
{
return new Content(
markdown: 'back::mails.generate-end-email',
with: [
'quantity' => $this->amount,
'gameName' => $this->gameName
],
);
}
}

View file

@ -11,12 +11,6 @@ class JobFailed extends Mailable
use Queueable;
use SerializesModels;
/** @var string */
public $job;
/** @var array */
public $exception;
/**
* Create a new message instance.
*
@ -24,11 +18,10 @@ class JobFailed extends Mailable
* @param array $exception
* @return void
*/
public function __construct(string $job, array $exception)
public function __construct(public string $job, public array $exception)
{
$this->job = $job;
$this->exception = $exception;
}
/**
* Build the message.
*

129
app/Models/ActivityLog.php Normal file
View file

@ -0,0 +1,129 @@
<?php
namespace App\Models;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Schema;
/**
* @property string $model Target model.
* @property string $model_id Id of the target model.
* @property string $event Event of this activity (ActivityLogsEventEnum).
* @property string $data Changes.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
*
* @property-read \App\Models\User $user User BelongsTo relation.
*/
class ActivityLog extends Model
{
use HasFactory;
use Notifiable;
/**
* Indicates if the model should be timestamped.
*
* @var boolean
*/
public $timestamps = false;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'is_anonymous',
'model_class',
'model_id',
'event',
'data',
'created_at',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'is_anonymous' => 'bool',
'event' => ActivityLogsEventEnum::class,
'data' => 'json',
'created_at' => 'datetime',
];
// * METHODS
/**
* Add new activity to the activity logs list.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum
* @return void
*/
public static function addActivity(Model $model, ActivityLogsEventEnum $eventEnum): void
{
$isAnonymous = true;
/** @var \Illuminate\Database\Eloquent\Model|null */
$userModel = auth('backend')->user();
if (is_object($userModel)) {
$userModel = $userModel->getKeyForSelectQuery();
$isAnonymous = false;
}
$activity = new self();
$activity->user_id = $userModel;
$activity->is_console = app()->runningInConsole();
$activity->is_anonymous = $isAnonymous;
$activity->model_class = \get_class($model);
$activity->model_id = $model->getKey();
$activity->event = $eventEnum;
$activity->data = static::getChangedColumns($activity, $model, $eventEnum);
$activity->created_at = Carbon::now();
$activity->saveOrFail();
}
/**
* Get old, new columns that has changed.
*
* @param \self $activity
* @param \Illuminate\Database\Eloquent\Model $model
* @param \App\Enums\ActivityLogs\ActivityLogsEventEnum $eventEnum
* @return array|null
*/
public static function getChangedColumns(self $activity, Model $model, ActivityLogsEventEnum $eventEnum): array|null
{
$modelClassName = $activity->model_class;
/** Get type of each fields of the target model */
$targetModel = $activity->model_class::where(
(new $modelClassName())->getRouteKeyName(),
$activity->model_id
)->first();
if ($targetModel != null) {
// phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
$targetModelTypes = collect($targetModel->toArray())
->map(function ($field, $fieldIndex) use ($targetModel) {
return Schema::getColumnType($targetModel->getTable(), $fieldIndex);
})->toArray();
// phpcs:enable
}
/** Return old, new and type of values changed */
return ($eventEnum === ActivityLogsEventEnum::updated) ?
array_intersect_key($targetModelTypes, $model->getChanges()) : null;
}
// * RELATIONS
/**
* User BelongsTo relation.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
}

View file

@ -22,6 +22,13 @@ use Kwaadpepper\Serial\SerialCaster;
*/
class Code extends Model
{
/**
* Indicates if the IDs are auto-incrementing.
*
* @var boolean
*/
public $incrementing = false;
/**
* The attributes that are mass assignable.
*
@ -33,6 +40,17 @@ class Code extends Model
'date_creation'
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'code_used' => 'bool',
'code_exported' => 'bool',
'date_creation' => 'date',
];
/**
* Indicates if the model should be timestamped.
*

View file

@ -1,25 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Content.
*
* @property-read string $key Content key.
* @property string $value Content text
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*/
class Content extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'value',
];
}

View file

@ -7,7 +7,7 @@ use Illuminate\Database\Eloquent\Model;
/**
* Settings.
*
* @property integer $name Name
* @property string $name Name
* @property \Illuminate\Support\Carbon $start_date Start date of the game
* @property \Illuminate\Support\Carbon $end_date End date of the game
* @property int $nb_participation_player Max paticipation per player

87
app/Models/StaticPage.php Normal file
View file

@ -0,0 +1,87 @@
<?php
namespace App\Models;
use App\Enums\StaticPageType;
use App\Lib\Helpers\ToolboxHelper;
use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
/**
* @property \App\Enums\StaticPageType $type Page type (StaticPageEnum).
* @property string $seo_title Seo title about 70 chars as recommended.
* @property string $seo_description Seo description about 160 chars as recommended.
* @property string $title Title for the page.
* @property string $slug Slug of the title for the page.
* @property string $content WYSIWYG text content using ckeditor.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*/
class StaticPage extends Model
{
use ActivityLog;
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'seo_title',
'seo_description',
'title',
'content',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'type' => StaticPageType::class,
];
/**
* Perform any actions required after the model boots.
*
* @return void
*/
protected static function booted(): void
{
static::creating(function (self $model) {
// * ASSERTIONS
static::assertTitleIsUnique($model->title);
// * CHANGES
$model->slug = Str::slug($model->title);
});
static::updating(function (self $model) {
// * ASSERTIONS
static::assertTitleIsUnique($model->title, $model->id);
// * CHANGES
$model->slug = Str::slug($model->title);
});
}
// * METHODS
/**
* Asserts using validation that the title is unique
*
* @param string|null $title
* @param integer|null $id
* @return void
* @throws \Illuminate\Validation\ValidationException If an actu title already exists.
*/
private static function assertTitleIsUnique(string|null $title, ?int $id = null)
{
$table = (new self())->getTable();
ToolboxHelper::assertFieldIsUnique($table, 'title', $title, $id);
ToolboxHelper::assertFieldIsUnique($table, 'slug', Str::slug($title), $id);
}
}

View file

@ -3,49 +3,50 @@
namespace App\Models;
use App\Enums\Users\RoleEnum;
use App\Traits\Models\ActivityLog;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
/**
* User.
* @property string $first_name Prénom.
* @property string $last_name Nom.
* @property string $email Email.
* @property string $password Mot de passe.
* @property \App\Enums\Users\RoleEnum $role Rôle.
* @property \Illuminate\Support\Carbon $published État de publication.
* @property \Illuminate\Support\Carbon $published_at Date de mise à jour de l'état de publication.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*
* @property string $firstname Firstname
* @property string $lastname Lastname
* @property string $email Email
* @property-read \Illuminate\Support\Carbon $email_verified_at Date of email verification.
* @property string $password Password
* @property-read string $remember_token Remember token
* @property \App\Enums\Users\RoleEnum $role Role of the user
* @property-read \Illuminate\Support\Carbon $created_at Creation Date.
* @property-read \Illuminate\Support\Carbon $updated_at Last update Date.
*
* @method protected static function booted() The "booted" method of the model.
* @method private static function updatePassword($model) Update the password if needed.
* @property-read \App\Models\ActivityLog $activityLogs Activities logs One-to-many relationship.
*/
class User extends Authenticatable
{
use ActivityLog;
use HasFactory;
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
* @var array<int, string>
*/
protected $fillable = [
'firstname',
'lastname',
'first_name',
'last_name',
'email',
'password',
'role',
'published',
'published_at',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array
* @var array<int, string>
*/
protected $hidden = [
'password',
@ -53,13 +54,14 @@ class User extends Authenticatable
];
/**
* Cast the property to an enum.
* The attributes that should be cast.
*
* @var array
* @var array<string, string>
*/
protected $enumCasts = [
'role' => RoleEnum::class,
protected $casts = [
'role' => RoleEnum::class,
'email_verified_at' => 'datetime',
'published' => 'boolean',
];
/**
@ -67,15 +69,30 @@ class User extends Authenticatable
*
* @return void
*/
protected static function booted()
protected static function booted(): void
{
// * EVENT CLOSURES.
static::creating(function (self $model) {
static::updatePassword($model);
static::setOrder($model);
// * Prevent user elevation privileges
throw_if(
auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(),
AuthorizationException::class
);
});
static::updating(function (self $model) {
static::updatePassword($model);
if (optional(auth('backend')->user())->getKey() === $model->getKey()) {
\validator(
['published' => $model->published],
['published' => 'required|boolean|accepted'],
['published.accepted' => trans('Vous ne pouvez pas déactiver votre propre compte')],
)->validate();
}
// * Prevent user elevation privileges
throw_if(
auth('backend')->user() and auth('backend')->user()->role->value() > $model->role->value(),
AuthorizationException::class
);
});
}
@ -93,4 +110,16 @@ class User extends Authenticatable
$model->password = Hash::make($model->password);
}
}
// * RELATIONS
/**
* Activities logs One-to-many relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function activityLogs(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(ActivityLog::class);
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace App\Observers;
use App\Enums\ActivityLogs\ActivityLogsEventEnum;
use App\Models\ActivityLog;
use Illuminate\Database\Eloquent\Model;
class ModelObserver
{
/**
* Handle the model "created" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function created(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::created);
}
/**
* Handle the model "updated" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function updated(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::updated);
}
/**
* Handle the model "duplicated" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function duplicated(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::duplicated);
}
/**
* Handle the model "deleted" event.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return void
*/
public static function deleted(Model $model): void
{
ActivityLog::addActivity($model, ActivityLogsEventEnum::deleted);
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace App\Policies\Rules;
use App\Enums\Users\RoleEnum;
use App\Models\User;
/**
* Userlevel Policiy Static Rules
*
* @codeCoverageIgnore
*/
abstract class UserStaticRules
{
/**
* Allow everyone.
*
* @return boolean
*/
public static function everyOne(): bool
{
return true;
}
/**
* Allow no one.
*
* @return boolean
*/
public static function noOne(): bool
{
return false;
}
/**
* At least a minimum role
*
* @param \App\Models\User $user
* @param \App\Enums\Users\RoleEnum $minRole
* @return boolean
*/
public static function atLeastRole(User $user, RoleEnum $minRole): bool
{
// phpcs:enable
return $user->role->value() <= $minRole->value();
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Models\StaticPage;
use App\Models\User;
use App\Policies\Rules\UserStaticRules;
class StaticPagePolicy
{
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function viewAny(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\StaticPage $static_page
* @return boolean
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundAfterLastUsed
*/
public function update(User $authUser, StaticPage $static_page): bool
{
// phpcs:enable
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
}

129
app/Policies/UserPolicy.php Normal file
View file

@ -0,0 +1,129 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use App\Policies\Rules\UserStaticRules;
class UserPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function viewAny(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function view(User $authUser, User $user): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
/**
* Determine whether the user can create models.
*
* @param \App\Models\User $authUser
* @return boolean
*/
public function create(User $authUser): bool
{
return UserStaticRules::atLeastRole($authUser, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function update(User $authUser, User $user): bool
{
return $authUser->getRouteKey() === $user->getRouteKey() or
(UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role));
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function delete(User $authUser, User $user): bool
{
return $authUser->getRouteKey() !== $user->getRouteKey() and
UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
/**
* Determine whether the user can restore the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function restore(User $authUser, User $user): bool
{
return $authUser->getRouteKey() !== $user->getRouteKey() and
UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
/**
* Determine whether the user can permanently delete the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function forceDelete(User $authUser, User $user): bool
{
return $authUser->getRouteKey() !== $user->getRouteKey() and
UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
/**
* Determine whether the user can duplicate the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function duplicate(User $authUser, User $user): bool
{
return $authUser->getRouteKey() !== $user->getRouteKey() and
UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
/**
* Determine whether the user can publish/unpublish the model.
*
* @param \App\Models\User $authUser
* @param \App\Models\User $user
* @return boolean
*/
public function changePublished(User $authUser, User $user): bool
{
return $authUser->getRouteKey() !== $user->getRouteKey() and
UserStaticRules::atLeastRole($authUser, RoleEnum::admin) and
UserStaticRules::atLeastRole($authUser, $user->role);
}
}

View file

@ -3,6 +3,8 @@
namespace App\Providers;
use App\Models\Settings;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\URL;
use Illuminate\Support\Pluralizer;
@ -26,6 +28,9 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
Model::shouldBeStrict(!$this->app->environment('production'));
// * HTTPS FIX
URL::forceScheme('https');
// * HA PROXY FIX.
@ -34,17 +39,17 @@ class AppServiceProvider extends ServiceProvider
return request()->getClientIp();
} else {
return request()->server('HTTP_X_FORWARDED_FOR') ??
request()->getClientIp();
request()->getClientIp();
}
});
// * LOCAL FORCE eg: for Carbon.
// * LOCAL FORCE eg: for Carbon
setlocale(LC_ALL, \sprintf(
'%s_%s.UTF-8',
config('app.locale'),
\strtoupper(config('app.locale'))
));
Carbon::setLocale(config('app.locale'));
Pluralizer::useLanguage('french');
app()->singleton(Settings::class, function () {

View file

@ -2,14 +2,17 @@
namespace App\Providers;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
/**
* The policy mappings for the application.
* The model to policy mappings for the application.
*
* @var array
* @var array<class-string, class-string>
*/
protected $policies = [];
@ -18,7 +21,10 @@ class AuthServiceProvider extends ServiceProvider
*
* @return void
*/
public function boot()
public function boot(): void
{
Gate::define('isConceptor', function (User $user) {
return $user->role === RoleEnum::conceptor;
});
}
}

View file

@ -9,9 +9,9 @@ use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvi
class EventServiceProvider extends ServiceProvider
{
/**
* The event listener mappings for the application.
* The event to listener mappings for the application.
*
* @var array
* @var array<class-string, array<int, class-string>>
*/
protected $listen = [
Registered::class => [
@ -19,15 +19,6 @@ class EventServiceProvider extends ServiceProvider
],
];
/**
* Register any events for your application.
*
* @return void
*/
public function boot(): void
{
}
/**
* Determine if events and listeners should be automatically discovered.
*

View file

@ -18,7 +18,7 @@ class TranslationServiceProvider extends ServiceProvider
public function boot(): void
{
foreach ($this->collectLocalesStrings() as $locale) {
// * Supported locales
// * Suported locales
Cache::rememberForever(sprintf('translations.%s', $locale), function () use ($locale) {
$translations = [
'php' => $this->phpTranslations($locale),

View file

@ -2,6 +2,7 @@
namespace App\Providers;
use App\Enums\Users\RoleEnum;
use App\Models\Prize;
use App\Models\Settings;
use Illuminate\Pagination\Paginator;
@ -25,16 +26,25 @@ class ViewServiceProvider extends ServiceProvider
View::addNamespace('back', resource_path('views'));
View::addNamespace('theme', resource_path('theme/mounted/views'));
if (!app()->runningInConsole()) {
$appInfos = Cache::remember('composer', 3600, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
View::share('name', $appInfos->name);
View::share('version', $appInfos->version);
View::share('license', $appInfos->license);
// * FORCE BOOTSTRAP PAGINATOR
Paginator::defaultView('back::modules.pagination');
$appInfos = Cache::remember('composer', 360, function () {
return json_decode(File::get(\app_path('../composer.json')));
});
// Share composer.json infos.
if (isset($appInfos->name) and isset($appInfos->version)) {
View::share('cpsr_name', $appInfos->name);
View::share('cpsr_version', $appInfos->version);
}
if (!app()->runningInConsole()) {
// * FORCE BOOTSTRAP PAGINATOR (or custom if in front)
// Early boot, request wont be filled as expected.
if (collect(explode('/', \request()->getPathInfo()))->get(1) === 'bo') {
Paginator::defaultView('modules.pagination');
} else {
Paginator::useBootstrapFive();
}
// FIXME: USe trait cache here
// On ramène le nom du jeu et le nom de la société sur toute les pages.
$settingsGlobal = Cache::get('settingsGlobal');
if (!$settingsGlobal) {
@ -53,9 +63,15 @@ class ViewServiceProvider extends ServiceProvider
}
View::share('prizesGlobal', $prizesGlobal);
// * CUSTOM IF STATEMENTS
Blade::if('admin', function () {
return auth('backend')->user() and auth('backend')->user()
->role->value() <= RoleEnum::admin->value();
});
Blade::if('superadmin', function () {
return auth()->user() && auth()->user()->role == 1;
return auth('backend')->user() and auth('backend')->user()
->role->equals(RoleEnum::conceptor);
});
} //end if
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Traits\Controllers;
use App\Http\Requests\Bo\UpdateChangeOrderRequest;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
trait ChangesModelOrder
{
/**
* Change order.
*
* @param \App\Http\Requests\Bo\UpdateChangeOrderRequest $request
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Http\RedirectResponse
* @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au model utilisé.
*/
public function changeOrder(UpdateChangeOrderRequest $request, Model $model): \Illuminate\Http\RedirectResponse
{
$params = collect(request()->route()->parameterNames);
$className = Str::ucfirst(Str::lower($params->get($params->count() - 2)));
$className = "\\App\\Models\\$className";
if (!class_exists($className)) {
throw new \RuntimeException("The $className class doesn't exist.");
}
/** @var \Illuminate\Database\Eloquent\Model|null */
$model = $className::where($model->getRouteKeyName(), $model->getRouteKey())->first();
if (!$model) {
return back()->with('error', trans('crud.messages.order_not_changed'));
}
return DB::transaction(function () use ($request, $className, $model) {
/** @var \Illuminate\Database\Eloquent\Model|null */
$tmp = $className::where('order', $request->direction ? '<' : '>', $model->order)
->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first();
if (!$tmp) {
return back()->with('error', trans('crud.messages.order_not_changed'));
}
$newOrder = $tmp->order;
$tmp->order = $model->order;
$model->order = $newOrder;
$tmp->saveOrFail();
$model->saveOrFail();
Session::flash('success', trans('crud.messages.order_changed'));
return back();
});
}
}

View file

@ -0,0 +1,26 @@
<?php
namespace App\Traits\Controllers;
use Illuminate\Database\Eloquent\Model;
/**
* This trait works with Route::bind in App service provider
* to resolve the model and the DB row
* It does not work with binder Service
*/
trait UpdateModelPublished
{
/**
* Change model published.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @return \Illuminate\Http\RedirectResponse
*/
public function changePublished(Model $model)
{
$model->published = !$model->published;
$model->saveOrFail();
return redirect()->back()->with('success', trans(__('crud.messages.publish_status_saved')));
}
}

View file

@ -4,14 +4,12 @@ namespace App\Traits\Enums;
use Illuminate\Support\Str;
/**
* @author Jérémy Munsch <jeremy.munsch@ldc.fr>
*/
trait BaseEnum
{
// * GENERIC METHODS
/**
* Get enum value
* Get enum value.
*
* @return string|integer
*/
@ -21,7 +19,7 @@ trait BaseEnum
}
/**
* Get enum label
* Get enum label.
*
* @return string
*/
@ -31,7 +29,7 @@ trait BaseEnum
}
/**
* Get enum name
* Get enum name.
*
* @return string
*/
@ -43,7 +41,7 @@ trait BaseEnum
// * STATIC FETCH METHODS
/**
* Get enum labels
* Get enum labels.
*
* @return array<string>
*/
@ -65,7 +63,7 @@ trait BaseEnum
}
/**
* Get enum values
* Get enum values.
*
* @return array<integer|string>
*/
@ -79,7 +77,7 @@ trait BaseEnum
}
/**
* Get enums
* Get enums.
*
* @return array
*/
@ -89,7 +87,7 @@ trait BaseEnum
}
/**
* Get enums
* Get enums.
*
* @return array
*/
@ -118,30 +116,36 @@ trait BaseEnum
}
/**
* Fetch enum using value
* Fetch enum using value.
*
* @param \App\Traits\Enums\BaseEnum|integer|string $value
* @param string $field
* @return \App\Traits\Enums\BaseEnum|null
*/
public static function make(self|int|string $value): self|null
public static function make(self|int|string $value, string $field = 'value'): self|null
{
if ($field === "label") {
$enumLabels = array_change_key_case(array_flip(self::toLabels()), CASE_LOWER);
$value = (isset($enumLabels[strtolower($value)])) ? $enumLabels[strtolower($value)] : false;
$field = 'name';
}
return !\is_object($value) ?
\collect(self::cases())->keyBy('value')->get($value) : $value;
\collect(self::cases())->keyBy($field)->get($value) : $value;
}
/**
* Test if 2 enum are equals
* Test if 2 enum are equals.
*
* @param object ...$others
* @return boolean
* @throws \Error If provided element are not enums taht extends BaseEnum.
* @throws \Error If provided element are not enums that extends BaseEnum.
*/
public function equals(object ...$others): bool
{
foreach ($others as $other) {
if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) {
throw new \Error(__METHOD__ .
' requires $enum to use ' . BaseEnum::class);
' requires $enum to use ' . BaseEnum::class);
}
if (
get_class($this) === get_class($other)
@ -154,7 +158,7 @@ trait BaseEnum
}
/**
* Serialize to json
* Serialize to json.
*
* @return mixed
*/
@ -167,7 +171,7 @@ trait BaseEnum
// * CACHE
/**
* Get cache array
* Get cache array.
*
* @param string $index
* @return array
@ -182,7 +186,7 @@ trait BaseEnum
}
/**
* Set cache array
* Set cache array.
*
* @param string $index
* @param array $items
@ -198,7 +202,7 @@ trait BaseEnum
}
/**
* Init cache if needed
* Init cache if needed.
*
* @return void
*/
@ -219,7 +223,7 @@ trait BaseEnum
// * PRIVATE
/**
* Fetch the LABELS constant if exists
* Fetch the LABELS constant if exists.
*
* @return array|null
*/
@ -235,7 +239,7 @@ trait BaseEnum
}
/**
* Get custom constants
* Get custom constants.
*
* @return array
*/

View file

@ -0,0 +1,23 @@
<?php
namespace App\Traits\Models;
use App\Observers\ModelObserver;
/**
* If there is a static function in a trait (named boot[TraitName]),
* it will be executed as the boot() function would on an Eloquent model.
* Futhermore, event in the Model observer doesn't erase actual events in the model,
* but adds them.
*/
trait ActivityLog
{
/**
*
* @return void
*/
public static function bootActivityLog(): void
{
static::observe(ModelObserver::class);
}
}

View file

@ -1,162 +0,0 @@
<?php
namespace LDCCRM;
class ApiV1
{
private $endpoint;
private $api_key;
public function __construct($endpoint, $api_key)
{
$this->endpoint = $endpoint;
$this->api_key = $api_key;
}
public function getCustomer($id){
return $this->callAPI('GET', "/customer/id/$id");
}
public function getCustomerByEmail($email){
return $this->callAPI('GET', "/customer/email/$email");
}
public function createCustomer($email, $password, $firstname, $lastname, $optin, $birthdate = null, $phone = null, $address = null, $address2 = null, $city = null, $postcode = null, $country = null){
$user = [
'email' => $email,
'password' => $password,
'firstname' => $firstname,
'lastname' => $lastname,
'opt_in' => $optin,
'birth_date' => $birthdate,
'phone' => $phone,
'address' => $address,
'address2' => $address2,
'city' => $city,
'postcode' => $postcode,
'country' => $country
];
return $this->callAPI('POST', '/customer/create', $user);
}
public function updateCustomer($crmId, $firstname = null, $lastname = null, $optin = null, $birthdate = null, $phone = null, $address = null, $address2 = null, $city = null, $postcode = null, $country = null){
$user = [];
if ($firstname !== null)
$user['firstname'] = $firstname;
if ($lastname !== null)
$user['lastname'] = $lastname;
if ($optin !== null)
$user['opt_in'] = $optin;
if ($birthdate !== null)
$user['birth_date'] = $birthdate;
if ($phone !== null)
$user['phone'] = $phone;
if ($address !== null)
$user['address'] = $address;
if ($address2 !== null)
$user['address2'] = $address2;
if ($city !== null)
$user['city'] = $city;
if ($postcode !== null)
$user['postcode'] = $postcode;
if ($country !== null)
$user['country'] = $country;
return $this->callAPI('PUT', "/customer/$crmId", $user);
}
public function deleteCustomer($id){
return $this->callAPI('DELETE', "/customer/$id");
}
public function changePwdCustomer($id, $password){
return $this->callAPI('POST', "/customer/$id/change-password", ['password' => $password]);
}
public function activateCustomer($id){
return $this->callAPI('POST', "/customer/$id/activate");
}
public function toggleOptInCustomer($id){
return $this->callAPI('POST', "/customer/$id/toggle-optin");
}
public function loginCustomer($email, $password){
return $this->callAPI('POST', "/customer/login", ['email' => $email, 'password' => $password]);
}
public function createSingleOptin($email){
return $this->callAPI('POST', '/single-optin', ['email' => $email]);
}
public function ArcadiaOutgoingMail($crm_id, $fields, $type){
return $this->callAPI('POST', '/arcadia/outgoing-mail', ['crm_id' => $crm_id, 'fields' => json_encode($fields), 'type' => $type]);
}
private function callAPI($method, $url, $data = false){
if (strpos($this->endpoint, "dev")){
if ($data !== false){
$data = array_merge($data, ['api_token' => $this->api_key]);
}else{
$data = ['api_token' => $this->api_key];
}
}
$url = $this->endpoint . '/api/v1' . $url;
$curl = curl_init();
switch ($method){
case "POST":
curl_setopt($curl, CURLOPT_POST, 1);
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
break;
case "PUT":
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT");
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
break;
case "DELETE":
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE");
if ($data)
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($data));
break;
default:
if ($data)
$url = sprintf("%s?%s", $url, http_build_query($data));
}
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_COOKIESESSION, TRUE);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
if (!strpos($this->endpoint, "dev")) {
curl_setopt($curl, CURLOPT_HTTPHEADER, array(
'Accept: application/json',
'Authorization: Bearer ' . $this->api_key
));
}
$result = curl_exec($curl);
if (!curl_errno($curl)) {
$http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if (!in_array($http_code, [200, 201]))
return (object) ['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed - Code HTTP : '. $http_code];
} else {
return (object) ['status' => 'KO', 'code' => 500, 'message' => 'Connection to API failed : '. curl_error($curl)];
}
curl_close($curl);
return json_decode($result);
}
}

View file

@ -1,30 +1,31 @@
{
"name": "laravel/laravel",
"name": "uwil/gen-game-web",
"version": "2.0.0",
"type": "project",
"description": "The Laravel Framework.",
"description": "Game generator",
"support": {
"email": "equipe.web@ldc.fr"
},
"keywords": [
"framework",
"game",
"laravel"
],
"license": "MIT",
"require": {
"php": "^8.1",
"php": "^8.2",
"diglactic/laravel-breadcrumbs": "^8.0",
"guzzlehttp/guzzle": "^7.2",
"laravel/framework": "^10.0",
"laravel/tinker": "^2.8",
"laravel/ui": "^4.2",
"mews/purifier": "^3.4",
"sentry/sentry-laravel": "*",
"kwaadpepper/enum": "^2.1",
"kwaadpepper/serial-int-caster": "^1.0"
"sentry/sentry-laravel": "*"
},
"require-dev": {
"doctrine/dbal": "^3.6",
"fakerphp/faker": "^1.9.1",
"kitloong/laravel-migrations-generator": "^6.8",
"krlove/eloquent-model-generator": "dev-l10-compatibility",
"laravel-lang/common": "^4.0",
"laravel/pint": "^1.0",
"laravel/sail": "^1.18",
"mockery/mockery": "^1.4.4",
@ -32,7 +33,6 @@
"phpunit/phpunit": "^10.0",
"slevomat/coding-standard": "^8.10",
"spatie/laravel-ignition": "^2.0",
"squizlabs/php_codesniffer": "^3.6",
"thedoctor0/laravel-factory-generator": "^1.2"
},
"autoload": {

View file

@ -108,7 +108,7 @@ return [
|
*/
'faker_locale' => 'en_US',
'faker_locale' => 'fr_FR',
/*
|--------------------------------------------------------------------------
@ -125,6 +125,23 @@ return [
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Maintenance Mode Driver
|--------------------------------------------------------------------------
|
| These configuration options determine the driver used to determine and
| manage Laravel's "maintenance mode" status. The "cache" driver will
| allow maintenance mode to be controlled across multiple machines.
|
| Supported drivers: "file", "cache"
|
*/
'maintenance' => [
'driver' => 'file'
],
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
@ -179,7 +196,6 @@ return [
App\Providers\RouteServiceProvider::class,
App\Providers\TranslationServiceProvider::class,
App\Providers\ViewServiceProvider::class,
],
/*

View file

@ -31,7 +31,7 @@ return [
| users are actually retrieved out of your database or other storage
| mechanisms used by this application to persist your user's data.
|
| Supported: "session", "token"
| Supported: "session"
|
*/
@ -43,6 +43,7 @@ return [
'frontend' => [
'driver' => 'session',
// ! Remplacer "users" par le model authenticable voulu
'provider' => 'customers',
],
],
@ -67,11 +68,11 @@ return [
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
'model' => \App\Models\User::class,
],
'customers' => [
'driver' => 'eloquent',
'model' => App\Models\Customer::class,
'model' => \App\Models\Customer::class,
],
],
@ -84,22 +85,26 @@ return [
| than one user table or model in the application and you want to have
| separate password reset settings based on the specific user types.
|
| The expire time is the number of minutes that the reset token should be
| The expiry time is the number of minutes that each reset token will be
| considered valid. This security feature keeps tokens short-lived so
| they have less time to be guessed. You may change this as needed.
|
| The throttle setting is the number of seconds a user must wait before
| generating more password reset tokens. This prevents the user from
| quickly generating a very large amount of password reset tokens.
|
*/
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],
'customers' => [
'provider' => 'customers',
'table' => 'password',
'table' => 'password_resets',
'expire' => 60,
'throttle' => 60,
],

View file

@ -23,7 +23,7 @@ return [
|
*/
'view' => 'breadcrumbs/breadcrumb-layout',
'view' => 'modules/breadcrumbs/breadcrumb-layout',
/*
|--------------------------------------------------------------------------

View file

@ -11,7 +11,7 @@ return [
| framework when an event needs to be broadcast. You may set this to
| any of the connections defined in the "connections" array below.
|
| Supported: "pusher", "redis", "log", "null"
| Supported: "pusher", "ably", "redis", "log", "null"
|
*/

View file

@ -13,9 +13,6 @@ return [
| using this caching library. This connection is used when another is
| not explicitly specified when executing a given caching function.
|
| Supported: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb"
|
*/
'default' => env('CACHE_DRIVER', 'file'),
@ -29,6 +26,9 @@ return [
| well as their drivers. You may even define multiple stores for the
| same cache driver to group types of items stored in your caches.
|
| Supported drivers: "apc", "array", "database", "file",
| "memcached", "redis", "dynamodb", "octane", "null"
|
*/
'stores' => [
@ -99,9 +99,9 @@ return [
| Cache Key Prefix
|--------------------------------------------------------------------------
|
| When utilizing a RAM based store such as APC or Memcached, there might
| be other applications utilizing the same cache. So, we'll specify a
| value to get prefixed to all our keys so we can avoid collisions.
| When utilizing the APC, database, memcached, Redis, or DynamoDB cache
| stores there might be other applications using the same cache. For
| that reason, you may prefix every cache key to avoid collisions.
|
*/

View file

@ -13,20 +13,7 @@ return [
|
*/
'default' => env('FILESYSTEM_DRIVER', 'local'),
/*
|--------------------------------------------------------------------------
| Default Cloud Filesystem Disk
|--------------------------------------------------------------------------
|
| Many applications store files both locally and in the cloud. For this
| reason, you may specify a default "cloud" driver here. This driver
| will be bound as the Cloud disk implementation in the container.
|
*/
'cloud' => env('FILESYSTEM_CLOUD', 's3'),
'default' => env('FILESYSTEM_DISK', 'local'),
/*
|--------------------------------------------------------------------------
@ -35,7 +22,7 @@ return [
|
| Here you may configure as many filesystem "disks" as you wish, and you
| may even configure multiple disks of the same driver. Defaults have
| been setup for each driver as an example of the required options.
| been set up for each driver as an example of the required values.
|
| Supported Drivers: "local", "ftp", "sftp", "s3"
|
@ -78,7 +65,6 @@ return [
],
/*
|--------------------------------------------------------------------------
| Symbolic Links

View file

@ -29,7 +29,7 @@ return [
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
'rounds' => env('BCRYPT_ROUNDS', 12),
],
/*

View file

@ -3,6 +3,7 @@
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
use Monolog\Processor\PsrLogMessageProcessor;
return [

View file

@ -15,13 +15,6 @@ return [
'default' => env('MAIL_MAILER', 'smtp'),
// Mail admin pour l'envoi de mail comme la fin de la génération des codes uniques.
'MAIL_ADMIN' => env('MAIL_ADMIN'),
'contactsList' => [
'serviceweb' => env('MAIL_ADMIN', 'equipe.web@ldc.fr')
],
/*
|--------------------------------------------------------------------------
| Mailer Configurations
@ -35,8 +28,8 @@ return [
| sending an e-mail. You will specify which one you are using for your
| mailers below. You are free to add additional mailers as required.
|
| Supported: "smtp", "sendmail", "mailgun", "ses",
| "postmark", "log", "array"
| Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2",
| "postmark", "log", "array", "failover"
|
*/
@ -118,11 +111,10 @@ return [
'theme' => 'default',
'paths' => [
// * For custom compiled css.
public_path('build/theme/css/mail'),
// * For generic customized blade (moteur seulement)
resource_path('views/vendor/mail'),
],
],
'admin' => env('MAIL_ADMIN')
];

View file

@ -101,7 +101,7 @@ return [
*/
'failed' => [
'driver' => env('QUEUE_FAILED_DRIVER', 'database'),
'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'),
'database' => env('DB_CONNECTION', 'mysql'),
'table' => 'failed_jobs',
],

View file

@ -72,7 +72,7 @@ return [
|
*/
'connection' => env('SESSION_CONNECTION', null),
'connection' => env('SESSION_CONNECTION'),
/*
|--------------------------------------------------------------------------
@ -164,7 +164,7 @@ return [
|
| By setting this option to true, session cookies will only be sent back
| to the server if the browser has a HTTPS connection. This will keep
| the cookie from being sent to you if it can not be done securely.
| the cookie from being sent to you when it can't be done securely.
|
*/

View file

@ -0,0 +1,44 @@
<?php
namespace Database\Factories;
use App\Enums\Users\RoleEnum;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition()
{
$published = $this->faker->boolean(75);
return [
'first_name' => fake()->firstname(),
'last_name' => fake()->lastname(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
'remember_token' => Str::random(10),
'role' => \collect(RoleEnum::toArray())->random()->value,
'published' => $published,
'published_at' => ($published) ? now() : null,
];
}
/**
* Indicate that the model's email address should be unverified.
*
* @return static
*/
public function unverified(): static
{
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}

View file

@ -15,13 +15,15 @@ return new class extends Migration
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('firstname')->comment('Firstname of the user.');
$table->string('lastname')->comment('Lastname of the user.');
$table->string('first_name')->comment('First name of the user.');
$table->string('last_name')->comment('Last name of the user.');
$table->string('email')->unique()->comment('Email of the user.');
$table->timestamp('email_verified_at')->nullable();
$table->string('password')->comment('Password of the user account.');
$table->rememberToken();
$table->boolean('role')->comment('Role of the user who defines the rights/access.');
$table->unsignedTinyInteger('role')->comment('Role of the user who defines the rights/access.');
$table->boolean('published')->comment('The user is acivated or not.');
$table->timestamp('published_at')->nullable()->comment('The date on which the user was acivated.');
$table->timestamps();
});
}

View file

@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePasswordResetsTable extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@ -29,4 +29,4 @@ class CreatePasswordResetsTable extends Migration
{
Schema::dropIfExists('password_resets');
}
}
};

View file

@ -4,7 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFailedJobsTable extends Migration
return new class extends Migration
{
/**
* Run the migrations.
@ -15,7 +15,7 @@ class CreateFailedJobsTable extends Migration
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->nullable()->unique();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
@ -33,4 +33,4 @@ class CreateFailedJobsTable extends Migration
{
Schema::dropIfExists('failed_jobs');
}
}
};

View file

@ -14,7 +14,7 @@ class CreateCodes extends Migration
public function up()
{
Schema::create('codes', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('id')->primary();
$table->boolean('code_used')->default(0);
$table->boolean('code_exported')->default(0);
$table->date('date_creation');

View file

@ -1,33 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateContents extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('contents', function (Blueprint $table) {
$table->id();
$table->string('key');
$table->text('value');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('content');
}
}

View file

@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('activity_logs', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->nullable()->constrained('users')
->comment('Id of the user associated, or nullable for a deleted user.');
$table->boolean('is_anonymous')->comment('If there is no user connected when action was realised.');
$table->boolean('is_console')->comment('If the action was realised in console.');
$table->string('model_class')->comment('Target model.');
$table->integer('model_id')->comment('Id of the target model.');
$table->integer('event')->comment('Event of this activity (ActivityLogsEventEnum).');
$table->json('data')->nullable()->comment('List of changes (old and new values).');
$table->timestamp('created_at')->comment('The date on which the user was published.');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('activity_logs');
}
};

View file

@ -0,0 +1,46 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up(): void
{
Schema::create('static_pages', function (Blueprint $table) {
$table->id();
$table->unsignedTinyInteger('type')->unique()->comment('Page type (StaticPageEnum)');
$table->string('seo_title')->comment('Seo title about 70 chars as recommended');
$table->string('seo_description')->comment('Seo description about 160 chars as recommended');
$table->string('title')->comment('Title for the page');
$table->string('slug')->unique()->comment('Unique title slug');
$table->text('content')->comment('WYSIWYG text content using ckeditor');
$table->timestamps();
});
Artisan::call('db:seed', [
'class' => 'StaticPageSeeder'
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down(): void
{
Schema::dropIfExists('static_pages');
}
};

View file

@ -1,33 +0,0 @@
<?php
namespace Database\Seeders;
use App\Models\Content;
use Illuminate\Database\Seeder;
class ContentSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$mentions = new Content();
$mentions->key = "mentions legales";
$mentions->value = "...";
$charte = new Content();
$charte->key = "charte";
$charte->value = "...";
$reglement = new Content();
$reglement->key = "reglement";
$reglement->value = "...";
$mentions->save();
$charte->save();
$reglement->save();
}
}

View file

@ -14,7 +14,7 @@ class DatabaseSeeder extends Seeder
public function run()
{
$this->call(SettingsSeeder::class);
$this->call(ContentSeeder::class);
$this->call(StaticPageSeeder::class);
$this->call(PrizeSeeder::class);
}
}

View file

@ -0,0 +1,55 @@
<?php
namespace Database\Seeders;
use App\Enums\StaticPageType;
use App\Models\StaticPage;
use Illuminate\Database\Seeder;
class StaticPageSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
StaticPage::query()->updateOrCreate(
[
'type' => StaticPageType::contact,
],
[
'seo_title' => 'Seo title pour la page contactez nous',
'seo_description' => 'Seo description pour la page contactez nous',
'title' => 'Contactez-nous',
'type' => StaticPageType::contact,
'content' => \clean(\fake()->randomHtml(6, 7)),
]
);
StaticPage::query()->updateOrCreate(
[
'type' => StaticPageType::legals,
],
[
'seo_title' => 'Seo title pour la page mentions légales',
'seo_description' => 'Seo description pour la page mentions légales',
'title' => 'Mentions légales',
'type' => StaticPageType::legals,
'content' => \clean(\fake()->randomHtml(6, 7)),
]
);
StaticPage::query()->updateOrCreate(
[
'type' => StaticPageType::rules,
],
[
'seo_title' => 'Seo title pour la page charte de confidentialitée',
'seo_description' => 'Seo description pour la page charte de confidentialitée',
'title' => 'Charte de confidentialitée',
'type' => StaticPageType::rules,
'content' => \clean(\fake()->randomHtml(6, 7)),
]
);
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Seeder;
class UserSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
User::factory(20)->create();
}
}

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