First developed project version

This commit is contained in:
Jérémy Munsch 2021-05-08 06:50:19 +02:00
parent 28e75bc2d7
commit 1af6018447
326 changed files with 53983 additions and 353 deletions

View file

@ -10,7 +10,7 @@ LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_DATABASE=portsalaries
DB_USERNAME=root
DB_PASSWORD=
@ -20,30 +20,13 @@ QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
MAIL_MAILER=
MAIL_HOST=
MAIL_PORT=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_ENCRYPTION=
MAIL_FROM_ADDRESS="no-reply@portsalaries.test"
MAIL_FROM_NAME="Ne pas répondre (Poartail Salariés)"
MAIL_LIST_SERVICEWEB=informatique.internet@ldc.fr

8
.gitignore vendored
View file

@ -1,7 +1,13 @@
/node_modules
/public/hot
/public/storage
/public/js/*
/public/css/*
/public/fonts/*
/public/uploads/*
mix-manifest.json
/storage/*.key
/storage/framework/laravel-excel/*
/vendor
.env
.env.backup
@ -11,3 +17,5 @@ Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log
workspace.code-workspace
.vscode

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,49 @@
@extends("layouts.app")
@section('title')
Mot de passe expiré | Portail Salariés
@endsection
@section('content')
<form method="post" action=" ">
@csrf
<img src="{{ asset('images/logo_LDC.jpg') }}" alt="Logo-LDC">
<h1>Portail Salariés</h1>
<h1 class="passExpired">Mot de passe expiré</h1>
<div class="instructions">
<h2>Votre mot de passe est expiré, vous devez le changez pour accéder à votre compte</h2>
<p>Il doit respecter les conditions suivantes : </p>
<ul>
<li>Au moins une lettre majuscule</li>
<li>Au moins une lettre minuscule</li>
<li>Au moins 10 caractères</li>
<li>Au plus 13 caractères</li>
<li>Au moins un chiffre</li>
<li>Au moins un symbole (% ! : ; # - _ = )</li>
</ul>
</div>
<label for="password">Nouveau Mot de passe</label> <br>
<input type="password" name="password" id="password" placeholder="Saisir votre nouveau mot de passe" >
<div class="changeMDPError active">
<p class="changeMDPError_content">Mot de passe invalide</p>
</div>
<label for="confirmPassword">Confirmer mot de passe</label> <br>
<input type="password" name="confirmPassword" id="confirmPassword" placeholder="Confirmer votre mot de passe">
<div class="changeMDPError active">
<p class="changeMDPError_content">Mot de passe invalide</p>
</div>
<div class="formLoginBottom ">
<input type="submit" name="submit" value="Sauvegarder" id="centerBtn">
</div>
</form>
@endsection

View file

@ -0,0 +1,22 @@
@extends("layouts.app")
@section('title')
Mot de passe oublié | Portail Salariés
@endsection
@section('content')
<form method="post" action=" ">
@csrf
<img src="{{ asset('images/logo_LDC.jpg') }}" alt="Logo-LDC">
<h1>Portail Salariés</h1>
<h1 class="passExpired">Mot de passe Oublié?</h1>
<div class="instructions">
<p>Vous recevrez un lien à votre adresse mail pour réinitialiser votre mot de passe.</p>
</div>
<label for="login">Matricule</label> <br>
<input type="text" name="login" id="login" placeholder="Saisir votre Matricule" > <br>
<div class="formLoginBottom">
<input type="submit" name="submit" value="Recevoir le lien" id="centerBtn">
</div>
</form>
@endsection

47
app/Casts/HtmlColor.php Normal file
View file

@ -0,0 +1,47 @@
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
/**
* Custom cast Html Color
* https://laravel.com/docs/8.x/eloquent-mutators#custom-casts
*/
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
class HtmlColor implements CastsAttributes
{
/**
* Cast the given value.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param mixed $value
* @param array $attributes
* @return string
*/
public function get($model, $key, $value, $attributes)
{
return "#" . $this->zeropad(dechex($value), 6);
}
/**
* Prepare the given value for storage.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param string $key
* @param array $value
* @param array $attributes
* @return int
*/
public function set($model, $key, $value, $attributes)
{
return \intval(hexdec(ltrim($value, '#')));
}
private function zeropad($num, $lim)
{
return (strlen($num) >= $lim) ? $num : $this->zeropad("0" . $num, $lim);
}
}

23
app/Casts/NiceDate.php Normal file
View file

@ -0,0 +1,23 @@
<?php
namespace App\Casts;
use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use Illuminate\Support\Carbon;
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed
//phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
class NiceDate implements CastsAttributes
{
public function get($model, $key, $value, $attributes)
{
$value = Carbon::parse($value);
return $value->isToday() ?
trans('Aujourd\'hui à :time', ['time' => $value->format('H:i')]) : $value->format('Y/m/d H:i');
}
public function set($model, $key, $value, $attributes)
{
return Carbon::parse($value)->format('Y-m-d H:i:s');
}
}

View file

@ -0,0 +1,44 @@
<?php
namespace App\Console\Commands;
use App\Models\User;
use Illuminate\Console\Command;
/**
* Changes the user password
*/
class UserChangePassword extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:password';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Changes a user password';
public function handle()
{
$email = $this->ask('What is the email?');
$user = User::where('email', $email)->first();
if (!$user) {
$this->error(sprintf('User %s could not be found', $email));
return -1;
}
$user->password = $this->secret('What is the password?');
$user->saveOrFail();
$this->info('User updated.');
return 0;
}
}

View file

@ -0,0 +1,118 @@
<?php
namespace App\Console\Commands;
use App\Enums\Role;
use App\Models\Site;
use App\Models\User;
use Exception;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
/**
* Creates a new user
*/
class UserCreate extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'user:create';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a backend user';
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$sitename = $this->ask('For which site would you like to create a user ?');
$site = Site::where('name', 'LIKE', "%$sitename%")->first();
if ($site) {
if (!$this->confirm(sprintf('Would you like to use %s ? ', $site->name))) {
$this->info('Command cancelled');
return 0;
}
} else {
$this->error(sprintf('Site %s not found cancelled', $sitename));
return 0;
}
$name = null;
while (is_null($name)) {
try {
$tmp = $this->ask('What is user the name?');
$validator = Validator::make(['nom' => $tmp], [
'nom' => 'required|min:5,max:255'
]);
$validator->validate();
$name = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid name %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$email = null;
while (is_null($email)) {
try {
$tmp = $this->ask('What is the email?');
$validator = Validator::make(['email' => $tmp], [
'email' => 'required|email:rfc,dns'
]);
$validator->validate();
$email = $tmp;
} catch (ValidationException $e) {
$this->error(sprintf(
'Please provide a valid email %s',
\implode(',', collect($e->errors())->flatten()->all())
));
continue;
}
}
$role = null;
while (is_null($role)) {
try {
$num = $this->ask(sprintf(
'What is the user role %s ?',
implode(',', collect(Role::toArray())->map(function ($role) {
return sprintf('(%s) %d', $role->label, $role->value);
})->all())
));
if (!\is_numeric($num)) {
$this->error('Please provide a valid role.');
continue;
}
$role = Role::make(intval($num))->value;
} catch (Exception $e) {
$this->error('Please provide a valid role.');
continue;
}
}
$password = $this->secret('What is the password?');
$user = new User([
'name' => $name,
'email' => $email,
'role' => $role,
'password' => $password,
'site_id' => $site->id
]);
$user->saveOrFail();
$this->info('User created.');
return 0;
}
}

View file

@ -2,18 +2,22 @@
namespace App\Console;
use App\Console\Commands\UserChangePassword;
use App\Console\Commands\UserCreate;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel
{
/**
* The Artisan commands provided by your application.
*
* @var array
*/
protected $commands = [
//
UserChangePassword::class,
UserCreate::class
];
/**
@ -34,7 +38,7 @@ class Kernel extends ConsoleKernel
*/
protected function commands()
{
$this->load(__DIR__.'/Commands');
$this->load(__DIR__ . '/Commands');
require base_path('routes/console.php');
}

49
app/Enums/CrudAction.php Normal file
View file

@ -0,0 +1,49 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
*
* https://laravel.com/docs/8.x/authorization
* Controller Method Policy Method
* index viewAny
* show view
* create create
* store create
* edit update
* update update
* destroy delete
*
* @method static self viewAny()
* @method static self view()
* @method static self create()
* @method static self update()
* @method static self delete()
*/
class CrudAction extends BaseEnum
{
protected static function values(): array
{
return [
'viewAny' => 0,
'view' => 1,
'create' => 2,
'update' => 3,
'delete' => 4,
];
}
protected static function labels(): array
{
return [
'viewAny' => trans('Liste'),
'view' => trans('Voir'),
'create' => trans('Créer'),
'update' => trans('Éditer'),
'delete' => trans('Supprimer'),
];
}
}

53
app/Enums/CrudType.php Normal file
View file

@ -0,0 +1,53 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
* @method static self boolean()
* @method static self int()
* @method static self string()
* @method static self text()
* @method static self email()
* @method static self password()
* @method static self float()
* @method static self enum()
* @method static self belongsTo()
* @method static self belongsToMany()
*/
class CrudType extends BaseEnum
{
protected static function values(): array
{
return [
'boolean' => 0,
'int' => 1,
'string' => 2,
'text' => 3,
'email' => 4,
'password' => 5,
'float' => 6,
'enum' => 7,
'belongsTo' => 8,
'belongsToMany' => 9
];
}
protected static function labels(): array
{
return [
'boolean' => trans('Booléen'),
'int' => trans('Nombre'),
'string' => trans('Chaîne de charactères simple'),
'text' => trans('Texte'),
'email' => trans('Chaîne de charactères email'),
'password' => trans('Mot de passe'),
'float' => trans('Nombre à virgule flotante'),
'enum' => trans('Énumération'),
'belongsTo' => trans('Clé étrangère'),
'belongsToMany' => trans('Relation Belongs To Many')
];
}
}

29
app/Enums/Role.php Normal file
View file

@ -0,0 +1,29 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
* @method static self admin()
* @method static self rh()
*/
class Role extends BaseEnum
{
protected static function values(): array
{
return [
'admin' => 0,
'rh' => 1
];
}
protected static function labels(): array
{
return [
'admin' => trans('Administrateur'),
'rh' => trans('Ressource humaines')
];
}
}

29
app/Enums/TypeSondage.php Normal file
View file

@ -0,0 +1,29 @@
<?php
namespace App\Enums;
use Kwaadpepper\Enum\BaseEnum;
/**
* @method static self radio()
* @method static self checkbox()
*/
class TypeSondage extends BaseEnum
{
protected static function values(): array
{
return [
'radio' => 0,
'checkbox' => 1
];
}
protected static function labels(): array
{
return [
'radio' => trans('Choix unique'),
'checkbox' => trans('Choix multiples')
];
}
}

View file

@ -0,0 +1,9 @@
<?php
namespace App\Exceptions;
use Exception;
class CrudException extends Exception
{
}

View file

@ -3,10 +3,13 @@
namespace App\Exceptions;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Support\Facades\Mail;
use RuntimeException;
use Throwable;
class Handler extends ExceptionHandler
{
/**
* A list of the exception types that are not reported.
*
@ -35,7 +38,27 @@ class Handler extends ExceptionHandler
public function register()
{
$this->reportable(function (Throwable $e) {
//
try {
if (!config('app.debug')) {
if ($this->shouldntReport($e)) {
return false;
}
Mail::send([
'html' => 'emails.exception',
'text' => 'emails.exception-plain'
], [
'exception' => $e,
'appName' => config('app.name')
], function ($m) {
$m->to(
config('mail.contactsList.serviceweb'),
sprintf('%s Exception Handler', config('app.name'))
)->subject('Need help !, an error occured in your application, please check Logs ASAP');
});
}
} finally {
return true;
}
});
}
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}

View file

@ -0,0 +1,39 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:web')->except('logout');
}
}

View file

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\Models\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest:web');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\Models\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}

View file

@ -2,12 +2,26 @@
namespace App\Http\Controllers;
use App\Models\Employee;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Auth;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
use AuthorizesRequests;
use DispatchesJobs;
use ValidatesRequests;
/**
* Get authenticated Employee
*
* @return Employee
*/
protected function getEmployee(): Employee
{
return Auth::guard('employee')->user();
}
}

View file

@ -0,0 +1,199 @@
<?php
namespace App\Http\Controllers;
use App\Exceptions\CrudException;
use App\Http\Requests\StoreCrudModel;
use App\Http\Requests\UpdateCrudModel;
use App\Http\Resources\CrudResource;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Str;
class CrudController extends Controller
{
/** @var \Illuminate\Database\Eloquent\Model */
protected static $modelClass = null;
// Eloquent callbacks
public function indexQuery(Builder $query): Builder
{
return $query;
}
public function storeModel(Model &$model)
{
}
public function updateModel(Model &$model)
{
}
public function __construct()
{
// AUTH CONTROL TO ROOT POLICY
$this->authorizeResource(static::$modelClass);
$modelClass = static::$modelClass;
if (is_null($modelClass) or !class_exists($modelClass)) {
throw new CrudException(sprintf(
'%s needs the modelClass prop to be a class, %s %s given',
__CLASS__,
gettype($modelClass),
$modelClass
));
}
if (!is_subclass_of(new $modelClass(), Model::class)) {
throw new CrudException(sprintf(
'%s needs the model prop to be a class the extends %s',
__CLASS__,
Model::class
));
}
if (!request()->ajax() and !app()->runningInConsole()) {
self::shareCrudModelsClassesToView();
$this->shareModelClassToView();
$this->shareModelTableToView();
$this->shareModelPropsToView();
}
}
public function index(Request $request)
{
/** @var \Illuminate\Database\Eloquent\Builder */
$models = static::$modelClass::query();
if ($request->s) {
$model = static::$modelClass;
$col = (new $model())->crudLabelColumn;
$models = $models->where($col, 'LIKE', "%$request->s%");
}
$models = $this->indexQuery($models);
/** @var \Illuminate\Pagination\AbstractPaginator */
$models = $models->paginate(config('crud.paginate', 15));
if ($request->ajax()) {
return response()->json(CrudResource::collection($models));
} else {
$models = $models->withQueryString();
return view('crud.index', compact('models'));
}
}
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function show(Model $model)
{
return view('crud.show', compact('model'));
}
public function create()
{
return view('crud.create');
}
public function store(StoreCrudModel $request)
{
$modelClass = static::$modelClass;
$model = new $modelClass();
$model->fill($request->validated());
$this->storeModel($model);
$model->saveOrFail();
$model->saveRelations($request);
return \redirect()->route(sprintf('bo.%s.edit', $model->getTable()), $model)
->with('success', trans(':model à bien été enregisté.', ['model' => $model->getModelName()]));
}
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function edit(Model $model)
{
return view('crud.edit', compact('model'));
}
public function update(UpdateCrudModel $request, Model $model)
{
$model->fill($request->validated());
$this->updateModel($model);
$model->saveOrFail();
$model->saveRelations($request);
return \redirect()->route(sprintf('bo.%s.edit', $model->getTable()), $model)
->with('success', trans(':model à bien été enregisté.', ['model' => $model->getModelName()]));
}
public function destroy(Model $model)
{
if ($restrict = $model->gotHasManyRelationWithRestrictOnDelete()) {
return redirect()->back()
->with('error', trans(
':model n\'as pas pu être supprimé car il est encore associé avec des :associated',
[
'model' => $model->getModelName(),
'associated' => Str::plural($restrict)
]
));
}
if ($model->delete()) {
return \redirect()->route(sprintf('bo.%s.index', $model->getTable()))
->with('warning', trans(':model à bien été supprimé.', ['model' => $model->getModelName()]));
} else {
return redirect()->back()
->with('error', trans(':model n\'as pas pu être supprimé', ['model' => $model->getModelName()]));
}
}
public function getModelClass()
{
return static::$modelClass;
}
/**
* Get the current model class
*
* @return void
*/
public static function shareCrudModelsClassesToView(): void
{
$models = [];
foreach (File::allFiles(\app_path('Http/Controllers')) as $file) {
$class = sprintf('App\Http\Controllers\%s', \substr($file->getFilename(), 0, -4));
if (is_subclass_of($class, CrudController::class) and $class::$modelClass) {
$models[] = $class::$modelClass;
}
}
View::share('modelsClasses', $models);
}
/**
* Get the current model class
*
* @return void
*/
private function shareModelClassToView(): void
{
View::share('modelClass', static::$modelClass);
}
/**
* Get the current model table
*
* @return void
*/
private function shareModelTableToView(): void
{
/** @var \Illuminate\Database\Eloquent\Model $model */
$model = static::$modelClass;
View::share('modelTable', (new $model())->getTable());
}
/**
* Get the current model properties
*
* @return void
*/
private function shareModelPropsToView(): void
{
$model = static::$modelClass;
View::share('props', (new $model())->getEditableProperties());
}
}

View file

@ -0,0 +1,384 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\EmployeeAddressUpdateRequest;
use App\Http\Requests\EmployeeBankUpdateRequest;
use App\Http\Requests\EmployeeFamilyUpdateRequest;
use App\Http\Requests\EmployeeForgottenPasswordRequest;
use App\Http\Requests\EmployeeFormationQuestionRequest;
use App\Http\Requests\EmployeeLoginRequest;
use App\Http\Requests\EmployeeMailChangeRequest;
use App\Http\Requests\EmployeePasswordChangeRequest;
use App\Http\Requests\EmployeeRdvQuestionRequest;
use App\Http\Requests\EmployeeResetPasswordRequest;
use App\Http\Requests\ImportEmployeesCsvFile;
use App\Imports\EmployeeImport;
use App\Imports\EmployeeSortie;
use App\Jobs\SendMailConfirmToEmployee;
use App\Jobs\SendMailResetPasswordToEmployee;
use App\Jobs\SendEmployeeInfoMailToSiteRH;
use App\Jobs\SendEmployeeLockedDownMailToEmployee;
use App\Jobs\SendEmployeeLockedDownMailToSiteRH;
use App\Mail\EmployeeInfoAddress;
use App\Mail\EmployeeInfoBank;
use App\Mail\EmployeeInfoFamily;
use App\Mail\EmployeeLockedDown;
use App\Mail\EmployeeQuestionFormation;
use App\Mail\EmployeeQuestionRdv;
use App\Mail\EmployeeYourAccountIsLockedDown;
use App\Models\Employee;
use App\Models\EmployeeMailToken;
use App\Models\Site;
use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Excel as LExcel;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\Validators\ValidationException;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
class EmployeesController extends CrudController
{
/** @var Employee */
protected static $modelClass = Employee::class;
// BO
// Eloquent callbacks
public function indexQuery(Builder $query): Builder
{
/** @var \App\Models\User */
$user = auth()->user();
return $query->whereIn('site_id', $user->sites()->pluck('id'));
}
public function storeModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
public function updateModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
public function importExit()
{
$this->authorize('importExit', Employee::class);
CrudController::shareCrudModelsClassesToView();
return view('back.employees.import-exit');
}
public function importExitSubmit(ImportEmployeesCsvFile $request)
{
$this->authorize('import', Employee::class);
/** @var \App\Models\User */
$user = auth()->user();
/** @var \App\Models\Site */
$site = Site::where('id', $user->selected_site_id)->firstOrFail();
$employeeImport = null;
try {
$employeeImport = new EmployeeSortie($site);
Excel::import(
$employeeImport,
$request->file,
null,
LExcel::CSV
);
} catch (ReaderException $e) {
// this time cannot read anything
return redirect()->back()->withErrors('Echec lors de l\'import le fichier doit être conforme à celui fourni lors du developement de l\'application');
} catch (Exception $e) {
// $employeeImport->errorMessages
// this time cannot read anything
return redirect()->back()->withErrors(sprintf(
'Echec lors de l\'import %s',
$e->getMessage()
));
} finally {
$message = sprintf(
"%d salariés on été supprimés",
$employeeImport->removedCount,
);
if (count($employeeImport->importedFailed)) {
$message .= sprintf(
"\n %d echecs\n Les lignes suivantes ont été ignorés \n%s\n",
count($employeeImport->importedFailed),
\collect($employeeImport->importedFailed)->map(function ($d) {
return $d->implode(',');
})->implode("\n"),
);
}
if (count($employeeImport->errorMessages)) {
$message .= sprintf(
"\n %d erreurs\n Les erreurs suivantes sont apparues %s",
count($employeeImport->errorMessages),
implode("\n", $employeeImport->errorMessages),
);
}
return redirect()->route('bo.employees.importExit')
->with('success', $message);
}
}
public function import()
{
$this->authorize('import', Employee::class);
CrudController::shareCrudModelsClassesToView();
return view('back.employees.import');
}
public function importSubmit(ImportEmployeesCsvFile $request)
{
$this->authorize('import', Employee::class);
/** @var \App\Models\User */
$user = auth()->user();
/** @var \App\Models\Site */
$site = Site::where('id', $user->selected_site_id)->firstOrFail();
$employeeImport = null;
try {
$employeeImport = new EmployeeImport($site);
Excel::import(
$employeeImport,
$request->file,
null,
LExcel::CSV
);
} catch (Exception $e) {
// this time cannot read anything
return redirect()->back()->withErrors(sprintf(
'Echec lors de l\'import %s',
$e->getMessage()
));
} catch (ReaderException $e) {
// this time cannot read anything
return redirect()->back()->withErrors('Echec lors de l\'import le fichier doit être conforme à celui fourni lors du developement de l\'application');
} finally {
$message = '';
$errors = [];
foreach ($employeeImport->errorMessages as $k => $err) {
$errors[] = sprintf(
"\nLigne %d :\n%s\nDonnée:\n%s\n",
$k + 1,
\implode("\n", $err),
\implode(", ", $employeeImport->importedFailed[$k]->all())
);
}
if (count($errors)) {
$message = sprintf(
"%d salariés on été importés \n %d salariés mis à jour\n %d erreurs\n Les erreurs suivantes sont apparues %s",
$employeeImport->importedCount,
$employeeImport->updatedCount,
count($errors),
implode("\n", $errors),
);
} else {
$message = sprintf(
"%d salariés on été importés",
$employeeImport->importedCount,
);
}
return redirect()->route('bo.employees.import')
->with('success', $message);
}
}
// FO
public function logout()
{
/** @var \Illuminate\Auth\SessionGuard */
$guard = Auth::guard('employee');
$guard->logout();
return redirect()->route('employees.login');
}
public function loginSubmit(EmployeeLoginRequest $request)
{
/** @var \Illuminate\Auth\SessionGuard */
$guard = Auth::guard('employee');
if (!($employee = Employee::where('matricule', $request->login)->first())) {
return \redirect()->route('employees.login')
->withErrors(['Les identifiants fournis sont incorrects'], 'login');
}
// Is employee locked down ?
if ($employee->password_attempts >= 3) {
return \redirect()->route('employees.login')
->withErrors([trans(sprintf(
'Votre compte est verouillé, il vous faut contacter votre responsable RH pour le deverouiller',
3 - $employee->password_attempts
))], 'lockedDown')->with('site', $employee->site);
}
// phpcs:ignore Generic.Files.LineLength.TooLong
if (!$guard->attempt(['matricule' => $request->login, 'password' => $request->password], $request->rememberMe)) {
$employee->password_attempts = $employee->password_attempts + 1;
$employee->saveOrFail();
// Is employee locked down ?
if ($employee->password_attempts >= 3) {
SendEmployeeLockedDownMailToEmployee::dispatchAfterResponse(new EmployeeLockedDown($employee), $employee);
SendEmployeeLockedDownMailToSiteRH::dispatchAfterResponse(new EmployeeYourAccountIsLockedDown($employee), $employee);
return \redirect()->route('employees.login')
->withErrors(
[trans('Votre compte est verouillé, il vous faut contacter votre responsable RH pour le deverouiller')],
'lockedDown'
)->with('site', $employee->site);
}
return \redirect()->route('employees.login')
->withErrors([trans(sprintf(
// phpcs:ignore Generic.Files.LineLength.TooLong
'Attention plus que %d essais, après cela votre compte sera verrouillé, un mail à été envoyé pour prévenir votre responsable RH (seul lui peux déverouiller votre compte)',
3 - $employee->password_attempts
))], 'login');
}
$request->session()->regenerate();
return redirect()->intended(route('fo.flash.index'));
}
public function passwordForgottenSubmit(EmployeeForgottenPasswordRequest $request)
{
$employee = Employee::where('matricule', $request->matricule)->firstOrFail();
$token = $employee->token ?? new EmployeeMailToken();
$token->fill([
'email' => $employee->email,
'employee_id' => $employee->id
]);
$token->saveOrFail();
$token->employee()->associate($token);
SendMailResetPasswordToEmployee::dispatchAfterResponse($employee);
return \redirect()->back()->with('successMail', true);
}
public function resetPassword(EmployeeMailToken $token = null)
{
if ($token) {
Session::put('resetMail', time());
Session::put('resetMailEmployee', $token->employee);
Session::save();
$token->delete();
}
// If token has been consumed and we has a resetMail in Session then pass trough
if (Session::has('resetMail') and (Session::get('resetMail') + 60 * 4) > time()) {
return view('front.employees.changePassword', ['reset' => true]);
}
Session::forget('resetMail');
Session::forget('resetMailEmployee');
Session::save();
abort(redirect()->route('fo.flash.index'));
}
public function resetPasswordSubmit(EmployeeResetPasswordRequest $request)
{
/** @var Employee */
if ($employee = Session::get('resetMailEmployee', null)) {
$employee->password = $request->newpassword;
$employee->saveOrFail();
Session::forget('resetMail');
Session::forget('resetMailEmployee');
Session::save();
return redirect()->route('employees.done-reset-password');
}
abort(redirect()->route('fo.flash.index'));
}
public function passwordChangeSubmit(EmployeePasswordChangeRequest $request)
{
$employee = $this->getEmployee();
$employee->password = $request->newpassword;
$employee->saveOrFail();
if (Session::has('required')) {
Session::remove('required');
Session::save();
return redirect()->route('fo.flash.index')
->with('message', trans('Votre mot de passe à bien été mis à jour'));
}
return \redirect()->back()->with('success', true);
}
public function mailUpdate(EmployeeMailChangeRequest $request)
{
$employee = $this->getEmployee();
$token = $employee->token ?? new EmployeeMailToken();
$token->fill([
'email' => $request->email,
'employee_id' => $employee->id
]);
$token->saveOrFail();
$token->employee()->associate($token);
SendMailConfirmToEmployee::dispatchAfterResponse($employee);
return \redirect()->back()->with('successMail', true)
->with('message', trans('Informations mises à jour'));
}
public function confirmMail(EmployeeMailToken $token)
{
$employee = $token->employee;
$employee->email = $token->email;
$employee->saveOrFail();
$token->delete();
return view('front.emailActivated');
}
public function familyUpdate(EmployeeFamilyUpdateRequest $request)
{
$this->sendInfo(EmployeeInfoFamily::class, $request);
return \redirect()->back()->with('success', true)
->with('message', trans('Informations transmises'));
}
public function addressUpdate(EmployeeAddressUpdateRequest $request)
{
$this->sendInfo(EmployeeInfoAddress::class, $request);
return \redirect()->back()->with('success', true)
->with('message', trans('Informations transmises'));
}
public function bankUpdate(EmployeeBankUpdateRequest $request)
{
$this->sendInfo(EmployeeInfoBank::class, $request);
return \redirect()->back()->with('success', true)
->with('message', trans('Informations transmises'));
}
public function questionFormation(EmployeeFormationQuestionRequest $request)
{
$this->sendInfo(EmployeeQuestionFormation::class, $request);
return \redirect()->back()->with('success', true)
->with('message', trans('Informations transmises'));
}
public function questionRdv(EmployeeRdvQuestionRequest $request)
{
$this->sendInfo(EmployeeQuestionRdv::class, $request);
return \redirect()->back()->with('success', true)
->with('message', trans('Informations transmises'));
}
private function sendInfo(string $mailClass, FormRequest $request)
{
SendEmployeeInfoMailToSiteRH::dispatchAfterResponse(
new $mailClass(
array_merge(
['employee' => auth('employee')->user()],
$request->validated()
)
),
auth('employee')->user()
);
}
}

View file

@ -0,0 +1,57 @@
<?php
namespace App\Http\Controllers;
use App\Models\Flash;
use App\Models\Site;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class FlashesController extends CrudController
{
/** @var Flash */
protected static $modelClass = Flash::class;
// BO
// Eloquent callbacks
public function indexQuery(Builder $query): Builder
{
/** @var \App\Models\User */
$user = auth()->user();
$flashesID = \collect([]);
$user->sites()->with('flashes')->get()->map(function (Site $site) use (&$flashesID) {
$flashesID = $flashesID->merge($site->flashes->pluck('id')->all());
});
return $query->whereIn('id', $flashesID->unique());
}
public function storeModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
public function updateModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
// FO
public function inform()
{
$flashes = $this->getEmployee()->getFlashes();
return view('front.flashes.index', compact('flashes'));
}
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function informShow(Flash $flash)
{
return view('front.flashes.show', compact('flash'));
}
}

View file

@ -0,0 +1,14 @@
<?php
namespace App\Http\Controllers;
class HomeController extends Controller
{
// BO
public function home()
{
CrudController::shareCrudModelsClassesToView();
return view('back.home');
}
}

View file

@ -0,0 +1,148 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ImportPlanningXlsFile;
use App\Imports\PlanningImport;
use App\Models\Planning;
use App\Models\Site;
use Illuminate\Http\UploadedFile;
use Maatwebsite\Excel\Facades\Excel;
use Maatwebsite\Excel\Excel as LExcel;
use Maatwebsite\Excel\HeadingRowImport;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
class PlanningController extends Controller
{
// BO
public function __construct()
{
// AUTH CONTROL TO ROOT POLICY
$this->authorizeResource(Planning::class);
}
public function import()
{
$this->authorize('import', Planning::class);
CrudController::shareCrudModelsClassesToView();
return view('back.plannings.import');
}
public function importSubmit(ImportPlanningXlsFile $request)
{
$this->authorize('import', Planning::class);
/** @var \App\Models\User */
$user = auth()->user();
/** @var \App\Models\Site */
$site = Site::where('id', $user->selected_site_id)->firstOrFail();
$planningImport = null;
try {
try {
$planningImport = new PlanningImport($site, (new HeadingRowImport())->toArray(
$request->file,
null,
LExcel::XLS
));
Excel::import(
$planningImport,
$request->file,
null,
LExcel::XLS
);
} catch (ReaderException $e) {
// Assert This is XML
$xmlString = $this->filterXml($request->file);
// create ne temp file
$stream = tmpfile();
fwrite($stream, $xmlString);
$file = new UploadedFile(
stream_get_meta_data($stream)['uri'],
'xml data',
'application/vnd.ms-excel'
);
$planningImport = new PlanningImport($site, (new HeadingRowImport())->toArray(
$file,
null,
LExcel::XML
));
Excel::import(
$planningImport,
$file,
null,
LExcel::XML
);
\fclose($stream);
}
} catch (ReaderException $e) {
// this time cannot read anything
return redirect()->back()->withErrors('Echec lors de l\'import le fichier doit être conforme à celui fourni lors du developement de l\'application');
} finally {
return redirect()->route('bo.plannings.import')
->with('success', sprintf(
"%d plannings on été importés \n Les plannings suivant n'ont pu être importés \n %s",
$planningImport->importedCount,
collect($planningImport->importedFailed)->map(function ($d) {
return $d->implode(',');
})->implode("\n")
));
}
}
/**
* Le parser Xml ne supporte pas la lecture de cellule
* dont le contenu est encapsulé avec du style... (toujours plus)
* Donc on va essayer de virer ceux-ci
*
* NOTE: Conforme au fichier transmis par Ossan et gitté dans resources/documents à cette date
* eg :
* <Cell ss:MergeDown="2" ss:StyleID="m2129200830208"><ss:Data ss:Type="String"
* xmlns="http://www.w3.org/TR/REC-html40"><B><Font html:Color="#000000">CHUPE JEAN CHARLES </Font><I><Font
* html:Face="Parisine Plus Ptf Sombre" x:Family="Swiss" html:Size="11"
* html:Color="#FFFFFF">SSsT</Font></I></B></ss:Data></Cell>
*
* @param UploadedFile $file
* @return string
*/
private function filterXml(UploadedFile $file): string
{
$xmlString = $file->get();
$xmlString = str_replace(
[
'<B>',
'</B>',
'<I>',
'</I>',
'<Font>',
'<Font ss:FontName="Arial" x:Family="Swiss" ss:Color="#000000" ss:Size="8" ss:Bold="1"/>',
'<Font ss:FontName="Arial" x:Family="Swiss" ss:Size="8" ss:Color="#000000" ss:Bold="1"/>',
'<Font ss:FontName="Arial" x:Family="Swiss" ss:Size="8" ss:Color="#FF0000" ss:Bold="1"/>',
'<Font html:Face=\'Parisine Plus Ptf Sombre\' x:Family=\'Swiss\' html:Size=\'11\' html:Color=\'#FFFFFF\'>',
"<Font ss:FontName=\"Arial\" x:Family=\"Swiss\" ss:Size=\"8\" ss:Color=\"#000000\"\n ss:Bold=\"1\"/>",
"<Font ss:FontName=\"Arial\" x:Family=\"Swiss\" ss:Size=\"8\" ss:Color=\"#000000\"\n",
"<Font ss:FontName=\"Arial\" x:Family=\"Swiss\" ss:Size=\"8\" ss:Color=\"#fdfefe\"\n",
"<Font ss:FontName=\"Arial\" x:Family=\"Swiss\" ss:Size=\"8\" ss:Color=\"#FFFFFF\"\n",
" ss:Bold=\"1\"/>\n",
'<Font html:Color=\'#000000\'>',
'</Font>'
],
'',
$xmlString
);
// dd($xmlString);
return $xmlString;
}
// FO
public function showPlannings()
{
/** @var \App\Models\Employee */
$employee = auth()->user();
$plannings = $employee->getWeekAndNextPlannings();
return view('front.employees.plannings', compact('plannings'));
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use App\Models\Pole;
class PolesController extends CrudController
{
/** @var Pole */
protected static $modelClass = Pole::class;
}

View file

@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers;
use App\Models\Site;
use Illuminate\Database\Eloquent\Builder;
class SitesController extends CrudController
{
/** @var Site */
protected static $modelClass = Site::class;
// BO
// Eloquent callbacks
public function indexQuery(Builder $query): Builder
{
/** @var \App\Models\User */
$user = auth()->user();
return $query->whereIn('id', $user->sites()->pluck('id'));
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use App\Models\Societe;
class SocietesController extends CrudController
{
/** @var Employee */
protected static $modelClass = Societe::class;
}

View file

@ -0,0 +1,156 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\SondageResponsesRequest;
use App\Http\Requests\UpdateCrudModel;
use App\Http\Requests\UpdateSondage;
use App\Models\ReponseSondage;
use App\Models\Sondage;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use League\Csv\Writer;
class SondagesController extends CrudController
{
/** @var Sondage */
protected static $modelClass = Sondage::class;
// Eloquent callbacks
public function indexQuery(Builder $query): Builder
{
/** @var \App\Models\User */
$user = auth()->user();
return $query->whereIn('site_id', $user->sites()->pluck('id'));
}
public function storeModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
public function updateModel(Model &$model)
{
/** @var \App\Models\User */
$user = auth()->user();
$model->site_id = $user->selected_site_id;
}
// BO
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function show(Model $model)
{
return view('crudOverLoad.sondages.show', compact('model'));
}
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function edit(Model $model)
{
return view('crudOverLoad.sondages.edit', compact('model'));
}
public function updateSondage(UpdateSondage $request, Sondage $model)
{
$data = $request->validated();
$model->fill($data);
$model->saveOrFail();
$model->reponseSondages()->delete();
foreach ($data['reponses'] as $reponse) {
$reponse = new ReponseSondage([
'value' => $reponse
]);
$reponse->sondage()->associate($model);
$reponse->saveorFail();
}
return \redirect()->route(sprintf('bo.%s.edit', $model->getTable()), $model)
->with('success', trans(':model à bien été enregisté.', ['model' => $model->getModelName()]));
}
public function export(Sondage $model)
{
$this->authorize('export', $model);
$anwsers = $model->reponseSondages->load('employees');
$header = '"employee_matricule";' . implode(';', $anwsers->map(function ($answer) {
return "\"$answer->value\"";
})->all());
$w = Writer::createFromString($header . PHP_EOL);
$w->setDelimiter(';');
$w->setEnclosure('"');
$w->setOutputBOM(Writer::BOM_UTF8);
$tmpl = [
0 => '0',
];
foreach ($anwsers as $k => $answer) {
$tmpl[$k + 1] = '0';
}
$res = [];
foreach ($anwsers as $k => $answer) {
$answer->employees->map(function ($employee) use ($answer, $k, &$res) {
if (!array_key_exists($employee->id, $res)) {
$res[$employee->id] = [
'employee' => $employee,
'answers' => []
];
}
$res[$employee->id]['answers'][$k + 2] = 1;
});
}
foreach ($res as $customerIntel) {
$w->insertOne(array_replace(
$tmpl,
[
$customerIntel['employee']->id,
],
$customerIntel['answers']
));
}
return response((string)$w, 200, [
'Content-Encoding' => 'none',
'Content-Type' => 'text/csv; charset=UTF-8',
'Content-Disposition' => sprintf(
'attachment; filename="Résultats Sondage %s - %s.csv',
Str::slug($model->question),
Str::slug(date('d_m_Y-H_i_s'))
),
'Content-Description' => 'File Transfer',
]);
}
// FO
public function sondages()
{
$sondages = $this->getEmployee()->unAnsweredSondages();
return view('front.sondages.index', compact('sondages'));
}
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClass
public function sondagesShow(Sondage $sondage)
{
$employee = $this->getEmployee();
if ($employee->hasAnweredToSondage($sondage)) {
return view('front.sondages.saved', ['answered' => true]);
}
return view('front.sondages.show', compact('sondage'));
}
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed
public function sondagesSubmit(SondageResponsesRequest $request, Sondage $sondage)
{
$employee = $this->getEmployee();
if ($employee->hasAnweredToSondage($sondage)) {
return view('front.sondages.saved', ['answered' => true]);
}
$employee->reponseSondages()->syncWithoutDetaching($request->reponses);
return view('front.sondages.saved');
}
}

View file

@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use App\Models\User;
class UsersController extends CrudController
{
/** @var User */
protected static $modelClass = User::class;
}

View file

@ -6,6 +6,7 @@ use Illuminate\Foundation\Http\Kernel as HttpKernel;
class Kernel extends HttpKernel
{
/**
* The application's global HTTP middleware stack.
*
@ -20,6 +21,7 @@ class Kernel extends HttpKernel
\App\Http\Middleware\PreventRequestsDuringMaintenance::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\App\Http\Middleware\XssSanitizer::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
];
@ -33,7 +35,6 @@ class Kernel extends HttpKernel
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
@ -62,5 +63,7 @@ class Kernel extends HttpKernel
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'userSite' => \App\Http\Middleware\UserSite::class,
'employeeOutdatedPassword' => \App\Http\Middleware\EmployeeOutDatedPassword::class,
];
}

View file

@ -14,8 +14,20 @@ class Authenticate extends Middleware
*/
protected function redirectTo($request)
{
$logins = [
'web' => route('bo.login'),
'employee' => route('employees.login')
];
if (! $request->expectsJson()) {
return route('login');
foreach ($request->route()->gatherMiddleware() as $middleware) {
if (\is_string($middleware) and strpos($middleware, 'auth:') !== false) {
$route = $logins[str_replace('auth:', '', $middleware)] ?? null;
if ($route) {
return $route;
}
}
}
return route('employees.login');
}
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Session;
class EmployeeOutDatedPassword
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var \App\Models\Employee */
$employee = auth('employee')->user();
if (
$employee->password_date->addMonths(3)->lt(Carbon::now()) and
!in_array($request->route()->getName(), [
'fo.employees.passwordChange', 'fo.employees.passwordChangeSubmit',
'fo.employees.logout'
])
) {
Session::put('required', true);
Session::save();
return redirect()->route('fo.employees.passwordChange');
}
return $next($request);
}
}

View file

@ -6,6 +6,7 @@ use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*

View file

@ -6,6 +6,7 @@ use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Mi
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*

View file

@ -19,11 +19,17 @@ class RedirectIfAuthenticated
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
$homes = [
'web' => route('bo.home'),
'employee' => route('fo.flash.index')
];
$guards = empty($guards) ? [] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
// Pas de safe route si il manque un gard..
return redirect($homes[$guard]);
}
}

View file

@ -6,6 +6,7 @@ use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
@ -14,6 +15,8 @@ class TrimStrings extends Middleware
protected $except = [
'current_password',
'password',
'newpassword',
'newpasswordConfirm',
'password_confirmation',
];
}

View file

@ -7,6 +7,7 @@ use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*

View file

@ -0,0 +1,47 @@
<?php
namespace App\Http\Middleware;
use App\Models\Site;
use Closure;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Support\Facades\Session;
class UserSite
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
/** @var \App\Models\User */
$user = auth()->user();
try {
if (!$user->selected_site_id) {
$site = $user->sites()->firstOrFail();
$user->selected_site_id = $site->id;
$user->saveOrFail();
}
if ($request->query('userSite')) {
$site = $user->sites()
->where('site_id', $request->query('userSite'))
->firstOrFail();
$user->selected_site_id = $site->id;
$user->saveOrFail();
} elseif (!Site::where('id', $user->selected_site_id)->exists()) {
$site = $user->sites()
->where('site_id', $request->query('userSite'))
->firstOrFail();
$user->selected_site_id = $site->id;
$user->saveOrFail();
}
} catch (ModelNotFoundException $e) {
abort(400, 'Vous n\'avez pas de site administrable');
}
return $next($request);
}
}

View file

@ -6,6 +6,7 @@ use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Middleware;
use Closure;
class XssSanitizer
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next, ...$params)
{
$inputs = $request->all();
array_walk_recursive($inputs, function (&$input, $name) use ($params) {
if (in_array($name, $params)) {
$input = clean($input);
} else {
$input = strip_tags($input);
}
});
$request->merge($inputs);
return $next($request);
}
}

View file

@ -0,0 +1,79 @@
<?php
namespace App\Http\Requests;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Exceptions\CrudException;
use App\Http\Controllers\CrudController;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Http\FormRequest;
use App\Traits\IsCrudModel;
class CrudRequest extends FormRequest
{
/** @var array */
protected $rules = [];
/**
* Get rules for a specific action
*
* @param CrudAction $action
* @return array
*/
public function getRulesForAction(CrudAction $action): array
{
$props = $this->getCurrentModel()::getEditableProperties();
$propsRules = [];
foreach ($this->getCurrentModel()::getRules() as $k => $rules) {
$propsRules[$k] = [];
$propIsAllowedOnUpdateAction = in_array($action, $props[$k]['actions']);
foreach ($rules as $rule) {
// dont copy required if prop is not allowed on update
if (!$propIsAllowedOnUpdateAction and is_string($rule) and $rule == 'required') {
continue;
}
if (!in_array($rule, $propsRules[$k])) {
$propsRules[$k][] = $rule;
}
// handle password
if (
$props[$k]['type']->equals(CrudType::password()) and
!$this->{$k} or (is_string($this->{$k}) and !\strlen($this->{$k}))
) {
$this->offsetUnset($k);
}
// handle boolean
if ($props[$k]['type']->equals(CrudType::boolean())) {
$this->merge([$k => $this->{$k} ? 1 : 0]);
}
// handle belongsToMany
if ($props[$k]['type']->equals(CrudType::belongsToMany())) {
$propsRules["$k.*"][0] = 'exists:' .
(new $props[$k]['belongsToMany']())->getTable() . ',id';
}
}
}
return $propsRules;
}
/**
* Returns the first parameter that is a crud model
*
* @return string \Illuminate\Database\Eloquent\Model class name
* @throws \App\Exceptions\CrudException if the request does not have a crud model parameter
*/
protected function getCurrentModel(): string
{
if (
!is_object($this->route()->controller) or
!$this->route()->controller instanceof CrudController
) {
throw new CrudException(sprintf(
'Request `%s` needs to be bound on route that use a CrudController',
get_class($this)
));
}
return $this->route()->controller->getModelClass();
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeAddressUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'address' => 'required|string|min:5',
'city' => 'required|string|min:2',
'postcode' => 'required|digits_between:5,7',
'phone' => 'required|numeric|digits_between:10,12',
'mobile' => 'required|numeric|digits_between:10,12',
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeBankUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:2048'
];
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeFamilyUpdateRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'message' => 'required|string|min:20',
'file' => 'required|file|mimes:jpeg,png,jpg,pdf|max:2048'
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeForgottenPasswordRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'matricule' => 'required|string|exists:employees,matricule'
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeFormationQuestionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'message' => 'required|string|min:20'
];
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeLoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$this->merge([
'rememberMe' => $this->rememberMe ? true : false
]);
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'login' => 'required|string',
'password' => 'required|string',
'rememberMe' => 'required|boolean'
];
}
}

View file

@ -0,0 +1,31 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeMailChangeRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email:rfc,dns|unique:employees,email,' . auth('employee')->user()->id,
'emailConfirm' => 'required|same:email'
];
}
}

View file

@ -0,0 +1,69 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeePasswordChangeRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
/** @var \App\Models\Employee */
$employee = auth('employee')->user();
return [
'password' => [
'required',
'string',
//phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) use ($employee) {
if (
!auth('employee')->validate([
'matricule' => $employee->matricule,
'password' => $value
])
) {
$fail(trans('Votre ancien mot de passe est invalide'));
}
}
],
'newpassword' => [
'required',
'string',
'regex:/^(?=.*[A-Z])(?=.*[²&~"#\'\{\(\[\-|`_\\^@\)\°=+\}¨^$£¤%?,;.:\/!§*<>])(?=.*[0-9].*)(?=.*[a-z]).{8,}$/'
],
'newpasswordConfirm' => 'required|same:newpassword'
];
}
/**
* Get the error messages for the defined validation rules.
*
* @return array
*/
public function messages()
{
return [
'newpassword.regex' => trans("
Votre mot de passe doit être d'au moins 8 charactères,
il doit comporter au moins un caractère en majuscule,
un caractère en minuscule,
un nombre et
un caractère spécial parmis les suivants : ²&~\"#'{([-|`_\^@)°=+}¨^\$£¤%?,;.:\/!§*<>"),
];
}
}

View file

@ -0,0 +1,30 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class EmployeeRdvQuestionRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'message' => 'required|string|min:20'
];
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Session;
class EmployeeResetPasswordRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return Session::has('resetMail');
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'newpassword' => [
'required',
'string',
'regex:/^(?=.*[A-Z])(?=.*[²&~"#\'\{\(\[\-|`_\\^@\)\°=+\}¨^$£¤%?,;.:\/!§*<>])(?=.*[0-9].*)(?=.*[a-z]).{8,}$/'
],
'newpasswordConfirm' => 'required|same:newpassword'
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ImportEmployeesCsvFile extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
// max 50 Mo
'file' => [
'required',
// phpcs:ignore Generic.Files.LineLength.TooLong
'mimetypes:text/csv,text/plain,application/csv,text/comma-separated-values,text/anytext,application/octet-stream,application/txt',
'max:51200',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->file->getClientMimeType() !== 'application/vnd.ms-excel') {
$fail(__('Le fichier doit être d\'un mime type application/vnd.ms-excel'));
}
}
]
];
}
}

View file

@ -0,0 +1,42 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ImportPlanningXlsFile extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
// max 50 Mo
'file' => [
'required',
// phpcs:ignore Generic.Files.LineLength.TooLong
'mimetypes:application/vnd.ms-excel,application/vnd.ms-office,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/xml',
'max:51200',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if ($this->file->getClientMimeType() !== 'application/vnd.ms-excel') {
$fail(__('Le fichier doit être d\'un mime type application/vnd.ms-excel'));
}
}
]
];
}
}

View file

@ -0,0 +1,49 @@
<?php
namespace App\Http\Requests;
use App\Enums\TypeSondage;
use Illuminate\Foundation\Http\FormRequest;
class SondageResponsesRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return auth('employee')->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
$responsesIds = $this->sondage->reponseSondages()->pluck('id')->all();
switch ($this->sondage->type) {
case TypeSondage::radio():
case TypeSondage::checkbox():
return [
'reponses' => 'required|array',
'reponses.*' => [
'required',
'numeric',
'exists:reponse_sondages,id',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) use ($responsesIds) {
if (!in_array($value, $responsesIds)) {
$fail(trans('Une des réponses fournis ne fait pas partie du sondage'));
}
}
]
];
default:
abort(500, 'Type de sondage non géré');
}
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use App\Enums\CrudAction;
class StoreCrudModel extends CrudRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$model = $this->getCurrentModel();
/** @var \App\Models\User|null $user */
// $user = auth()->user();
// return auth()->check() and $user->can('update', $model);
$authorized = true;
if ($authorized) {
$this->rules = $this->getRulesForAction(CrudAction::create());
}
return $authorized;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return $this->rules;
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Http\Requests;
use App\Enums\CrudAction;
class UpdateCrudModel extends CrudRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$model = $this->getCurrentModel();
/** @var \App\Models\User|null $user */
// $user = auth()->user();
// return auth()->check() and $user->can('update', $model);
$authorized = true;
if ($authorized) {
$this->rules = $this->getRulesForAction(CrudAction::update());
}
return $authorized;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return $this->rules;
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Requests;
class UpdateSondage extends UpdateCrudModel
{
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return array_merge($this->rules, [
'reponses' => 'required|array',
'reponses.*' => 'required|string|min:10',
]);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class CrudResource extends JsonResource
{
/**
* Transform the resource collection into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return [
'label' => $this->crudLabel,
'value' => $this->crudValue
];
}
}

View file

@ -0,0 +1,177 @@
<?php
namespace App\Imports;
use App\Jobs\SendEmployeeCreatedAccountMail;
use App\Models\Employee;
use App\Models\EmployeeMailToken;
use App\Models\Site;
use Exception;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsEmptyRows;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
class EmployeeImport implements
ToCollection,
WithCustomCsvSettings,
SkipsEmptyRows
{
use Importable;
/** @var \App\Models\Site */
private $site;
/** @var array */
private $headers;
public $updatedCount = 0;
public $importedCount = 0;
public $errorMessages = [];
public $importedFailed = [];
public function __construct(Site $site)
{
$this->site = $site;
}
public function collection(Collection $rows)
{
/** @var \App\Models\User */
$user = auth()->user();
/** @var \App\Models\Site */
$site = Site::where('id', $user->selected_site_id)->firstOrFail();
$cMAT = 0;
$cNOM = 1;
$cPRENOM = 2;
$cMAIL = 3;
foreach ($rows as $k => $row) {
$employee = Employee::where('matricule', $row[$cMAT])->firstOrNew();
if (!$this->validateRow($k, $row, $employee)) {
$this->importedFailed[$k] = $row;
continue;
}
$createdAccount = false;
// If employee exists but is in a different Site
if (!is_null($employee->site) and $employee->site_id !== $site->id) {
$this->setErrorMessages($k, [sprintf(
'Le salariée %s %s %s existe déjà dans le site %s',
$row[$cMAT],
$row[$cNOM],
$row[$cPRENOM],
$site->name
)]);
$this->importedFailed[$k] = $row;
}
// set fields
$employee->fill([
'matricule' => $row[$cMAT],
'firstname' => $row[$cPRENOM],
'lastname' => $row[$cNOM],
'site_id' => $site->id,
]);
if (!$employee->exists) {
$createdAccount = true;
}
$employee->email = $row[$cMAIL];
// Anti date mdp
$employee->password_date = Carbon::now()->subDay();
$employee->password_attempts = 0;
try {
$employee->saveOrFail();
} catch (Exception $e) {
$this->setErrorMessages($k, [sprintf(
'Erreur %s',
$e->getMessage()
)]);
$this->importedFailed[$k] = $row;
continue;
}
if ($createdAccount) {
$this->importedCount += 1;
if ($employee->hashcode) {
$employee->hashcode->delete();
}
$employee->hashcode = new EmployeeMailToken([
'email' => $employee->email,
'employee_id' => $employee->id
]);
$employee->hashcode->saveOrFail();
dispatch(new SendEmployeeCreatedAccountMail($employee))
->onConnection('database');
} else {
$this->updatedCount += 1;
}
}
}
public function getCsvSettings(): array
{
return [
'input_encoding' => 'UTF-8',
'delimiter' => ';'
];
}
/**
* Validate row
*
* @param \Illuminate\Support\Collection $key
* @param array $row
* @return boolean
*/
private function validateRow(int $key, Collection $row, Employee $employee = null): bool
{
try {
$unique = 'unique:employees,email';
if ($employee->id) {
$unique .= ",$employee->id";
}
Validator::make($row->all(), [
'0' => 'required|digits_between:7,8',
'1' => 'required|string',
'2' => 'required|string',
'3' => "required|$unique|email:rfc"
], [], ['matricule', 'prénom', 'nom', 'email'])->validate();
} catch (ValidationException $e) {
$this->setErrorMessages($key, $e->errors());
return false;
}
return true;
}
/**
* Set import error messages
*
* @param integer $row
* @param array $messages
* @return void
*/
public function setErrorMessages(int $row, array $messages): void
{
if (!isset($this->errorMessages[$row])) {
$this->errorMessages[$row] = [];
}
$m1 = $this->errorMessages[$row];
$m2 = collect($messages)->flatten()->all();
array_merge_recursive_distinct($m1, $m2);
$this->errorMessages[$row] = array_merge_recursive_distinct($m1, $m2);
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace App\Imports;
use App\Models\Employee;
use App\Models\Site;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\SkipsOnFailure;
use Maatwebsite\Excel\Concerns\WithCustomCsvSettings;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithValidation;
use Maatwebsite\Excel\Validators\Failure;
class EmployeeSortie implements ToCollection, WithCustomCsvSettings, WithValidation, WithHeadingRow, SkipsOnFailure
{
use Importable;
/** @var \App\Models\Site */
private $site;
/** @var array */
private $headers;
public $removedCount = 0;
public $importedFailed = [];
public $errorMessages = [];
public function __construct(Site $site)
{
$this->site = $site;
}
public function collection(Collection $rows)
{
/** @var \App\Models\User */
$user = auth()->user();
/** @var \App\Models\Site */
$site = Site::where('id', $user->selected_site_id)->firstOrFail();
foreach ($rows as $row) {
$employee = Employee::where('matricule', $row->get('matricule_sirh'))->first();
if (!$employee) {
$this->importedFailed[] = $row;
Log::debug(sprintf('Employee Import Remove, skipped (Employee not found) %s', $row->implode(' | ')));
continue;
}
if (!$row->get('date_fin_sirh')) {
$this->importedFailed[] = $row;
continue;
}
if (Carbon::createFromFormat('d/m/Y', $row->get('date_fin_sirh')) < Carbon::now()) {
$employee->delete();
$this->removedCount += 1;
}
}
}
public function rules(): array
{
return [
'matricule_sirh' => 'required|digits_between:7,8',
'nom_sirh' => 'required|string',
'prenom_sirh' => 'required|string',
'date_fin_sirh' => 'required|string|date_format:d/m/Y',
'societe_groupe' => 'required|integer',
'libelle_societe_groupe' => 'required|string',
'site_groupe' => 'required|integer',
'libelle_site_groupe' => 'required|string'
];
}
public function getCsvSettings(): array
{
return [
'input_encoding' => 'UTF-8',
'delimiter' => ';'
];
}
/**
* @param Failure[] $failures
*/
public function onFailure(Failure ...$failures)
{
foreach ($failures as $failure) {
$this->errorMessages[] = sprintf(
"\nLigne %d :\n%s\nDonnée:\n%s\n",
$failure->row(),
\implode("\n", $failure->errors()),
\implode(", ", $failure->values())
);
}
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace App\Imports;
use App\Models\Employee;
use App\Models\Planning as ModelsPlanning;
use App\Models\Site;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\Importable;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
class PlanningImport implements ToCollection, WithHeadingRow
{
use Importable;
/** @var \App\Models\Site */
private $site;
/** @var string */
private $title;
public $importedCount = 0;
public $importedFailed = [];
public function __construct(Site $site, array $headers)
{
$this->site = $site;
$this->title = implode(collect($headers)->flatten()->all());
}
public function collection(Collection $rows)
{
$dates = null;
$planningLines = [];
$k = -1;
foreach ($rows as $row) {
// concatenate all merged rows where 'nom_prenom' is null
if (!is_null($row['nom_prenom'])) {
$planningLines[++$k] = $row;
} else {
$planningLines[$k] = $planningLines[$k]->map(function ($item, $key) use ($row) {
// ignore empty row titles
if (!strlen($key)) {
return false;
}
$d = $row->get($key);
if ($d and is_string($d) and strlen($d)) {
$item = strlen($item) ? sprintf('%s \n %s', $item, $d) : $d;
}
return $item;
})->reject(function ($value) {
return $value === false;
});
}
}
$planningLines = collect($planningLines);
// Get Start and End dates
$keys = collect(array_keys($planningLines->first()->all()));
$startDate = collect(explode('_', $keys->get(1)));
$endDate = collect(explode('_', $keys->last()));
$validator = Validator::make([
'startDay' => $startDate->first(),
'start' => Carbon::createFromFormat('dmY', $startDate->last()),
'endDay' => $endDate->first(),
'end' => Carbon::createFromFormat('dmY', $endDate->last()),
], [
'startDay' => 'required|in:lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche',
'start' => 'required|date',
'endDay' => 'required|in:lundi,mardi,mercredi,jeudi,vendredi,samedi,dimanche',
'end' => 'required|date',
]);
$validator->validate();
$dates = (object)$validator->validated();
/**
* Non mais bordel, déjà parser un xsls c'est de la merde
* mais en plus de pas fournir de clé et en plus ne pas séparer
* le nom et prénom, c'est vraiment WTF
*/
foreach ($planningLines as $line) {
// Try to find employee with "nom_prenom", pfff
$np = trim(preg_replace('/SST$/', '', $line['nom_prenom']));
$ns = explode(' ', $np);
$firstName = '';
$lastName = '';
$employee = null;
do {
$lastName = strlen($lastName) ?
sprintf('%s %s', $lastName, \array_shift($ns)) : \array_shift($ns);
$firstName = trim(implode(' ', $ns));
$employee = Employee::where('site_id', $this->site->id)
->whereRaw("UPPER(`lastname`) = '" . strtoupper($lastName) . "'")
->whereRaw("UPPER(`firstname`) = '" . strtoupper($firstName) . "'")
->get();
// phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
} while (strlen($firstName) and !count($employee) and count($ns) > 1);
if (!count($employee)) {
$this->importedFailed[] = $line;
Log::debug(sprintf('Planning Import, skipped (Employee not found) %s', $line->implode(' | ')));
continue;
}
$line->forget('nom_prenom');
$this->parsePlanningData($line);
// Get existing planning using start date or create a new one
$planning = ModelsPlanning::whereDate('start', $dates->start->format('Y/m/d'))
->where('employee_id', $employee->first()->id)->firstOrNew();
$planning->fill([
'start' => $dates->start,
'end' => $dates->end,
'data' => $line->all()
]);
$planning->employee_id = $employee->first()->id;
$planning->saveOrFail();
$this->importedCount += 1;
}
}
private function parsePlanningData(Collection &$data)
{
$data = $data->mapWithKeys(function ($day, $key) {
$key = substr($key, strpos($key, '_') + 1);
return ["$key" => $day];
});
}
public function headingRow(): int
{
return 2;
}
}

View file

@ -0,0 +1,48 @@
<?php
namespace App\Jobs;
use App\Mail\EmployeeCreatedMail;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendEmployeeCreatedAccountMail implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var Employee */
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee->withoutRelations();
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->email])
->send(new EmployeeCreatedMail($this->employee));
Log::debug(sprintf('Sent %s to %s', EmployeeCreatedMail::class, $this->employee->email));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Jobs;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendEmployeeInfoMailToSiteRH implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var Mailable */
protected $mail;
/** @var Employee */
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Mailable $mail, Employee $employee)
{
$this->mail = $mail;
$this->employee = $employee;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->site->email_contact])->send($this->mail);
Log::debug(sprintf('Sent %s to %s', get_class($this->mail), $this->employee->site->email_contact));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Jobs;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendEmployeeLockedDownMailToEmployee implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var Mailable */
protected $mail;
/** @var Employee */
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Mailable $mail, Employee $employee)
{
$this->mail = $mail;
$this->employee = $employee;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->email])->send($this->mail);
Log::debug(sprintf('Sent %s to %s', get_class($this->mail), $this->employee->email));
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace App\Jobs;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendEmployeeLockedDownMailToSiteRH implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/** @var Mailable */
protected $mail;
/** @var Employee */
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Mailable $mail, Employee $employee)
{
$this->mail = $mail;
$this->employee = $employee;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->site->email_contact])->send($this->mail);
Log::debug(sprintf('Sent %s to %s', get_class($this->mail), $this->employee->site->email_contact));
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Jobs;
use App\Mail\EmployeeConfirmMail;
use App\Models\Employee;
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\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendMailConfirmToEmployee implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->token->email])->send(new EmployeeConfirmMail($this->employee));
Log::debug(sprintf('Sent %s to %s', EmployeeConfirmMail::class, config('mail.contactsList.contact')));
}
}

View file

@ -0,0 +1,45 @@
<?php
namespace App\Jobs;
use App\Mail\EmployeePasswordResetMail;
use App\Models\Employee;
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\SerializesModels;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
class SendMailResetPasswordToEmployee implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
protected $employee;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
Mail::to(['email' => $this->employee->token->email])->send(new EmployeePasswordResetMail($this->employee));
Log::debug(sprintf('Sent %s to %s', EmployeePasswordResetMail::class, config('mail.contactsList.contact')));
}
}

193
app/Jobs/XArcadiaFlux.php Normal file
View file

@ -0,0 +1,193 @@
<?php
namespace App\Jobs;
use App\Enums\LogType;
use Exception;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
abstract class XArcadiaFlux implements ShouldQueue
{
use Dispatchable;
use InteractsWithQueue;
use Queueable;
use SerializesModels;
/**
* The Laravel disk name
*
* @var string
*/
protected $disk = null;
/**
* This is for Laravel Console usage
*/
public $console = null;
/**
* The relative folder where to archive data
*
* @var string
*/
private $archiveFolderName = 'archives';
/**
* The files to ignore
*
* @var array
*/
protected $ignoreFiles = [
'.gitignore', '.gitkeep'
];
/**
* Check if the file extension
*
* @param string $filename
* @return boolean
*/
protected function checkExtension(string $filename, string $extension): bool
{
$isExtension = strtolower(pathinfo($filename, \PATHINFO_EXTENSION)) === $extension;
if (!$isExtension) {
Log::warning(sprintf(
'%s : unwanted file type in %s wanted %s',
get_class($this),
$filename,
$extension
));
}
return $isExtension;
}
/**
* Move the file to archives
*
* @param string $filename
* @return void
*/
protected function moveToArchives(string $filename): void
{
if (!$this->getDisk()->move($filename, sprintf('%s/%s', $this->archiveFolderName, $filename))) {
$this->log(
sprintf(
'File %s could not be moved to archives',
$filename
),
LogType::warning()
);
}
}
/**
* Suppression des fichiers dans
* archives qui ont plus de 2j
*
* @return void
*/
protected function clearOldArchives(): void
{
$disk = $this->getDisk();
$archives = $disk->files($this->archiveFolderName);
// /** @var File */
foreach ($archives as $archive) {
$pi = \pathinfo($archive, \PATHINFO_BASENAME);
if (in_array($pi, $this->ignoreFiles)) {
continue;
}
if ($disk->lastModified($archive) < strtotime(Carbon::now()->subDays(2))) {
if (!$disk->delete($archive)) {
Log::critical(sprintf('%s : Could not delete file %s', get_class($this), $archive));
};
}
}
}
/**
* Get the concerned disk
*
* @return Filesystem
*/
protected function getDisk(): Filesystem
{
return Storage::disk($this->disk);
}
/**
* Logs data
*
* @param string $message
* @param LogType $logtype
* @return void
*/
protected function log(string $message, LogType $logtype = null): void
{
if (!$logtype) {
$logtype = LogType::debug();
}
$message = sprintf('%s : %s', get_class($this), $message);
switch ($logtype->value) {
case LogType::debug()->value:
Log::debug($message);
break;
case LogType::info()->value:
Log::info($message);
break;
case LogType::log()->value:
Log::log($message);
break;
case LogType::warning()->value:
Log::warning($message);
break;
case LogType::error()->value:
Log::error($message);
break;
case LogType::critical()->value:
Log::critical($message);
break;
default:
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
}
$this->consolePrint($message, $logtype);
}
/**
* Prints to the console
*
* @param string $message
* @param LogType $logtype
* @return void
*/
protected function consolePrint(string $message, LogType $logtype): void
{
if ($this->console) {
switch ($logtype->value) {
case LogType::debug()->value:
case LogType::info()->value:
case LogType::log()->value:
$this->console->info($message);
break;
case LogType::warning()->value:
$this->console->comment($message);
break;
case LogType::error()->value:
case LogType::critical()->value:
$this->console->error($message);
break;
default:
throw new Exception(sprintf('%s : unhandled log type %s', get_class($this), $logtype->label));
}
}
}
}

160
app/Jobs/XSCVMepReader.php Normal file
View file

@ -0,0 +1,160 @@
<?php
namespace App\Jobs;
use App\Enums\LogType;
use App\Exceptions\AbortCsvImportException;
use App\Exceptions\AbortRowImportException;
use App\Mail\OcbMissingBreederReminder;
use App\Models\OCBBreeder;
use App\Models\OCBWaitingList;
use App\Models\XmlBreeder;
use ErrorException;
use Illuminate\Support\Carbon;
use League\Csv\Reader;
use TypeError;
class XSCVMepReader extends XArcadiaFlux
{
/**
* The Laravel disk name
*
* @var string
*/
protected $disk = 'mise_en_place';
protected $expectedArray = [
"No Eleveur",
"Code mise en place LDC",
"Opa",
"Vivant",
"Numero semaine de sortie",
"Quantite Initiale",
"Date mise en place",
"Couvoir",
"Code Lot OPA",
"Nombre de volaille mises en place",
"Quantite Nouvelle",
"Libelle Opa",
"Libelle Vivant",
"Nom Prenom Eleveur",
"Code Société",
"Nom Société"
];
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
/** @var Filesystem */
$disk = $this->getDisk();
$this->log('Démarrage du traitement');
$files = $disk->files();
$progress = 1;
$total = count(array_filter($files, function ($filename) {
if ($this->fileHasToBeIgnored($filename)) {
return false;
}
return true;
}));
foreach ($files as $filename) {
if ($this->fileHasToBeIgnored($filename)) {
continue;
}
$this->log(sprintf(' traitement %d sur %s ', $progress++, $total));
/** @var string $data */
$data = $disk->get($filename);
//load the CSV document from a file path
$csv = Reader::createFromString($data);
$csv->setDelimiter(';');
$csv->setHeaderOffset(0);
$header = $csv->getHeader();
$records = $csv->getRecords();
try {
$this->assertHeaderIsAsExpected($header);
foreach ($records as $record) {
$noElev = $record['No Eleveur'];
$inuav = substr($noElev, strlen($noElev) - 7, strlen($noElev));
// Check if inuav exists
if (!OCBBreeder::where('inuavs', 'LIKE', sprintf('%%s%', $inuav))->count()) {
$iWL = OCBWaitingList::where('inuav_code', $inuav)->firstOrNew();
$iWL->inuav_code = $inuav;
$NP = explode(' ', $record['Nom Prenom Eleveur'], 2);
// try to find more info with xml_breeders
$xmlBreeder = XmlBreeder::where('inuav_code', $inuav)->first();
$iWL->fill([
'date_mep' => Carbon::createFromFormat('d/m/Y', $record['Date mise en place'])
->toDateTimeString(),
'last_name' => $NP[0],
'first_name' => $NP[1],
'cp' => $xmlBreeder->postal_code ?? '',
'city' => $xmlBreeder->city ?? '',
'inuav' => $inuav
]);
$iWL->save();
}
}
} catch (ErrorException | TypeError $e) {
$this->log(
sprintf(
'File %s could not be parsed => %s on line %d',
$filename,
$e->getMessage(),
$e->getLine()
),
LogType::critical()
);
continue; // ignore the file
} catch (AbortCsvImportException $e) {
$this->log(
sprintf(
'File %s could not be parsed => %s on line %d',
$filename,
$e->getMessage(),
$e->getLine()
),
LogType::error()
);
continue; // ignore the file
}
$this->moveToArchives($filename);
}
// Clear old archives
$this->clearOldArchives();
}
protected function assertHeaderIsAsExpected(array $header)
{
if ($this->expectedArray !== $header) {
throw new AbortRowImportException(trans('CSV Header does not match expected'));
}
}
/**
* Is the file to be ignored
*
* @param string $filename
* @return boolean
*/
protected function fileHasToBeIgnored(string $filename): bool
{
return in_array($filename, $this->ignoreFiles) or !$this->checkExtension($filename, 'csv');
}
}

View file

@ -0,0 +1,33 @@
<?php
namespace App\Jobs;
use App\Models\XmlBreeder;
class XmlEleveursReader extends XmlJobs
{
protected $disk = 'eleveurs';
protected $takeColumns = [
'codeopa.Nom' => 'opa_nom',
'codeopa.code' => 'opa_code',
'NomEleveur' => 'last_name',
'CodeEleveur' => 'inuav_code',
'Prenom' => 'first_name',
'code' => 'unique_key',
'codePostal' => 'postal_code',
'Ville' => 'city',
'LongitudeGps' => 'longitude',
'LatitudeGps' => 'latitude'
];
protected $nullableFields = ['longitude', 'latitude'];
protected $uniqueKeyField = ['unique_key', 'inuav_code'];
public function handle()
{
$this->parseData('ELEVEURS', XmlBreeder::class);
}
}

280
app/Jobs/XmlJobs.php Normal file
View file

@ -0,0 +1,280 @@
<?php
namespace App\Jobs;
use App\Enums\LogType;
use App\Exceptions\AbortXmlImportException;
use ErrorException;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use RecursiveArrayIterator;
use RecursiveIteratorIterator;
use SimpleXMLElement;
use TypeError;
abstract class XmlJobs extends XArcadiaFlux
{
/**
* The columns to take from the XML
* subcolumns can be added with a dot
*
* @var array
*/
protected $takeColumns = [];
/**
* The columns allowed to be null
*
* @var array
*/
protected $nullableFields = [];
/**
* Need to encapsulate the freeking XML
* Because provided one is dirty
*
* @var boolean
*/
protected $encapsulate = false;
/**
* Parse Data from the XML and gives it to the callback
*
* @param string $entityName
* @param string $modelClass
* @return void
*/
// phpcs:ignore Generic.Metrics.CyclomaticComplexity.TooHigh
protected function parseData(string $entityName, string $modelClass, callable $callback = null): void
{
/** @var Filesystem */
$disk = $this->getDisk();
$this->log('Démarrage du traitement');
$files = $disk->files();
$progress = 1;
$total = count(array_filter($files, function ($filename) {
if ($this->fileHasToBeIgnored($filename)) {
return false;
}
return true;
}));
foreach ($files as $filename) {
if ($this->fileHasToBeIgnored($filename)) {
continue;
}
$this->log(sprintf(' traitement %d sur %s ', $progress++, $total));
/** @var string $data */
$data = $disk->get($filename);
try {
if ($this->encapsulate) {
$data = sprintf('<Root>%s</Root>', $data);
/** @var SimpleXMLElement|false */
$xmlContent = simplexml_load_string($data);
} else {
/** @var SimpleXMLElement|false */
$xmlContent = simplexml_load_string($data, null, LIBXML_NOCDATA | LIBXML_PARSEHUGE);
}
if ($xmlContent === false) {
$this->log(
sprintf('File %s could not be parsed (simplexml_load_string)', $filename),
LogType::critical()
);
continue; // ignore the file
}
$data = $this->extract($xmlContent);
$lines = [];
foreach ($data[$entityName] as $traca) {
$line = $this->processFields($traca);
if ($callback) {
$callback($xmlContent, $line);
}
$lines[] = $line;
}
// Mise en base des données
foreach ($lines as $line) {
/** @var array Unique Key columns */
$uniqueColumnsKey = [];
foreach ($this->uniqueKeyField as $uField) {
$uniqueColumnsKey[$uField] = $line[$uField];
}
forward_static_call_array(
[$modelClass, 'updateOrCreate'],
[$uniqueColumnsKey, $line]
);
}
} catch (ErrorException | TypeError | QueryException $e) {
$message = $e->getMessage();
if (get_class($e) === QueryException::class) {
$message = implode(' ', $e->errorInfo);
}
$this->log(
sprintf(
'File %s could not be parsed => %s on line %d',
$filename,
$message,
$e->getLine()
),
LogType::critical()
);
continue; // ignore the file
} catch (AbortXmlImportException $e) {
$this->log(
sprintf(
'File %s could not be parsed => %s on line %d',
$filename,
$e->getMessage(),
$e->getLine()
),
LogType::error()
);
continue; // ignore the file
}
$this->moveToArchives($filename);
}
// Clear old archives
$this->clearOldArchives();
}
/**
* Recursive extract to array
*
* @param \SimpleXMLElement|false $sxe
* @return array
*/
protected function extract($sxe = null): array
{
if (!$sxe instanceof SimpleXMLElement) {
return array();
}
$extract = array();
foreach ($sxe->children() as $key => $value) {
if ($value->count()) {
$extract[$key][] = $this->extract($value);
continue;
}
$value = strval($value);
if (array_key_exists($key, $extract) and is_array($extract[$key])) {
$extract[$key][] = strlen($value) ? $value : null;
} else {
$extract[$key] = strlen($value) ? $value : null;
}
}
return $extract;
}
/**
* Process fields to gather data
*
* @param array $fields
* @return array
* @throws AbortXmlImportException
*/
protected function processFields(array $fields): array
{
$result = [];
foreach ($fields as $key => $field) {
if (!($key = $this->shouldFieldBeConsidered($key))) {
continue;
}
if (!$this->isFieldNullable($key) && is_null($field)) {
throw new AbortXmlImportException(sprintf('The field %s is not allowed to be null', $key));
}
if (is_array($field)) {
// Flatten data
$it = new RecursiveIteratorIterator(new RecursiveArrayIterator($field));
foreach ($it as $k => $f) {
$ko = strtolower(sprintf('%s.%s', $key, $k));
if (!$this->isFieldNullable($ko) && is_null($f)) {
throw new AbortXmlImportException(sprintf(
'%s : The field %s is not allowed to be null',
get_class($this),
$ko
));
}
if (!($k = $this->shouldFieldBeConsidered($ko))) {
continue;
}
$result[$k] = $f;
}
} else {
$result[$key] = $field;
}
}
return $result;
}
/**
* Is the filed nullable
*
* @param string $key
* @return boolean
*/
private function isFieldNullable(string $key): bool
{
if (in_array($key, $this->nullableFields)) {
return true;
}
return false;
}
/**
* - replace key name with the corresponding alias
* - strtolower the key
* - snake_case the key
*
* @param string $key
* @return string|false
*/
private function shouldFieldBeConsidered(string $key)
{
$key = strtolower($key);
foreach ($this->takeColumns as $k => $alias) {
if (strtolower($k) === $key) {
return Str::snake(strtolower($alias));
}
}
return false;
}
/**
* Is the file to be ignored
*
* @param string $filename
* @return boolean
*/
protected function fileHasToBeIgnored(string $filename): bool
{
return in_array($filename, $this->ignoreFiles) or !$this->checkExtension($filename, 'xml');
}
/**
* Check if the file is an xml file
*
* @param string $filename
* @return boolean
*/
protected function isFileXml(string $filename): bool
{
$isXml = strtolower(pathinfo($filename, \PATHINFO_EXTENSION)) === 'xml';
if (!$isXml) {
Log::warning(sprintf('%s : unwanted file type in %s', get_class($this), $filename));
}
return $isXml;
}
}

View file

@ -0,0 +1,104 @@
<?php
namespace App\Lib\PolicySystem;
use App\Models\User;
use App\Lib\PolicySystem\StaticRules as SR;
use Illuminate\Auth\Access\Response;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
class RootPolicy
{
protected $policy = null;
protected $policies = null;
public function __construct()
{
$cls = \explode('\\', \get_called_class());
$this->policy = \strtolower(\str_replace('Policy', '', \end($cls)));
}
protected function actionPolicy(string $method, string $policy, User $user, Model $model = null)
{
$this->setPolicies($user, $model);
$ctx = [$policy, $user, $model];
if (!$this->hasPolicy($policy)) {
$m = __('Unhandled policy ":policy"', ['policy' => $policy]);
Log::critical($m, $ctx);
return $this->deny($m);
}
if (!is_array($this->policies[$policy])) {
$m = __('Policy ":policy" has to be array', ['policy' => $policy]);
Log::critical($m, $ctx);
return $this->deny($m);
}
// Policy method exists
if (!array_key_exists($method, $this->policies[$policy])) {
$m = __('Unhandled policy ":policy" for action ":action"', ['policy' => $policy, 'action' => $method]);
Log::critical($m, $ctx);
return $this->deny($m);
}
// Call policy method
if (Sr::handle($this->policies[$policy][$method], $user, $model)) {
return $this->allow();
} else {
$m = __('Policy ":policy" for action ":action" denied :onmodel', [
'policy' => $policy,
'action' => $method,
'onmodel' => $model ? get_class($model) : ''
]);
Log::debug(sprintf("%s user => %s model => %s", $m, $user, $model), $ctx);
return $this->deny($m);
}
$m = __('Unhandled policy case on resource ":method"', ['method' => $method]);
Log::critical($m, $ctx);
return $this->deny();
}
/**
** Do the magic calling Root policy from any policy
*/
public function __call($method, $args)
{
return call_user_func_array([$this, 'actionPolicy'], array_merge([$method, $this->policy], $args));
}
private function hasPolicy(string $policy)
{
return array_key_exists($policy, $this->policies);
}
/**
* Allow action for the user
*
* @param string|null $message
* @return \Illuminate\Auth\Access\Response
*/
protected function allow(?string $message = null)
{
return Response::allow($message);
}
/**
* Deny action for the user
*
* @param string|null $message
* @param mixed $code
* @return \Illuminate\Auth\Access\Response
*/
protected function deny(?string $message = null, $code = null)
{
if (!$message) {
$message = __('This action is not allowed to you for this resource.');
}
return Response::deny($message, $code);
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace App\Lib\PolicySystem;
use App\Models\User;
use Exception;
use Closure;
use Illuminate\Database\Eloquent\Model;
/**
* Generic Policiy Static Rules
*! Rules has to return boolean value
*/
abstract class StaticRules
{
/**
* Make a rule closure
*
* @param string $rulename
* @param mixed ...$args
* @return Closure
*/
public static function make(string $rulename, ...$args)
{
if (!method_exists(static::class, $rulename)) {
throw new Exception(sprintf('Unknown rule %s in Policies StaticRules', $rulename));
}
return function () use ($rulename, $args) {
return call_user_func_array([static::class, $rulename], $args);
};
}
public static function and(array $ruleClosures)
{
return function (User $user, Model $model = null) use ($ruleClosures) {
$nbRules = count($ruleClosures);
$out = true;
$i = 0;
do {
if (!($out = (self::handle($ruleClosures[$i++], $user, $model) && $out))) {
break;
}
} while ($i < $nbRules);
return $out;
};
}
public static function or(array $ruleClosures)
{
return function (User $user, Model $model = null) use ($ruleClosures) {
$nbRules = count($ruleClosures);
$out = false;
$i = 0;
do {
if ($out = (self::handle($ruleClosures[$i++], $user, $model) || $out)) {
break;
}
} while ($i < $nbRules);
return $out;
};
}
public static function handle($rule, User $user, Model $model = null)
{
if (is_array($rule)) {
$nbRule = count($rule);
$out = false;
$i = 0;
while ($i < $nbRule) {
if ($out = self::handle($rule[$i++], $user, $model)) {
break;
}
}
return $out;
}
if (!is_callable($rule)) {
throw new Exception('Rule has to ba callable.');
}
return (bool)$rule($user, $model);
}
protected static function anyone()
{
return true;
}
}

104
app/Lib/Utils.php Normal file
View file

@ -0,0 +1,104 @@
<?php
namespace App\Lib;
// phpcs:disable Generic.Files.LineLength.TooLong
class Utils
{
/**
* Replace stange accents
*
* https://stackoverflow.com/questions/3371697/replacing-accented-characters-php
* @author Jérémy Munsch <jeremy.munsch@jeremydev.ovh>
* @param string $subject
* @return string
*/
public static function replaceSpecChar(string $subject): string
{
$char_map = array(
"ъ" => "-", "ь" => "-", "Ъ" => "-", "Ь" => "-",
"А" => "A", "Ă" => "A", "Ǎ" => "A", "Ą" => "A", "À" => "A", "Ã" => "A", "Á" => "A", "Æ" => "A", "Â" => "A", "Å" => "A", "Ǻ" => "A", "Ā" => "A", "א" => "A",
"Б" => "B", "ב" => "B", "Þ" => "B",
"Ĉ" => "C", "Ć" => "C", "Ç" => "C", "Ц" => "C", "צ" => "C", "Ċ" => "C", "Č" => "C", "©" => "C", "ץ" => "C",
"Д" => "D", "Ď" => "D", "Đ" => "D", "ד" => "D", "Ð" => "D",
"È" => "E", "Ę" => "E", "É" => "E", "Ë" => "E", "Ê" => "E", "Е" => "E", "Ē" => "E", "Ė" => "E", "Ě" => "E", "Ĕ" => "E", "Є" => "E", "Ə" => "E", "ע" => "E",
"Ф" => "F", "Ƒ" => "F",
"Ğ" => "G", "Ġ" => "G", "Ģ" => "G", "Ĝ" => "G", "Г" => "G", "ג" => "G", "Ґ" => "G",
"ח" => "H", "Ħ" => "H", "Х" => "H", "Ĥ" => "H", "ה" => "H",
"I" => "I", "Ï" => "I", "Î" => "I", "Í" => "I", "Ì" => "I", "Į" => "I", "Ĭ" => "I", "I" => "I", "И" => "I", "Ĩ" => "I", "Ǐ" => "I", "י" => "I", "Ї" => "I", "Ī" => "I", "І" => "I",
"Й" => "J", "Ĵ" => "J",
"ĸ" => "K", "כ" => "K", "Ķ" => "K", "К" => "K", "ך" => "K",
"Ł" => "L", "Ŀ" => "L", "Л" => "L", "Ļ" => "L", "Ĺ" => "L", "Ľ" => "L", "ל" => "L",
"מ" => "M", "М" => "M", "ם" => "M",
"Ñ" => "N", "Ń" => "N", "Н" => "N", "Ņ" => "N", "ן" => "N", "Ŋ" => "N", "נ" => "N", "ʼn" => "N", "Ň" => "N",
"Ø" => "O", "Ó" => "O", "Ò" => "O", "Ô" => "O", "Õ" => "O", "О" => "O", "Ő" => "O", "Ŏ" => "O", "Ō" => "O", "Ǿ" => "O", "Ǒ" => "O", "Ơ" => "O",
"פ" => "P", "ף" => "P", "П" => "P",
"ק" => "Q",
"Ŕ" => "R", "Ř" => "R", "Ŗ" => "R", "ר" => "R", "Р" => "R", "®" => "R",
"Ş" => "S", "Ś" => "S", "Ș" => "S", "Š" => "S", "С" => "S", "Ŝ" => "S", "ס" => "S",
"Т" => "T", "Ț" => "T", "ט" => "T", "Ŧ" => "T", "ת" => "T", "Ť" => "T", "Ţ" => "T",
"Ù" => "U", "Û" => "U", "Ú" => "U", "Ū" => "U", "У" => "U", "Ũ" => "U", "Ư" => "U", "Ǔ" => "U", "Ų" => "U", "Ŭ" => "U", "Ů" => "U", "Ű" => "U", "Ǖ" => "U", "Ǜ" => "U", "Ǚ" => "U", "Ǘ" => "U",
"В" => "V", "ו" => "V",
"Ý" => "Y", "Ы" => "Y", "Ŷ" => "Y", "Ÿ" => "Y",
"Ź" => "Z", "Ž" => "Z", "Ż" => "Z", "З" => "Z", "ז" => "Z",
"а" => "a", "ă" => "a", "ǎ" => "a", "ą" => "a", "à" => "a", "ã" => "a", "á" => "a", "æ" => "a", "â" => "a", "å" => "a", "ǻ" => "a", "ā" => "a", "א" => "a",
"б" => "b", "ב" => "b", "þ" => "b",
"ĉ" => "c", "ć" => "c", "ç" => "c", "ц" => "c", "צ" => "c", "ċ" => "c", "č" => "c", "©" => "c", "ץ" => "c",
"Ч" => "ch", "ч" => "ch",
"д" => "d", "ď" => "d", "đ" => "d", "ד" => "d", "ð" => "d",
"è" => "e", "ę" => "e", "é" => "e", "ë" => "e", "ê" => "e", "е" => "e", "ē" => "e", "ė" => "e", "ě" => "e", "ĕ" => "e", "є" => "e", "ə" => "e", "ע" => "e",
"ф" => "f", "ƒ" => "f",
"ğ" => "g", "ġ" => "g", "ģ" => "g", "ĝ" => "g", "г" => "g", "ג" => "g", "ґ" => "g",
"ח" => "h", "ħ" => "h", "х" => "h", "ĥ" => "h", "ה" => "h",
"i" => "i", "ï" => "i", "î" => "i", "í" => "i", "ì" => "i", "į" => "i", "ĭ" => "i", "ı" => "i", "и" => "i", "ĩ" => "i", "ǐ" => "i", "י" => "i", "ї" => "i", "ī" => "i", "і" => "i",
"й" => "j", "Й" => "j", "Ĵ" => "j", "ĵ" => "j",
"ĸ" => "k", "כ" => "k", "ķ" => "k", "к" => "k", "ך" => "k",
"ł" => "l", "ŀ" => "l", "л" => "l", "ļ" => "l", "ĺ" => "l", "ľ" => "l", "ל" => "l",
"מ" => "m", "м" => "m", "ם" => "m",
"ñ" => "n", "ń" => "n", "н" => "n", "ņ" => "n", "ן" => "n", "ŋ" => "n", "נ" => "n", "ʼn" => "n", "ň" => "n",
"ø" => "o", "ó" => "o", "ò" => "o", "ô" => "o", "õ" => "o", "о" => "o", "ő" => "o", "ŏ" => "o", "ō" => "o", "ǿ" => "o", "ǒ" => "o", "ơ" => "o",
"פ" => "p", "ף" => "p", "п" => "p",
"ק" => "q",
"ŕ" => "r", "ř" => "r", "ŗ" => "r", "ר" => "r", "р" => "r", "®" => "r",
"ş" => "s", "ś" => "s", "ș" => "s", "š" => "s", "с" => "s", "ŝ" => "s", "ס" => "s",
"т" => "t", "ț" => "t", "ט" => "t", "ŧ" => "t", "ת" => "t", "ť" => "t", "ţ" => "t",
"ù" => "u", "û" => "u", "ú" => "u", "ū" => "u", "у" => "u", "ũ" => "u", "ư" => "u", "ǔ" => "u", "ų" => "u", "ŭ" => "u", "ů" => "u", "ű" => "u", "ǖ" => "u", "ǜ" => "u", "ǚ" => "u", "ǘ" => "u",
"в" => "v", "ו" => "v",
"ý" => "y", "ы" => "y", "ŷ" => "y", "ÿ" => "y",
"ź" => "z", "ž" => "z", "ż" => "z", "з" => "z", "ז" => "z", "ſ" => "z",
"™" => "tm",
"@" => "at",
"Ä" => "ae", "Ǽ" => "ae", "ä" => "ae", "æ" => "ae", "ǽ" => "ae",
"ij" => "ij", "IJ" => "ij",
"я" => "ja", "Я" => "ja",
"Э" => "je", "э" => "je",
"ё" => "jo", "Ё" => "jo",
"ю" => "ju", "Ю" => "ju",
"œ" => "oe", "Œ" => "oe", "ö" => "oe", "Ö" => "oe",
"щ" => "sch", "Щ" => "sch",
"ш" => "sh", "Ш" => "sh",
"ß" => "ss",
"Ü" => "ue",
"Ж" => "zh", "ж" => "zh",
);
return self::mbStrtr($subject, $char_map);
}
/**
* Multibyte strtr
*
* @param string $str
* @param string|array $from
* @param string|null $to
* @return void
*/
public static function mbStrtr($str, $from, $to = null)
{
if (is_array($from)) {
$from = array_map('utf8_decode', $from);
$from = array_map('utf8_decode', array_flip($from));
return utf8_encode(strtr(utf8_decode($str), array_flip($from)));
}
return utf8_encode(strtr(utf8_decode($str), utf8_decode($from), utf8_decode($to)));
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Mail;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class EmployeeConfirmMail extends Mailable
{
use Queueable;
use SerializesModels;
protected $employee;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Veuillez confirmer votre adresse email', config('app.name')))
->view('emails.mail-confirm')
->text('emails.mail-confirm-plain')
->with('employee', $this->employee);
}
}

View file

@ -0,0 +1,36 @@
<?php
namespace App\Mail;
use App\Models\Employee;
use Illuminate\Mail\Mailable;
class EmployeeCreatedMail extends Mailable
{
protected $employee;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Un compte viens de vous être créé', config('app.name')))
->view('emails.mail-created')
->text('emails.mail-created-plain')
->with('employee', $this->employee);
}
}

53
app/Mail/EmployeeInfo.php Normal file
View file

@ -0,0 +1,53 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
abstract class EmployeeInfo extends Mailable
{
use Queueable;
use SerializesModels;
protected $data;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(array $data)
{
$this->data = $data;
}
/**
* Build the message.
*
* @return $this
*/
abstract public function build();
/**
* Attach a joined file if given in data
*
* @return void
*/
protected function joinFileIfProvided(): void
{
if (isset($this->data['file'])) {
$joinedFileName = sprintf(
'PortEmployee-PJ-%s.%s',
date("YmdHis"),
$this->data['file']->extension()
);
$this->attach($this->data['file'], [
'as' => $joinedFileName,
'mime' => $this->data['file']->getMimeType()
]);
}
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Mail;
class EmployeeInfoAddress extends EmployeeInfo
{
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->joinFileIfProvided();
return $this
->subject(sprintf('%s : Un salarié à mis à jour ses infos', config('app.name')))
->view('emails.employee-address')
->text('emails.employee-address-plain')
->with('employee', $this->data['employee'])
->with('data', $this->data);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Mail;
class EmployeeInfoBank extends EmployeeInfo
{
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->joinFileIfProvided();
return $this
->subject(sprintf('%s : Un salarié à mis à jour ses infos', config('app.name')))
->view('emails.employee-bank')
->text('emails.employee-bank-plain')
->with('employee', $this->data['employee'])
->with('data', $this->data);
}
}

View file

@ -0,0 +1,22 @@
<?php
namespace App\Mail;
class EmployeeInfoFamily extends EmployeeInfo
{
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$this->joinFileIfProvided();
return $this
->subject(sprintf('%s : Un salarié à mis à jour ses infos', config('app.name')))
->view('emails.employee-family')
->text('emails.employee-family-plain')
->with('employee', $this->data['employee'])
->with('data', $this->data);
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Mail;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class EmployeeLockedDown extends Mailable
{
use Queueable;
use SerializesModels;
/** @var Employee */
protected $employee;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Un compte salarié viens d\'être verrouillé', config('app.name')))
->view('emails.employee-lockedDown')
->text('emails.employee-lockedDown-plain')
->with('employee', $this->employee);
}
}

View file

@ -0,0 +1,40 @@
<?php
namespace App\Mail;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class EmployeePasswordResetMail extends Mailable
{
use Queueable;
use SerializesModels;
protected $employee;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Voici un lien pour réinitialiser votre mot de passe', config('app.name')))
->view('emails.password-reset')
->text('emails.password-reset-plain')
->with('employee', $this->employee);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Mail;
class EmployeeQuestionFormation extends EmployeeInfo
{
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Un salarié a une question', config('app.name')))
->view('emails.employee-question-formation')
->text('emails.employee-question-formation-plain')
->with('employee', $this->data['employee'])
->with('data', $this->data);
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace App\Mail;
class EmployeeQuestionRdv extends EmployeeInfo
{
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Un salarié vousdrais prendre rendez-vous', config('app.name')))
->view('emails.employee-question-rdv')
->text('emails.employee-question-rdv-plain')
->with('employee', $this->data['employee'])
->with('data', $this->data);
}
}

View file

@ -0,0 +1,41 @@
<?php
namespace App\Mail;
use App\Models\Employee;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class EmployeeYourAccountIsLockedDown extends Mailable
{
use Queueable;
use SerializesModels;
/** @var Employee */
protected $employee;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(Employee $employee)
{
$this->employee = $employee;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this
->subject(sprintf('%s : Votre compte salarié viens d\'être verrouillé', config('app.name')))
->view('emails.employee-own-lockedDown')
->text('emails.employee-own-lockedDown-plain')
->with('employee', $this->employee);
}
}

277
app/Models/Employee.php Normal file
View file

@ -0,0 +1,277 @@
<?php
namespace App\Models;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection as SupportCollection;
class Employee extends Authenticatable
{
use HasFactory;
use IsCrudModel;
protected $crudLabelColumn = 'email';
protected $crudValueColumn = 'id';
protected $fillable = [
'matricule',
'firstname',
'lastname',
'email',
'site_id'
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'password_date' => 'datetime',
];
protected $attributes = [
'password_attempts' => 0
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'matricule' => [
'label' => trans('Matricule'),
'type' => CrudType::string(),
'readonly' => true,
'validate' => [
'string',
'min:10',
'max:50'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'firstname' => [
'label' => trans('Prénom'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:60'
],
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'lastname' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'nullable' => false,
'validate' => [
'string',
'max:60'
],
'rules' => [
'min' => 5,
'max' => 10,
'step' => 2
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'email' => [
'label' => trans('Email'),
'type' => CrudType::email(),
'nullable' => false,
'validate' => [
'max:150'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'password' => [
'label' => trans('Mot de passe'),
'placeholder' => '#M1s3Cure.P4ss0rd!',
'type' => CrudType::password(),
'nullable' => false,
'validate' => [
'max:150'
],
'actions' => [
CrudAction::create(),
CrudAction::delete()
]
],
'site_id' => [
'label' => trans('Site'),
'type' => CrudType::belongsTo(),
'belongsTo' => Site::class,
'actions' => [
CrudAction::create(),
CrudAction::update()
]
],
'blocked' => [
'label' => trans('Verrouillé'),
'type' => CrudType::boolean(),
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::update(),
],
'getAttribute' => function () {
return $this->getAttribute('password_attempts') >= 3;
},
'setAttribute' => function ($value) {
$this->setAttribute('password_attempts', $value ? 3 : 0);
return $this->getAttribute('password_attempts');
},
]
];
parent::__construct($attributes);
}
protected static function booted()
{
static::creating(function ($employee) {
$employee->password = Hash::make($employee->password);
$employee->password_date = Carbon::now()->subDay();
});
static::updating(function ($employee) {
if (Hash::needsRehash($employee->password)) {
$employee->password = Hash::make($employee->password);
}
});
}
// RELATIONS
/**
* Has one Site
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function site()
{
return $this->belongsTo(Site::class);
}
/**
* Has many Planning
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function plannings()
{
return $this->hasMany(Planning::class);
}
/**
* Has one EmployeeMailToken
*
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function token()
{
return $this->hasOne(EmployeeMailToken::class);
}
/**
* BelongsToMany
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function reponseSondages()
{
return $this->belongsToMany(ReponseSondage::class)->withTimestamps();
}
// METHODS
/**
* Has the user answered to a specific sondage ?
* @param Sondage $sondage
* @return bool
*/
public function hasAnweredToSondage(Sondage $sondage): bool
{
$responsesIDS = $sondage->reponseSondages()->pluck(sprintf('%s.id', (new ReponseSondage())->getTable()));
return (bool)$this->reponseSondages()->wherePivotIn('reponse_sondage_id', $responsesIDS)->count();
}
/**
* Get Employee site flashes
*
* @return Collection
*/
public function getFlashes(): Collection
{
return $this->site->flashes()->orderBy('created_at', 'DESC')->get();
}
/**
* Get Employee unanswered Sondages
*
* @return Collection
*/
public function unAnsweredSondages(): Collection
{
$siteId = $this->site_id;
$sondagesIDS = $this->reponseSondages()->with('sondage')->get()->pluck('sondage.id');
return Sondage::whereHas('site', function ($q) use ($siteId) {
$q->whereId($siteId);
})->whereNotIn('id', $sondagesIDS)
->orderBy('created_at', 'DESC')
->get();
}
/**
* Prevent Infinite loop on mail send to Employee
* {@inheritdoc}
* @url https://stackoverflow.com/questions/52514863/laravel-getqueueablerelations-circular-relation
*/
public function getQueueableRelations()
{
return [];
}
/**
* Get Plannings for the current week + next week
*
* @return \Illuminate\Support\Collection
*/
public function getWeekAndNextPlannings(): SupportCollection
{
$now = Carbon::now()->format('Y/m/d');
$nowPlus7 = Carbon::now()->addDays(7)->format('Y/m/d');
return collect([
'now' => Planning::whereDate('start', '<', $now)
->whereDate('end', '>', $now)
->where('employee_id', $this->id)
->first(),
'next' => Planning::whereDate('start', '<', $nowPlus7)
->whereDate('end', '>', $nowPlus7)
->where('employee_id', $this->id)
->first()
]);
}
}

View file

@ -0,0 +1,34 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
class EmployeeMailToken extends Model
{
use HasFactory;
protected $fillable = [
'email',
'hashcode',
'employee_id'
];
protected static function booted()
{
static::creating(function ($employee) {
$employee->hashcode = Str::uuid();
});
}
/**
* Has one Employee
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function employee()
{
return $this->belongsTo(Employee::class);
}
}

75
app/Models/Flash.php Normal file
View file

@ -0,0 +1,75 @@
<?php
namespace App\Models;
use App\Casts\NiceDate;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Flash extends Model
{
use HasFactory;
use IsCrudModel;
protected $crudLabelColumn = 'title';
protected $crudValueColumn = 'id';
protected $fillable = [
'title',
'content',
'status'
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'title' => [
'label' => trans('Titre'),
'type' => CrudType::string(),
'rule' => [
'max' => 255
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'content' => [
'label' => trans('Contenu'),
'type' => CrudType::text(),
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'status' => [
'label' => trans('Publié'),
'type' => CrudType::boolean(),
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}
/**
* Has one Site
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function site()
{
return $this->belongsTo(Site::class);
}
}

38
app/Models/Planning.php Normal file
View file

@ -0,0 +1,38 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Planning extends Model
{
use HasFactory;
protected $fillable = [
'start',
'end',
'data'
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'start' => 'datetime',
'end' => 'datetime',
'data' => 'array'
];
/**
* Has one Employee
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function employee()
{
return $this->belongsTo(Employee::class);
}
}

54
app/Models/Pole.php Normal file
View file

@ -0,0 +1,54 @@
<?php
namespace App\Models;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Pole extends Model
{
use HasFactory;
use IsCrudModel;
protected $crudLabelColumn = 'name';
protected $crudValueColumn = 'id';
protected $fillable = [
'name'
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'name' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
]
];
parent::__construct($attributes);
}
/**
* Has many Societe
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function societes()
{
return $this->hasMany(Societe::class);
}
}

View file

@ -0,0 +1,37 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Concerns\HasTimestamps;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class ReponseSondage extends Model
{
use HasFactory;
use HasTimestamps;
protected $fillable = [
'value'
];
/**
* Has one Sondage
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function sondage()
{
return $this->belongsTo(Sondage::class);
}
/**
* BelongsToMany
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/
public function employees()
{
return $this->belongsToMany(Employee::class)->withTimestamps();
}
}

View file

@ -0,0 +1,10 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
class ReponseSondageEmployee extends Pivot
{
//
}

227
app/Models/Site.php Normal file
View file

@ -0,0 +1,227 @@
<?php
namespace App\Models;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Site extends Model
{
use HasFactory;
use IsCrudModel;
protected $crudLabelColumn = 'name';
protected $crudValueColumn = 'id';
protected $fillable = [
'url',
'name',
'email_contact',
'address',
'city',
'postcode',
'latitude',
'longitude'
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'url' => [
'label' => trans('Url'),
'type' => CrudType::string(),
'validate' => [
'url',
'max:255'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'name' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:255'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'email_contact' => [
'label' => trans('Email de contact'),
'type' => CrudType::email(),
'required' => true,
'nullable' => false,
'validate' => [
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'address' => [
'label' => trans('Adresse'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:255'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'city' => [
'label' => trans('Ville'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:70'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'postcode' => [
'label' => trans('Code postal'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:10'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'latitude' => [
'label' => trans('Latitude'),
'type' => CrudType::float(),
'validate' => [
'numeric',
'between:-89.99,90'
],
'rules' => [
'min' => -89.99,
'max' => 90,
'step' => 0.01
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'longitude' => [
'label' => trans('Longitude'),
'type' => CrudType::float(),
'validate' => [
'numeric',
'between:-179.99,180.00',
],
'rules' => [
'min' => -179.99,
'max' => 180,
'step' => 0.01
],
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'societe_id' => [
'label' => trans('Societe'),
'type' => CrudType::belongsTo(),
'belongsTo' => Site::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}
/**
* Has one Societe
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function societe()
{
return $this->belongsTo(Societe::class);
}
/**
* Has many Employee
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function employees()
{
return $this->hasMany(Employee::class);
}
/**
* Belongs To Many Sites
*
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
public function users()
{
return $this->belongsToMany(Site::class);
}
/**
* Has many Flash
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function flashes()
{
return $this->hasMany(Flash::class);
}
/**
* Has many Sondage
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function sondages()
{
return $this->hasMany(Sondage::class);
}
}

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

@ -0,0 +1,129 @@
<?php
namespace App\Models;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\UploadedFile;
class Societe extends Model
{
use HasFactory;
use IsCrudModel;
protected $crudLabelColumn = 'name';
protected $crudValueColumn = 'id';
protected $fillable = [
'name',
'logo',
'style',
'colors'
];
protected $casts = [
'colors' => 'array',
];
protected $attributes = [
'style' => '',
'colors' => '{"color0":"#EEE"'
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'name' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'validate' => [
'string',
'min:10',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'logo' => [
'label' => trans('Logo'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'style' => [
'label' => trans('Style'),
'type' => CrudType::text(),
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'colors' => [
'label' => trans('Couleurs'),
'type' => CrudType::text(),
'actions' => [
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'pole_id' => [
'label' => trans('Pole'),
'type' => CrudType::belongsTo(),
'belongsTo' => Pole::class,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}
/**
* Has one Pole
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function pole()
{
return $this->belongsTo(Pole::class);
}
/**
* Has many Site
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function sites()
{
return $this->hasMany(Site::class);
}
public static function saveLogo(UploadedFile $image, string $name)
{
return $image->storePubliclyAs(
'/public/societe',
sprintf('%s-%s.%s', $name, rand(10000, 99999), $image->extension())
);
}
}

118
app/Models/Sondage.php Normal file
View file

@ -0,0 +1,118 @@
<?php
namespace App\Models;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Enums\TypeSondage;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Kwaadpepper\Enum\Traits\CastsEnums;
class Sondage extends Model
{
use HasFactory;
use CastsEnums;
use IsCrudModel;
protected $crudLabelColumn = 'question';
protected $crudValueColumn = 'id';
protected $enumCasts = [
'type' => TypeSondage::class
];
protected $fillable = [
'type',
'question',
'status'
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'question' => [
'label' => trans('Question'),
'type' => CrudType::string(),
'validate' => [
'max:255'
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'type' => [
'label' => trans('Type'),
'type' => CrudType::enum(),
'enum' => TypeSondage::class,
'validate' => [
'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (!in_array($value, TypeSondage::toValues())) {
$fail(trans('Veuillez choisir entre :values', [
'value' => implode(',', TypeSondage::toLabels())
]));
}
}
],
'required' => true,
'nullable' => false,
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'reponses' => [
'label' => trans('Nombre de Réponses'),
'type' => CrudType::string(),
'actions' => [
CrudAction::viewAny()
],
'getAttribute' => function () {
return $this->reponseSondages()->count();
},
],
'status' => [
'label' => trans('Publié'),
'type' => CrudType::boolean(),
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
]
];
parent::__construct($attributes);
}
/**
* Has one Site
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function site()
{
return $this->belongsTo(Site::class);
}
/**
* Has many ReponseSondage
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function reponseSondages()
{
return $this->hasMany(ReponseSondage::class);
}
}

View file

@ -2,14 +2,31 @@
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use App\Enums\CrudAction;
use App\Enums\CrudType;
use App\Enums\Role;
use App\Traits\IsCrudModel;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Kwaadpepper\Enum\Traits\CastsEnums;
class User extends Authenticatable
{
use HasFactory, Notifiable;
use HasFactory;
use Notifiable;
use CastsEnums;
use IsCrudModel;
protected $crudLabelColumn = 'name';
protected $crudValueColumn = 'id';
protected $enumCasts = [
'role' => Role::class
];
/**
* The attributes that are mass assignable.
@ -17,9 +34,11 @@ class User extends Authenticatable
* @var array
*/
protected $fillable = [
'role',
'name',
'email',
'password',
'site_id'
];
/**
@ -32,12 +51,124 @@ class User extends Authenticatable
'remember_token',
];
public function __construct($attributes = [])
{
self::$editableProperties = [
'role' => [
'label' => trans('Role'),
'type' => CrudType::enum(),
'enum' => Role::class,
'validate' => [
'required',
// phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassBeforeLastUsed
function ($attribute, $value, $fail) {
if (!in_array($value, Role::toValues())) {
$fail(trans('Veuillez choisir entre :values', [
'value' => implode(',', Role::toLabels())
]));
}
}
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update()
]
],
'name' => [
'label' => trans('Nom'),
'type' => CrudType::string(),
'validate' => [
'string',
'max:255'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'email' => [
'label' => trans('Email'),
'type' => CrudType::email(),
'nullable' => false,
'validate' => [
'max:255',
'unique:users,email'
],
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
],
'password' => [
'label' => trans('Mot de passe'),
'placeholder' => '#M1s3Cure.P4ss0rd!',
'type' => CrudType::password(),
'nullable' => true,
'required' => false,
'validate' => [
'nullable',
'string',
'max:255'
],
'actions' => [
CrudAction::create(),
CrudAction::update()
]
],
'sites' => [
'label' => trans('Sites'),
'type' => CrudType::belongsToMany(),
'belongsToMany' => Site::class,
'belongsToManyLabel' => 'name',
'actions' => [
CrudAction::viewAny(),
CrudAction::view(),
CrudAction::create(),
CrudAction::update(),
]
]
];
parent::__construct($attributes);
}
protected static function booted()
{
static::creating(function ($user) {
$user->password = Hash::make($user->password);
$user->remember_token = Str::random(10);
});
static::updating(function ($user) {
if (Hash::needsRehash($user->password)) {
$user->password = Hash::make($user->password);
}
$user->remember_token = Str::random(10);
});
}
/**
* The attributes that should be cast to native types.
* Belongs To Many Sites
*
* @var array
* @return \Illuminate\Database\Eloquent\Relations\belongsToMany
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
public function sites()
{
return $this->belongsToMany(Site::class, 'user_site');
}
/**
* Get User Selected Site
*
* @return Site|null
*/
public function selectedSite()
{
return Site::where('id', $this->selected_site_id)->first();
}
}

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